# HG changeset patch # User Edouard Tisserant # Date 1517324818 -3600 # Node ID 8391c11477f4b46bc4c81828583059361d555841 # Parent d0b1ffcb9368653c162d92b9d8b073b673ade11e# Parent c1298e7ffe3a3079ffa1e3e3fdc4548b2213bc04 Merged Mario's modbus branch. Fixed line endings. diff -r c1298e7ffe3a -r 8391c11477f4 .hgignore --- a/.hgignore Fri Mar 24 12:07:47 2017 +0000 +++ b/.hgignore Tue Jan 30 16:06:58 2018 +0100 @@ -1,7 +1,7 @@ .project -syntax: glob -bug_report* +.directory + syntax: regexp ^tests/.*/build$ ^.idea/.* @@ -15,3 +15,6 @@ bug_report.*\.txt i18n/.*.new$ revision + +doc/_build +doc/locale diff -r c1298e7ffe3a -r 8391c11477f4 .pylint --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.pylint Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,410 @@ +[MASTER] + +# Specify a configuration file. +#rcfile= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Pickle collected data for later comparisons. +persistent=yes + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Allow optimization of some AST trees. This will activate a peephole AST +# optimizer, which will apply various small optimizations. For instance, it can +# be used to obtain the result of joining multiple strings with the addition +# operator. Joining a lot of strings can lead to a maximum recursion error in +# Pylint and this flag can prevent that. It has one side effect, the resulting +# AST will be different than the one from reality. This option is deprecated +# and it will be removed in Pylint 2.0. +optimize-ast=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +# disable=all +# disable=import-star-module-level,old-octal-literal,oct-method,print-statement,unpacking-in-except,parameter-unpacking,backtick,old-raise-syntax,old-ne-operator,long-suffix,dict-view-method,dict-iter-method,metaclass-assignment,next-method-called,raising-string,indexing-exception,raw_input-builtin,long-builtin,file-builtin,execfile-builtin,coerce-builtin,cmp-builtin,buffer-builtin,basestring-builtin,apply-builtin,too-many-lines,filter-builtin-not-iterating,line-too-long,using-cmp-argument,useless-suppression,bad-whitespace,missing-docstring,range-builtin-not-iterating,suppressed-message,invalid-name,no-absolute-import,old-division,cmp-method,reload-builtin,zip-builtin-not-iterating,intern-builtin,unichr-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,input-builtin,round-builtin,fixme,broad-except,hex-method,nonzero-method,map-builtin-not-iterating + + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +# enable=W0611 + + + +[REPORTS] + +# Set the output format. Available formats are text, parseable, colorized, msvs +# (visual studio) and html. You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=parseable + +# Put messages in a separate file for each module / package specified on the +# command line instead of printing them on stdout. Reports (if any) will be +# written in a file name "pylint_global.[txt|html]". This option is deprecated +# and it will be removed in Pylint 2.0. +files-output=no + +# Tells whether to display a full report or only the messages +reports=no + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + + +[VARIABLES] + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins=_,website,JS + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[SIMILARITIES] + +# Minimum lines number of a similarity. +min-similarity-lines=4 + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[BASIC] + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Regular expression matching correct function names +function-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for function names +function-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct variable names +variable-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for variable names +variable-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct attribute names +attr-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for attribute names +attr-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct argument names +argument-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for argument names +argument-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct method names +method-rgx=[a-z_][a-z0-9_]{2,30}$ + +# Naming hint for method names +method-name-hint=[a-z_][a-z0-9_]{2,30}$ + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + + +[ELIF] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[TYPECHECK] + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules=_ctypes,_winreg,winreg,django.core.serializers,__pyjamas__,gluon.contrib.simplejson + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + + +[FORMAT] + +# Maximum number of characters on a single line. +max-line-length=100 + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Maximum number of lines in a module +max-module-lines=1000 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format=LF + + +[IMPORTS] + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=regsub,TERMIOS,Bastion,rexec + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.* + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of statements in function / method body +max-statements=50 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff -r c1298e7ffe3a -r 8391c11477f4 Beremiz.py --- a/Beremiz.py Fri Mar 24 12:07:47 2017 +0000 +++ b/Beremiz.py Tue Jan 30 16:06:58 2018 +0100 @@ -4,7 +4,7 @@ # This file is part of Beremiz, a Integrated Development Environment for # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2016 - 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,1269 +22,179 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -updateinfo_url = None - -import os, sys, getopt -import __builtin__ -import tempfile -import shutil -import random + +from __future__ import absolute_import +from __future__ import print_function +import os +import sys +import getopt import time -import version -from types import ListType - -beremiz_dir = os.path.dirname(os.path.realpath(__file__)) - -if __name__ == '__main__': - import wxversion - wxversion.select(['2.8', '3.0']) - import wx - + +import wx from wx.lib.agw.advancedsplash import AdvancedSplash -def Bpath(*args): - return os.path.join(beremiz_dir,*args) - -def ShowSplashScreen(): - bmp = wx.Image(Bpath("images", "splash.png")).ConvertToBitmap() - #splash=AdvancedSplash(None, bitmap=bmp, style=wx.SPLASH_CENTRE_ON_SCREEN, timeout=4000) - splash = AdvancedSplash(None, bitmap=bmp) - - # process all events - # even the events generated by splash themself during showing - for i in range(0,30): - wx.Yield() - time.sleep(0.01); - return splash - -if __name__ == '__main__': - def usage(): - print "\nUsage of Beremiz.py :" - print "\n %s [Projectpath] [Buildpath]\n"%sys.argv[0] - - try: - opts, args = getopt.getopt(sys.argv[1:], "hu:e:", ["help", "updatecheck=", "extend="]) - except getopt.GetoptError: - # print help information and exit: - usage() - sys.exit(2) - - extensions=[] - - for o, a in opts: +import util.paths as paths + + +class BeremizIDELauncher(object): + def __init__(self): + self.app = None + self.frame = None + self.updateinfo_url = None + self.extensions = [] + self.app_dir = paths.AbsDir(__file__) + self.projectOpen = None + self.buildpath = None + self.splash = None + self.splashPath = self.Bpath("images", "splash.png") + + def Bpath(self, *args): + return os.path.join(self.app_dir, *args) + + def ShowSplashScreen(self): + bmp = wx.Image(self.splashPath).ConvertToBitmap() + self.splash = AdvancedSplash(None, bitmap=bmp) + + # process all events + # even the events generated by splash themself during showing + if wx.Platform == '__WXMSW__': + self.splash.Show() + self.splash.ProcessEvent(wx.PaintEvent()) + else: + for dummy in range(0, 30): + wx.Yield() + time.sleep(0.01) + + def Usage(self): + print("Usage:") + print("%s [Options] [Projectpath] [Buildpath]" % sys.argv[0]) + print("") + print("Supported options:") + print("-h --help Print this help") + print("-u --updatecheck URL Retrieve update information by checking URL") + print("-e --extend PathToExtension Extend IDE functionality by loading at start additional extensions") + print("") + print("") + + def SetCmdOptions(self): + self.shortCmdOpts = "hu:e:" + self.longCmdOpts = ["help", "updatecheck=", "extend="] + + def ProcessOption(self, o, a): if o in ("-h", "--help"): - usage() + self.Usage() sys.exit() if o in ("-u", "--updatecheck"): - updateinfo_url = a + self.updateinfo_url = a if o in ("-e", "--extend"): - extensions.append(a) - - if len(args) > 2: - usage() - sys.exit() - elif len(args) == 1: - projectOpen = args[0] - buildpath = None - elif len(args) == 2: - projectOpen = args[0] - buildpath = args[1] - else: - projectOpen = None - buildpath = None - - if os.path.exists("BEREMIZ_DEBUG"): - __builtin__.__dict__["BMZ_DBG"] = True - else : - __builtin__.__dict__["BMZ_DBG"] = False - - if wx.VERSION >= (3, 0, 0): - app = wx.App(redirect=BMZ_DBG) - else: - app = wx.PySimpleApp(redirect=BMZ_DBG) - - app.SetAppName('beremiz') - if wx.VERSION < (3, 0, 0): - wx.InitAllImageHandlers() - - # popup splash - splash = ShowSplashScreen() - - # load internatialization files - from util.misc import InstallLocalRessources - InstallLocalRessources(beremiz_dir) - - if updateinfo_url is not None: - updateinfo = _("Fetching %s") % updateinfo_url - # warn for possible updates - def updateinfoproc(): - global updateinfo - try : - import urllib2 - updateinfo = urllib2.urlopen(updateinfo_url,None).read() - except : - updateinfo = _("update info unavailable.") - - from threading import Thread - splash.SetText(text=updateinfo) - wx.Yield() - updateinfoThread = Thread(target=updateinfoproc) - updateinfoThread.start() - updateinfoThread.join(2) - splash.SetText(text=updateinfo) - wx.Yield() - - # Load extensions - for extfilename in extensions: - from util.TranslationCatalogs import AddCatalog - from util.BitmapLibrary import AddBitmapFolder - extension_folder = os.path.split(os.path.realpath(extfilename))[0] - sys.path.append(extension_folder) - AddCatalog(os.path.join(extension_folder, "locale")) - AddBitmapFolder(os.path.join(extension_folder, "images")) - execfile(extfilename, locals()) - - -import wx.lib.buttons, wx.lib.statbmp, wx.stc -import cPickle -import types, time, re, platform, time, traceback, commands - -from docutil import OpenHtmlFrame -from editors.EditorPanel import EditorPanel -from editors.Viewer import Viewer -from editors.TextViewer import TextViewer -from editors.ResourceEditor import ConfigurationEditor, ResourceEditor -from editors.DataTypeEditor import DataTypeEditor -from util.MiniTextControler import MiniTextControler -from util.ProcessLogger import ProcessLogger -from controls.LogViewer import LogViewer -from controls.CustomStyledTextCtrl import CustomStyledTextCtrl -from controls import EnhancedStatusBar as esb -from dialogs.AboutDialog import ShowAboutDialog - -from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY, ITEM_PROJECT, ITEM_RESOURCE -from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE - - -MAX_RECENT_PROJECTS = 10 - -if wx.Platform == '__WXMSW__': - faces = { - 'mono' : 'Courier New', - 'size' : 8, - } -else: - faces = { - 'mono' : 'Courier', - 'size' : 10, - } - -from threading import Lock,Timer,currentThread -MainThread = currentThread().ident -REFRESH_PERIOD = 0.1 -from time import time as gettime -class LogPseudoFile: - """ Base class for file like objects to facilitate StdOut for the Shell.""" - def __init__(self, output, risecall): - self.red_white = 1 - self.red_yellow = 2 - self.black_white = wx.stc.STC_STYLE_DEFAULT - self.output = output - self.risecall = risecall - # to prevent rapid fire on rising log panel - self.rising_timer = 0 - self.lock = Lock() - self.YieldLock = Lock() - self.RefreshLock = Lock() - self.TimerAccessLock = Lock() - self.stack = [] - self.LastRefreshTime = gettime() - self.LastRefreshTimer = None - - def write(self, s, style = None): - if self.lock.acquire(): - self.stack.append((s,style)) - self.lock.release() - current_time = gettime() - self.TimerAccessLock.acquire() - if self.LastRefreshTimer: - self.LastRefreshTimer.cancel() - self.LastRefreshTimer=None - self.TimerAccessLock.release() - if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False): - self._should_write() - else: - self.TimerAccessLock.acquire() - self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired) - self.LastRefreshTimer.start() - self.TimerAccessLock.release() - - def _timer_expired(self): - if self.RefreshLock.acquire(False): - self._should_write() + self.extensions.append(a) + + def ProcessCommandLineArgs(self): + self.SetCmdOptions() + try: + opts, args = getopt.getopt(sys.argv[1:], self.shortCmdOpts, self.longCmdOpts) + except getopt.GetoptError: + # print help information and exit: + self.Usage() + sys.exit(2) + + for o, a in opts: + self.ProcessOption(o, a) + + if len(args) > 2: + self.Usage() + sys.exit() + + elif len(args) == 1: + self.projectOpen = args[0] + self.buildpath = None + elif len(args) == 2: + self.projectOpen = args[0] + self.buildpath = args[1] + + def CreateApplication(self): + BMZ_DBG = os.path.exists("BEREMIZ_DEBUG") + + if wx.VERSION >= (3, 0, 0): + self.app = wx.App(redirect=BMZ_DBG) else: - self.TimerAccessLock.acquire() - self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired) - self.LastRefreshTimer.start() - self.TimerAccessLock.release() - - def _should_write(self): - wx.CallAfter(self._write) - if MainThread == currentThread().ident: - app = wx.GetApp() - if app is not None: - if self.YieldLock.acquire(0): - app.Yield() - self.YieldLock.release() - - def _write(self): - if self.output : - self.output.Freeze() - self.lock.acquire() - for s, style in self.stack: - if style is None : style=self.black_white - if style != self.black_white: - self.output.StartStyling(self.output.GetLength(), 0xff) - - # Temporary deactivate read only mode on StyledTextCtrl for - # adding text. It seems that text modifications, even - # programmatically, are disabled in StyledTextCtrl when read - # only is active - start_pos = self.output.GetLength() - self.output.SetReadOnly(False) - self.output.AppendText(s) - self.output.SetReadOnly(True) - text_len = self.output.GetLength() - start_pos - - if style != self.black_white: - self.output.SetStyling(text_len, style) - self.stack = [] - self.lock.release() - self.output.Thaw() - self.LastRefreshTime = gettime() - try: - self.RefreshLock.release() - except: - pass - newtime = time.time() - if newtime - self.rising_timer > 1: - self.risecall(self.output) - self.rising_timer = newtime - - def write_warning(self, s): - self.write(s,self.red_white) - - def write_error(self, s): - self.write(s,self.red_yellow) - - def writeyield(self, s): - self.write(s) - wx.GetApp().Yield() - - def flush(self): - # Temporary deactivate read only mode on StyledTextCtrl for clearing - # text. It seems that text modifications, even programmatically, are - # disabled in StyledTextCtrl when read only is active - self.output.SetReadOnly(False) - self.output.SetText("") - self.output.SetReadOnly(True) - - def isatty(self): - return False - -ID_FILEMENURECENTPROJECTS = wx.NewId() - -from IDEFrame import TITLE,\ - EDITORTOOLBAR,\ - FILEMENU,\ - EDITMENU,\ - DISPLAYMENU,\ - PROJECTTREE,\ - POUINSTANCEVARIABLESPANEL,\ - LIBRARYTREE,\ - SCALING,\ - PAGETITLES,\ - IDEFrame, AppendMenu,\ - EncodeFileSystemPath, DecodeFileSystemPath -from util.BitmapLibrary import GetBitmap - -class Beremiz(IDEFrame): - - def _init_utils(self): - self.ConfNodeMenu = wx.Menu(title='') - self.RecentProjectsMenu = wx.Menu(title='') - - IDEFrame._init_utils(self) - - def _init_coll_FileMenu_Items(self, parent): - AppendMenu(parent, help='', id=wx.ID_NEW, - kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') - AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') - parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu) - parent.AppendSeparator() - AppendMenu(parent, help='', id=wx.ID_SAVE, - kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') - AppendMenu(parent, help='', id=wx.ID_SAVEAS, - kind=wx.ITEM_NORMAL, text=_(u'Save as') + '\tCTRL+SHIFT+S') - AppendMenu(parent, help='', id=wx.ID_CLOSE, - kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') - AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, - kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') - parent.AppendSeparator() - AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, - kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') - AppendMenu(parent, help='', id=wx.ID_PREVIEW, - kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') - AppendMenu(parent, help='', id=wx.ID_PRINT, - kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') - parent.AppendSeparator() - AppendMenu(parent, help='', id=wx.ID_EXIT, - kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') - - self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) - self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) - self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) - self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) - self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) - self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) - self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) - self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) - self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) - self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) - - self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), - (wx.ID_OPEN, "open", _(u'Open'), None), - (wx.ID_SAVE, "save", _(u'Save'), None), - (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), - (wx.ID_PRINT, "print", _(u'Print'), None)]) - - def _RecursiveAddMenuItems(self, menu, items): - for name, text, help, children in items: - new_id = wx.NewId() - if len(children) > 0: - new_menu = wx.Menu(title='') - menu.AppendMenu(new_id, text, new_menu) - self._RecursiveAddMenuItems(new_menu, children) - else: - AppendMenu(menu, help=help, id=new_id, - kind=wx.ITEM_NORMAL, text=text) - self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), - id=new_id) - - def _init_coll_AddMenu_Items(self, parent): - IDEFrame._init_coll_AddMenu_Items(self, parent, False) - self._RecursiveAddMenuItems(parent, GetAddMenuItems()) - - def _init_coll_HelpMenu_Items(self, parent): - parent.Append(help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'About')) - self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) - - def _init_coll_ConnectionStatusBar_Fields(self, parent): - parent.SetFieldsCount(3) - - parent.SetStatusText(number=0, text='') - parent.SetStatusText(number=1, text='') - parent.SetStatusText(number=2, text='') - - parent.SetStatusWidths([-1, 300, 200]) - - def _init_ctrls(self, prnt): - IDEFrame._init_ctrls(self, prnt) - - self.EditMenuSize = self.EditMenu.GetMenuItemCount() - - inspectorID = wx.NewId() - self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) - accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL|wx.ACCEL_ALT, ord('I'), inspectorID)] - - keyID = wx.NewId() - self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID) - accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)] - - for method,shortcut in [("Stop", wx.WXK_F4), - ("Run", wx.WXK_F5), - ("Transfer", wx.WXK_F6), - ("Connect", wx.WXK_F7), - ("Build", wx.WXK_F11)]: - def OnMethodGen(obj,meth): - def OnMethod(evt): - if obj.CTR is not None: - obj.CTR.CallMethod('_'+meth) - wx.CallAfter(self.RefreshStatusToolBar) - return OnMethod - newid = wx.NewId() - self.Bind(wx.EVT_MENU, OnMethodGen(self,method), id=newid) - accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut,newid)] - - self.SetAcceleratorTable(wx.AcceleratorTable(accels)) - - self.LogConsole = CustomStyledTextCtrl( - name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0), - size=wx.Size(0, 0)) - self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged) - self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged) - self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI) - self.LogConsole.SetReadOnly(True) - self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR) - - # Define Log Console styles - self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) - self.LogConsole.StyleClearAll() - self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces) - self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces) - - # Define Log Console markers - self.LogConsole.SetMarginSensitive(1, True) - self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL) - self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED") - - self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT) - - self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick) - self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified) - - self.MainTabs["LogConsole"] = (self.LogConsole, _("Console")) - self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"]) - #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT) - - self.LogViewer = LogViewer(self.BottomNoteBook, self) - self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log")) - self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"]) - #self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT) - - StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) - StatusToolBar.SetToolBitmapSize(wx.Size(25, 25)) - StatusToolBar.Realize() - self.Panes["StatusToolBar"] = StatusToolBar - self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo(). - Name("StatusToolBar").Caption(_("Status ToolBar")). - ToolbarPane().Top().Position(1). - LeftDockable(False).RightDockable(False)) - - self.AUIManager.Update() - - self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP) - self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) - self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range = 100) - self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) - self.ProgressStatusBar.Hide() - self.SetStatusBar(self.ConnectionStatusBar) - - def __init_execute_path(self): - if os.name == 'nt': - # on windows, desktop shortcut launches Beremiz.py - # with working dir set to mingw/bin. - # then we prefix CWD to PATH in order to ensure that - # commands invoked by build process by default are - # found here. - os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] - - - def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): - # Add beremiz's icon in top left corner of the frame - self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO) - self.__init_execute_path() - - IDEFrame.__init__(self, parent, debug) - self.Log = LogPseudoFile(self.LogConsole,self.SelectTab) - - self.local_runtime = None - self.runtime_port = None - self.local_runtime_tmpdir = None - - self.LastPanelSelected = None - - # Define Tree item icon list - self.LocationImageList = wx.ImageList(16, 16) - self.LocationImageDict = {} - - # Icons for location items - for imgname, itemtype in [ - ("CONFIGURATION", LOCATION_CONFNODE), - ("RESOURCE", LOCATION_MODULE), - ("PROGRAM", LOCATION_GROUP), - ("VAR_INPUT", LOCATION_VAR_INPUT), - ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), - ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: - self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname)) - - # Icons for other items - for imgname, itemtype in [ - ("Extension", ITEM_CONFNODE)]: - self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) - - if projectOpen is not None: - projectOpen = DecodeFileSystemPath(projectOpen, False) - - if projectOpen is not None and os.path.isdir(projectOpen): - self.CTR = ProjectController(self, self.Log) - self.Controler = self.CTR - result, err = self.CTR.LoadProject(projectOpen, buildpath) - if not result: - self.LibraryPanel.SetController(self.Controler) - self.ProjectTree.Enable(True) - self.PouInstanceVariablesPanel.SetController(self.Controler) - self.RefreshConfigRecentProjects(os.path.abspath(projectOpen)) - self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - else: - self.ResetView() - self.ShowErrorMessage(result) - else: - self.CTR = ctr - self.Controler = ctr - if ctr is not None: - self.LibraryPanel.SetController(self.Controler) - self.ProjectTree.Enable(True) - self.PouInstanceVariablesPanel.SetController(self.Controler) - self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - if self.EnableDebug: - self.DebugVariablePanel.SetDataProducer(self.CTR) - - self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) - - self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) - self.RefreshAll() - self.LogConsole.SetFocus() - - def RefreshTitle(self): - name = _("Beremiz") - if self.CTR is not None: - projectname = self.CTR.GetProjectName() - if self.CTR.ProjectTestModified(): - projectname = "~%s~" % projectname - self.SetTitle("%s - %s" % (name, projectname)) - else: - self.SetTitle(name) - - def StartLocalRuntime(self, taskbaricon = True): - if (self.local_runtime is None) or (self.local_runtime.exitcode is not None): - # create temporary directory for runtime working directory - self.local_runtime_tmpdir = tempfile.mkdtemp() - # choose an arbitrary random port for runtime - self.runtime_port = int(random.random() * 1000) + 61131 - # launch local runtime - self.local_runtime = ProcessLogger(self.Log, - "\"%s\" \"%s\" -p %s -i localhost %s %s"%( - sys.executable, - Bpath("Beremiz_service.py"), - self.runtime_port, - {False : "-x 0", True :"-x 1"}[taskbaricon], - self.local_runtime_tmpdir), - no_gui=False, - timeout=500, keyword = self.local_runtime_tmpdir, - cwd = self.local_runtime_tmpdir) - self.local_runtime.spin() - return self.runtime_port - - def KillLocalRuntime(self): - if self.local_runtime is not None: - # shutdown local runtime - self.local_runtime.kill(gently=False) - # clear temp dir - shutil.rmtree(self.local_runtime_tmpdir) - - self.local_runtime = None - - def OnOpenWidgetInspector(self, evt): - # Activate the widget inspection tool - from wx.lib.inspection import InspectionTool - if not InspectionTool().initialized: - InspectionTool().Init() - - # Find a widget to be selected in the tree. Use either the - # one under the cursor, if any, or this frame. - wnd = wx.FindWindowAtPointer() - if not wnd: - wnd = self - InspectionTool().Show(wnd, True) - - def OnLogConsoleFocusChanged(self, event): - self.RefreshEditMenu() - event.Skip() - - def OnLogConsoleUpdateUI(self, event): - self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True) - event.Skip() - - def OnLogConsoleMarginClick(self, event): - line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) - wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx)) - event.Skip() - - def OnLogConsoleModified(self, event): - line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) - line = self.LogConsole.GetLine(line_idx) - if line: - result = MATIEC_ERROR_MODEL.match(line) - if result is not None: - self.LogConsole.MarkerAdd(line_idx, 0) - event.Skip() - - def SearchLineForError(self, line): - if self.CTR is not None: - result = MATIEC_ERROR_MODEL.match(line) - if result is not None: - first_line, first_column, last_line, last_column, error = result.groups() - infos = self.CTR.ShowError(self.Log, - (int(first_line), int(first_column)), - (int(last_line), int(last_column))) - - ## Function displaying an Error dialog in PLCOpenEditor. - # @return False if closing cancelled. - def CheckSaveBeforeClosing(self, title=_("Close Project")): - if self.CTR.ProjectTestModified(): - dialog = wx.MessageDialog(self, - _("There are changes, do you want to save?"), - title, - wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) - answer = dialog.ShowModal() - dialog.Destroy() - if answer == wx.ID_YES: - self.CTR.SaveProject() - elif answer == wx.ID_CANCEL: - return False - - for idx in xrange(self.TabsOpened.GetPageCount()): - window = self.TabsOpened.GetPage(idx) - if not window.CheckSaveBeforeClosing(): - return False - - return True - - def GetTabInfos(self, tab): - if (isinstance(tab, EditorPanel) and - not isinstance(tab, (Viewer, - TextViewer, - ResourceEditor, - ConfigurationEditor, - DataTypeEditor))): - return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName()) - elif (isinstance(tab, TextViewer) and - (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))): - return ("confnode", None, tab.GetInstancePath()) - else: - return IDEFrame.GetTabInfos(self, tab) - - def LoadTab(self, notebook, page_infos): - if page_infos[0] == "confnode": - if page_infos[1] is None: - confnode = self.CTR - else: - confnode = self.CTR.GetChildByName(page_infos[1]) - return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:])) - else: - return IDEFrame.LoadTab(self, notebook, page_infos) - - # Strange hack required by WAMP connector, using twisted. - # Twisted reactor needs to be stopped only before quit, - # since it cannot be restarted - ToDoBeforeQuit = [] - def AddToDoBeforeQuit(self, Thing): - self.ToDoBeforeQuit.append(Thing) - - def OnCloseFrame(self, event): - for evt_type in [wx.EVT_SET_FOCUS, - wx.EVT_KILL_FOCUS, - wx.stc.EVT_STC_UPDATEUI]: - self.LogConsole.Unbind(evt_type) - if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")): - if self.CTR is not None: - self.CTR.KillDebugThread() - self.KillLocalRuntime() - - self.SaveLastState() - - for Thing in self.ToDoBeforeQuit : - Thing() - self.ToDoBeforeQuit = [] - - event.Skip() - else: - event.Veto() - - def RefreshFileMenu(self): - self.RefreshRecentProjectsMenu() - - MenuToolBar = self.Panes["MenuToolBar"] - if self.CTR is not None: - selected = self.TabsOpened.GetSelection() - if selected >= 0: - window = self.TabsOpened.GetPage(selected) - viewer_is_modified = window.IsModified() - is_viewer = isinstance(window, Viewer) - else: - viewer_is_modified = is_viewer = False - if self.TabsOpened.GetPageCount() > 0: - self.FileMenu.Enable(wx.ID_CLOSE, True) - if is_viewer: - self.FileMenu.Enable(wx.ID_PREVIEW, True) - self.FileMenu.Enable(wx.ID_PRINT, True) - MenuToolBar.EnableTool(wx.ID_PRINT, True) - else: - self.FileMenu.Enable(wx.ID_PREVIEW, False) - self.FileMenu.Enable(wx.ID_PRINT, False) - MenuToolBar.EnableTool(wx.ID_PRINT, False) - else: - self.FileMenu.Enable(wx.ID_CLOSE, False) - self.FileMenu.Enable(wx.ID_PREVIEW, False) - self.FileMenu.Enable(wx.ID_PRINT, False) - MenuToolBar.EnableTool(wx.ID_PRINT, False) - self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) - project_modified = self.CTR.ProjectTestModified() or viewer_is_modified - self.FileMenu.Enable(wx.ID_SAVE, project_modified) - MenuToolBar.EnableTool(wx.ID_SAVE, project_modified) - self.FileMenu.Enable(wx.ID_SAVEAS, True) - MenuToolBar.EnableTool(wx.ID_SAVEAS, True) - self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) - else: - self.FileMenu.Enable(wx.ID_CLOSE, False) - self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) - self.FileMenu.Enable(wx.ID_PREVIEW, False) - self.FileMenu.Enable(wx.ID_PRINT, False) - MenuToolBar.EnableTool(wx.ID_PRINT, False) - self.FileMenu.Enable(wx.ID_SAVE, False) - MenuToolBar.EnableTool(wx.ID_SAVE, False) - self.FileMenu.Enable(wx.ID_SAVEAS, False) - MenuToolBar.EnableTool(wx.ID_SAVEAS, False) - self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) - - def RefreshRecentProjectsMenu(self): - try: - recent_projects = map(DecodeFileSystemPath, - self.GetConfigEntry("RecentProjects", [])) - except: - recent_projects = [] - - while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects): - item = self.RecentProjectsMenu.FindItemByPosition(0) - self.RecentProjectsMenu.RemoveItem(item) - - self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0) - for idx, projectpath in enumerate(recent_projects): - text = u'%d: %s' % (idx + 1, projectpath) - - if idx < self.RecentProjectsMenu.GetMenuItemCount(): - item = self.RecentProjectsMenu.FindItemByPosition(idx) - id = item.GetId() - item.SetItemLabel(text) - self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType()) - else: - id = wx.NewId() - AppendMenu(self.RecentProjectsMenu, help='', id=id, - kind=wx.ITEM_NORMAL, text=text) - self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id) - - def GenerateOpenRecentProjectFunction(self, projectpath): - def OpenRecentProject(event): - if self.CTR is not None and not self.CheckSaveBeforeClosing(): - return - - self.OpenProject(projectpath) - return OpenRecentProject - - def GenerateMenuRecursive(self, items, menu): - for kind, infos in items: - if isinstance(kind, ListType): - text, id = infos - submenu = wx.Menu('') - self.GenerateMenuRecursive(kind, submenu) - menu.AppendMenu(id, text, submenu) - elif kind == wx.ITEM_SEPARATOR: - menu.AppendSeparator() - else: - text, id, help, callback = infos - AppendMenu(menu, help='', id=id, kind=kind, text=text) - if callback is not None: - self.Bind(wx.EVT_MENU, callback, id=id) - - def RefreshEditorToolBar(self): - IDEFrame.RefreshEditorToolBar(self) - self.AUIManager.GetPane("EditorToolBar").Position(2) - self.AUIManager.GetPane("StatusToolBar").Position(1) - self.AUIManager.Update() - - def RefreshStatusToolBar(self): - StatusToolBar = self.Panes["StatusToolBar"] - StatusToolBar.ClearTools() - - if self.CTR is not None: - - for confnode_method in self.CTR.StatusMethods: - if "method" in confnode_method and confnode_method.get("shown",True): - id = wx.NewId() - StatusToolBar.AddSimpleTool(id, - GetBitmap(confnode_method.get("bitmap", "Unknown")), - confnode_method["tooltip"]) - self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id) - - StatusToolBar.Realize() - self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show() - else: - self.AUIManager.GetPane("StatusToolBar").Hide() - self.AUIManager.GetPane("EditorToolBar").Position(2) - self.AUIManager.GetPane("StatusToolBar").Position(1) - self.AUIManager.Update() - - def RefreshEditMenu(self): - IDEFrame.RefreshEditMenu(self) - if self.FindFocus() == self.LogConsole: - self.EditMenu.Enable(wx.ID_COPY, True) - self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True) - - if self.CTR is not None: - selected = self.TabsOpened.GetSelection() - if selected >= 0: - panel = self.TabsOpened.GetPage(selected) - else: - panel = None - if panel != self.LastPanelSelected: - for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): - item = self.EditMenu.FindItemByPosition(self.EditMenuSize) - if item is not None: - if item.IsSeparator(): - self.EditMenu.RemoveItem(item) - else: - self.EditMenu.Delete(item.GetId()) - self.LastPanelSelected = panel - if panel is not None: - items = panel.GetConfNodeMenuItems() - else: - items = [] - if len(items) > 0: - self.EditMenu.AppendSeparator() - self.GenerateMenuRecursive(items, self.EditMenu) - if panel is not None: - panel.RefreshConfNodeMenu(self.EditMenu) - else: - for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): - item = self.EditMenu.FindItemByPosition(i) - if item is not None: - if item.IsSeparator(): - self.EditMenu.RemoveItem(item) - else: - self.EditMenu.Delete(item.GetId()) - self.LastPanelSelected = None - self.MenuBar.UpdateMenus() - - def RefreshAll(self): - self.RefreshStatusToolBar() - - def GetMenuCallBackFunction(self, method): - """ Generate the callbackfunc for a given CTR method""" - def OnMenu(event): - # Disable button to prevent re-entrant call - event.GetEventObject().Disable() - # Call - getattr(self.CTR, method)() - # Re-enable button - event.GetEventObject().Enable() - return OnMenu - - def GetConfigEntry(self, entry_name, default): - return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default)))) - - def ResetConnectionStatusBar(self): - for field in xrange(self.ConnectionStatusBar.GetFieldsCount()): - self.ConnectionStatusBar.SetStatusText('', field) - - def ResetView(self): - IDEFrame.ResetView(self) - self.ConfNodeInfos = {} - if self.CTR is not None: - self.CTR.CloseProject() - self.CTR = None - self.Log.flush() - if self.EnableDebug: - self.DebugVariablePanel.SetDataProducer(None) - self.ResetConnectionStatusBar() - - def RefreshConfigRecentProjects(self, projectpath, err=False): - try: - recent_projects = map(DecodeFileSystemPath, - self.GetConfigEntry("RecentProjects", [])) - except: - recent_projects = [] - if projectpath in recent_projects: - recent_projects.remove(projectpath) - if not err: - recent_projects.insert(0, projectpath) - self.Config.Write("RecentProjects", cPickle.dumps( - map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))) - self.Config.Flush() - - def ResetPerspective(self): - IDEFrame.ResetPerspective(self) - self.RefreshStatusToolBar() - - def OnNewProjectMenu(self, event): - if self.CTR is not None and not self.CheckSaveBeforeClosing(): - return - - try: - defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) - except: - defaultpath = os.path.expanduser("~") - - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath) - if dialog.ShowModal() == wx.ID_OK: - projectpath = dialog.GetPath() - self.Config.Write("lastopenedfolder", - EncodeFileSystemPath(os.path.dirname(projectpath))) - self.Config.Flush() - self.ResetView() - ctr = ProjectController(self, self.Log) - result = ctr.NewProject(projectpath) - if not result: - self.CTR = ctr - self.Controler = self.CTR - self.LibraryPanel.SetController(self.Controler) - self.ProjectTree.Enable(True) - self.PouInstanceVariablesPanel.SetController(self.Controler) - self.RefreshConfigRecentProjects(projectpath) - if self.EnableDebug: - self.DebugVariablePanel.SetDataProducer(self.CTR) - self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - else: - self.ResetView() - self.ShowErrorMessage(result) - self.RefreshAll() - self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) - dialog.Destroy() - - def OnOpenProjectMenu(self, event): - if self.CTR is not None and not self.CheckSaveBeforeClosing(): - return - - try: - defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) - except: - defaultpath = os.path.expanduser("~") - - dialog = wx.DirDialog(self , _("Choose a project"), defaultpath, style=wx.DEFAULT_DIALOG_STYLE| - wx.RESIZE_BORDER) - if dialog.ShowModal() == wx.ID_OK: - self.OpenProject(dialog.GetPath()) - dialog.Destroy() - - def OpenProject(self, projectpath): - if os.path.isdir(projectpath): - self.Config.Write("lastopenedfolder", - EncodeFileSystemPath(os.path.dirname(projectpath))) - self.Config.Flush() - self.ResetView() - self.CTR = ProjectController(self, self.Log) - self.Controler = self.CTR - result, err = self.CTR.LoadProject(projectpath) - if not result: - self.LibraryPanel.SetController(self.Controler) - self.ProjectTree.Enable(True) - self.PouInstanceVariablesPanel.SetController(self.Controler) - if self.EnableDebug: - self.DebugVariablePanel.SetDataProducer(self.CTR) - self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - else: - self.ResetView() - self.ShowErrorMessage(result) - self.RefreshAll() - self.SearchResultPanel.ResetSearchResults() - else: - self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) - err = True - self.RefreshConfigRecentProjects(projectpath, err) - self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) - - def OnCloseProjectMenu(self, event): - if self.CTR is not None and not self.CheckSaveBeforeClosing(): - return - - self.ResetView() - self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) - self.RefreshAll() - - def OnSaveProjectMenu(self, event): - selected = self.TabsOpened.GetSelection() - if selected != -1: - window = self.TabsOpened.GetPage(selected) - window.Save() - if self.CTR is not None: - self.CTR.SaveProject() - self.RefreshAll() - self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) - - def OnSaveProjectAsMenu(self, event): - selected = self.TabsOpened.GetSelection() - if selected != -1: - window = self.TabsOpened.GetPage(selected) - window.SaveAs() - if self.CTR is not None: - self.CTR.SaveProjectAs() - self.RefreshAll() - self.RefreshConfigRecentProjects(self.CTR.ProjectPath) - self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) - - def OnQuitMenu(self, event): - self.Close() - - def OnAboutMenu(self, event): - info = version.GetAboutDialogInfo() - ShowAboutDialog(self, info) - - def OnProjectTreeItemBeginEdit(self, event): - selected = event.GetItem() - if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE: - event.Veto() - else: - IDEFrame.OnProjectTreeItemBeginEdit(self, event) - - def OnProjectTreeRightUp(self, event): - item = event.GetItem() - item_infos = self.ProjectTree.GetPyData(item) - - if item_infos["type"] == ITEM_CONFNODE: - confnode_menu = wx.Menu(title='') - - confnode = item_infos["confnode"] - if confnode is not None: - menu_items = confnode.GetContextualMenuItems() - if menu_items is not None: - for text, help, callback in menu_items: - new_id = wx.NewId() - confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text) - self.Bind(wx.EVT_MENU, callback, id=new_id) - else: - for name, XSDClass, help in confnode.CTNChildrenTypes: - new_id = wx.NewId() - confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Add") + " " + name) - self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), id=new_id) - - new_id = wx.NewId() - AppendMenu(confnode_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete")) - self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id) - - self.PopupMenu(confnode_menu) - confnode_menu.Destroy() - - event.Skip() - elif item_infos["type"] == ITEM_RESOURCE: - # prevent last resource to be delted - parent = self.ProjectTree.GetItemParent(item) - parent_name = self.ProjectTree.GetItemText(parent) - if parent_name == _("Resources"): - IDEFrame.OnProjectTreeRightUp(self, event) - else: - IDEFrame.OnProjectTreeRightUp(self, event) - - def OnProjectTreeItemActivated(self, event): - selected = event.GetItem() - name = self.ProjectTree.GetItemText(selected) - item_infos = self.ProjectTree.GetPyData(selected) - if item_infos["type"] == ITEM_CONFNODE: - item_infos["confnode"]._OpenView() - event.Skip() - elif item_infos["type"] == ITEM_PROJECT: - self.CTR._OpenView() - else: - IDEFrame.OnProjectTreeItemActivated(self, event) - - def ProjectTreeItemSelect(self, select_item): - if select_item is not None and select_item.IsOk(): - name = self.ProjectTree.GetItemText(select_item) - item_infos = self.ProjectTree.GetPyData(select_item) - if item_infos["type"] == ITEM_CONFNODE: - item_infos["confnode"]._OpenView(onlyopened=True) - elif item_infos["type"] == ITEM_PROJECT: - self.CTR._OpenView(onlyopened=True) - else: - IDEFrame.ProjectTreeItemSelect(self, select_item) - - def SelectProjectTreeItem(self, tagname): - if self.ProjectTree is not None: - root = self.ProjectTree.GetRootItem() - if root.IsOk(): - words = tagname.split("::") - if len(words) == 1: - if tagname == "Project": - self.SelectedItem = root - self.ProjectTree.SelectItem(root) - self.ResetSelectedItem() - else: - return self.RecursiveProjectTreeItemSelection(root, - [(word, ITEM_CONFNODE) for word in tagname.split(".")]) - elif words[0] == "R": - return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)]) - elif not os.path.exists(words[0]): - IDEFrame.SelectProjectTreeItem(self, tagname) - - def GetAddConfNodeFunction(self, name, confnode=None): - def AddConfNodeMenuFunction(event): - wx.CallAfter(self.AddConfNode, name, confnode) - return AddConfNodeMenuFunction - - def GetDeleteMenuFunction(self, confnode): - def DeleteMenuFunction(event): - wx.CallAfter(self.DeleteConfNode, confnode) - return DeleteMenuFunction - - def AddConfNode(self, ConfNodeType, confnode=None): - if self.CTR.CheckProjectPathPerm(): - ConfNodeName = "%s_0" % ConfNodeType - if confnode is not None: - confnode.CTNAddChild(ConfNodeName, ConfNodeType) - else: - self.CTR.CTNAddChild(ConfNodeName, ConfNodeType) - self._Refresh(TITLE, FILEMENU, PROJECTTREE) - - def DeleteConfNode(self, confnode): - if self.CTR.CheckProjectPathPerm(): - dialog = wx.MessageDialog(self, - _("Really delete node '%s'?") % confnode.CTNName(), - _("Remove %s node") % confnode.CTNType, - wx.YES_NO|wx.NO_DEFAULT) - if dialog.ShowModal() == wx.ID_YES: - confnode.CTNRemove() - del confnode - self._Refresh(TITLE, FILEMENU, PROJECTTREE) - dialog.Destroy() - -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- - - def ShowHighlight(self, infos, start, end, highlight_type): - config_name = self.Controler.GetProjectMainConfigurationName() - if config_name is not None and infos[0] == self.Controler.ComputeConfigurationName(config_name): - self.CTR._OpenView() - selected = self.TabsOpened.GetSelection() - if selected != -1: - viewer = self.TabsOpened.GetPage(selected) - viewer.AddHighlight(infos[1:], start, end, highlight_type) - else: - IDEFrame.ShowHighlight(self, infos, start, end, highlight_type) - -#------------------------------------------------------------------------------- -# Exception Handler -#------------------------------------------------------------------------------- -import threading, traceback - -Max_Traceback_List_Size = 20 - -def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path): - trcbck_lst = [] - for i,line in enumerate(traceback.extract_tb(e_tb)): - trcbck = " " + str(i+1) + ". " - if line[0].find(os.getcwd()) == -1: - trcbck += "file : " + str(line[0]) + ", " - else: - trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ", " - trcbck += "line : " + str(line[1]) + ", " + "function : " + str(line[2]) - trcbck_lst.append(trcbck) - - # Allow clicking.... - cap = wx.Window_GetCapture() - if cap: - cap.ReleaseMouse() - - dlg = wx.SingleChoiceDialog(None, - _(""" -An unhandled exception (bug) occured. Bug report saved at : -(%s) - -Please be kind enough to send this file to: -beremiz-devel@lists.sourceforge.net - -You should now restart program. - -Traceback: -""") % bug_report_path + - repr(e_type) + " : " + repr(e_value), - _("Error"), - trcbck_lst) - try: - res = (dlg.ShowModal() == wx.ID_OK) - finally: - dlg.Destroy() - - return res - -def get_last_traceback(tb): - while tb.tb_next: - tb = tb.tb_next - return tb - - -def format_namespace(d, indent=' '): - return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) - - -ignored_exceptions = [] # a problem with a line in a module is only reported once per session - -def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): - - def handle_exception(e_type, e_value, e_traceback): - traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func - last_tb = get_last_traceback(e_traceback) - ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) - if ex not in ignored_exceptions: - date = time.ctime() - bug_report_path = path+os.sep+"bug_report_"+date.replace(':','-').replace(' ','_')+".txt" - result = Display_Exception_Dialog(e_type,e_value,e_traceback,bug_report_path) - if result: - ignored_exceptions.append(ex) - info = { - 'app-title' : wx.GetApp().GetAppName(), # app_title - 'app-version' : app_version, - 'wx-version' : wx.VERSION_STRING, - 'wx-platform' : wx.Platform, - 'python-version' : platform.python_version(), #sys.version.split()[0], - 'platform' : platform.platform(), - 'e-type' : e_type, - 'e-value' : e_value, - 'date' : date, - 'cwd' : os.getcwd(), - } - if e_traceback: - info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) - last_tb = get_last_traceback(e_traceback) - exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred - info['locals'] = format_namespace(exception_locals) - if 'self' in exception_locals: - try : - info['self'] = format_namespace(exception_locals['self'].__dict__) - except : - pass - - output = open(bug_report_path,'w') - lst = info.keys() - lst.sort() - for a in lst: - output.write(a+":\n"+str(info[a])+"\n\n") - - #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) - sys.excepthook = handle_exception - - init_old = threading.Thread.__init__ - def init(self, *args, **kwargs): - init_old(self, *args, **kwargs) - run_old = self.run - def run_with_except_hook(*args, **kw): - try: - run_old(*args, **kw) - except (KeyboardInterrupt, SystemExit): - raise - except: - sys.excepthook(*sys.exc_info()) - self.run = run_with_except_hook - threading.Thread.__init__ = init + self.app = wx.PySimpleApp(redirect=BMZ_DBG) + + self.app.SetAppName('beremiz') + if wx.VERSION < (3, 0, 0): + wx.InitAllImageHandlers() + + self.ShowSplashScreen() + self.BackgroundInitialization() + + def BackgroundInitialization(self): + self.InitI18n() + self.CheckUpdates() + self.LoadExtensions() + self.ImportModules() + self.InstallExceptionHandler() + self.ShowUI() + + def InitI18n(self): + from util.misc import InstallLocalRessources + InstallLocalRessources(self.app_dir) + + def LoadExtensions(self): + for extfilename in self.extensions: + from util.TranslationCatalogs import AddCatalog + from util.BitmapLibrary import AddBitmapFolder + extension_folder = os.path.split(os.path.realpath(extfilename))[0] + sys.path.append(extension_folder) + AddCatalog(os.path.join(extension_folder, "locale")) + AddBitmapFolder(os.path.join(extension_folder, "images")) + execfile(extfilename, locals()) + + def CheckUpdates(self): + if self.updateinfo_url is not None: + self.updateinfo = _("Fetching %s") % self.updateinfo_url + + def updateinfoproc(): + try: + import urllib2 + self.updateinfo = urllib2.urlopen(self.updateinfo_url, None).read() + except Exception: + self.updateinfo = _("update info unavailable.") + + from threading import Thread + self.splash.SetText(text=self.updateinfo) + updateinfoThread = Thread(target=updateinfoproc) + updateinfoThread.start() + updateinfoThread.join(2) + self.splash.SetText(text=self.updateinfo) + + def ImportModules(self): + global BeremizIDE + import BeremizIDE + + def InstallExceptionHandler(self): + import version + import util.ExceptionHandler + util.ExceptionHandler.AddExceptHook(version.app_version) + + def ShowUI(self): + import BeremizIDE + self.frame = BeremizIDE.Beremiz(None, self.projectOpen, self.buildpath) + if self.splash: + self.splash.Close() + self.frame.Show() + + def PreStart(self): + self.ProcessCommandLineArgs() + self.CreateApplication() + + def MainLoop(self): + self.app.MainLoop() + + def Start(self): + self.PreStart() + self.MainLoop() + if __name__ == '__main__': - # Install a exception handle for bug reports - AddExceptHook(os.getcwd(),version.app_version) - - frame = Beremiz(None, projectOpen, buildpath) - if splash: - splash.Close() - frame.Show() - app.MainLoop() + beremiz = BeremizIDELauncher() + beremiz.Start() diff -r c1298e7ffe3a -r 8391c11477f4 BeremizIDE.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BeremizIDE.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,1098 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2016: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import os +import sys +import tempfile +import shutil +import random +import types +import time +from time import time as gettime +from threading import Lock, Timer, currentThread + +import cPickle +import wx.lib.buttons +import wx.lib.statbmp +import wx.stc + + +import version +from editors.EditorPanel import EditorPanel +from editors.Viewer import Viewer +from editors.TextViewer import TextViewer +from editors.ResourceEditor import ConfigurationEditor, ResourceEditor +from editors.DataTypeEditor import DataTypeEditor +from util import paths as paths +from util.MiniTextControler import MiniTextControler +from util.ProcessLogger import ProcessLogger +from util.BitmapLibrary import GetBitmap +from controls.LogViewer import LogViewer +from controls.CustomStyledTextCtrl import CustomStyledTextCtrl +from controls import EnhancedStatusBar as esb +from dialogs.AboutDialog import ShowAboutDialog + +from PLCControler import \ + LOCATION_CONFNODE, \ + LOCATION_MODULE, \ + LOCATION_GROUP, \ + LOCATION_VAR_INPUT, \ + LOCATION_VAR_OUTPUT, \ + LOCATION_VAR_MEMORY, \ + ITEM_PROJECT, \ + ITEM_RESOURCE + +from ProjectController import ProjectController, GetAddMenuItems, MATIEC_ERROR_MODEL, ITEM_CONFNODE + +from IDEFrame import \ + TITLE,\ + EDITORTOOLBAR,\ + FILEMENU,\ + EDITMENU,\ + DISPLAYMENU,\ + PROJECTTREE,\ + POUINSTANCEVARIABLESPANEL,\ + LIBRARYTREE,\ + PAGETITLES,\ + IDEFrame, \ + AppendMenu,\ + EncodeFileSystemPath, \ + DecodeFileSystemPath + + +beremiz_dir = paths.AbsDir(__file__) + + +def Bpath(*args): + return os.path.join(beremiz_dir, *args) + + +MAX_RECENT_PROJECTS = 9 + + +if wx.Platform == '__WXMSW__': + faces = { + 'mono': 'Courier New', + 'size': 8, + } +else: + faces = { + 'mono': 'Courier', + 'size': 10, + } + + +MainThread = currentThread().ident +REFRESH_PERIOD = 0.1 + + +class LogPseudoFile(object): + """ Base class for file like objects to facilitate StdOut for the Shell.""" + def __init__(self, output, risecall): + self.red_white = 1 + self.red_yellow = 2 + self.black_white = wx.stc.STC_STYLE_DEFAULT + self.output = output + self.risecall = risecall + # to prevent rapid fire on rising log panel + self.rising_timer = 0 + self.lock = Lock() + self.YieldLock = Lock() + self.RefreshLock = Lock() + self.TimerAccessLock = Lock() + self.stack = [] + self.LastRefreshTime = gettime() + self.LastRefreshTimer = None + + def write(self, s, style=None): + if self.lock.acquire(): + self.stack.append((s, style)) + self.lock.release() + current_time = gettime() + self.TimerAccessLock.acquire() + if self.LastRefreshTimer: + self.LastRefreshTimer.cancel() + self.LastRefreshTimer = None + self.TimerAccessLock.release() + if current_time - self.LastRefreshTime > REFRESH_PERIOD and self.RefreshLock.acquire(False): + self._should_write() + else: + self.TimerAccessLock.acquire() + self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired) + self.LastRefreshTimer.start() + self.TimerAccessLock.release() + + def _timer_expired(self): + if self.RefreshLock.acquire(False): + self._should_write() + else: + self.TimerAccessLock.acquire() + self.LastRefreshTimer = Timer(REFRESH_PERIOD, self._timer_expired) + self.LastRefreshTimer.start() + self.TimerAccessLock.release() + + def _should_write(self): + app = wx.GetApp() + if app is not None: + wx.CallAfter(self._write) + + if MainThread == currentThread().ident: + if app is not None: + if self.YieldLock.acquire(0): + app.Yield() + self.YieldLock.release() + + def _write(self): + if self.output: + self.output.Freeze() + self.lock.acquire() + for s, style in self.stack: + if style is None: + style = self.black_white + if style != self.black_white: + self.output.StartStyling(self.output.GetLength(), 0xff) + + # Temporary deactivate read only mode on StyledTextCtrl for + # adding text. It seems that text modifications, even + # programmatically, are disabled in StyledTextCtrl when read + # only is active + start_pos = self.output.GetLength() + self.output.SetReadOnly(False) + self.output.AppendText(s) + self.output.SetReadOnly(True) + text_len = self.output.GetLength() - start_pos + + if style != self.black_white: + self.output.SetStyling(text_len, style) + self.stack = [] + self.lock.release() + self.output.Thaw() + self.LastRefreshTime = gettime() + try: + self.RefreshLock.release() + except Exception: + pass + newtime = time.time() + if newtime - self.rising_timer > 1: + self.risecall(self.output) + self.rising_timer = newtime + + def write_warning(self, s): + self.write(s, self.red_white) + + def write_error(self, s): + self.write(s, self.red_yellow) + + def writeyield(self, s): + self.write(s) + wx.GetApp().Yield() + + def flush(self): + # Temporary deactivate read only mode on StyledTextCtrl for clearing + # text. It seems that text modifications, even programmatically, are + # disabled in StyledTextCtrl when read only is active + self.output.SetReadOnly(False) + self.output.SetText("") + self.output.SetReadOnly(True) + + def isatty(self): + return False + + +ID_FILEMENURECENTPROJECTS = wx.NewId() + + +class Beremiz(IDEFrame): + + def _init_utils(self): + self.ConfNodeMenu = wx.Menu(title='') + self.RecentProjectsMenu = wx.Menu(title='') + + IDEFrame._init_utils(self) + + def _init_coll_FileMenu_Items(self, parent): + AppendMenu(parent, help='', id=wx.ID_NEW, + kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') + AppendMenu(parent, help='', id=wx.ID_OPEN, + kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') + parent.AppendMenu(ID_FILEMENURECENTPROJECTS, _("&Recent Projects"), self.RecentProjectsMenu) + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_SAVE, + kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') + AppendMenu(parent, help='', id=wx.ID_SAVEAS, + kind=wx.ITEM_NORMAL, text=_(u'Save as') + '\tCTRL+SHIFT+S') + AppendMenu(parent, help='', id=wx.ID_CLOSE, + kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') + AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, + kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, + kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') + AppendMenu(parent, help='', id=wx.ID_PREVIEW, + kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') + AppendMenu(parent, help='', id=wx.ID_PRINT, + kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') + parent.AppendSeparator() + AppendMenu(parent, help='', id=wx.ID_EXIT, + kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') + + self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) + self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) + self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) + self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) + self.Bind(wx.EVT_MENU, self.OnCloseTabMenu, id=wx.ID_CLOSE) + self.Bind(wx.EVT_MENU, self.OnCloseProjectMenu, id=wx.ID_CLOSE_ALL) + self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) + self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) + self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) + self.Bind(wx.EVT_MENU, self.OnQuitMenu, id=wx.ID_EXIT) + + self.AddToMenuToolBar([(wx.ID_NEW, "new", _(u'New'), None), + (wx.ID_OPEN, "open", _(u'Open'), None), + (wx.ID_SAVE, "save", _(u'Save'), None), + (wx.ID_SAVEAS, "saveas", _(u'Save As...'), None), + (wx.ID_PRINT, "print", _(u'Print'), None)]) + + def _RecursiveAddMenuItems(self, menu, items): + for name, text, help, children in items: + new_id = wx.NewId() + if len(children) > 0: + new_menu = wx.Menu(title='') + menu.AppendMenu(new_id, text, new_menu) + self._RecursiveAddMenuItems(new_menu, children) + else: + AppendMenu(menu, help=help, id=new_id, + kind=wx.ITEM_NORMAL, text=text) + self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name), + id=new_id) + + def _init_coll_AddMenu_Items(self, parent): + IDEFrame._init_coll_AddMenu_Items(self, parent, False) + self._RecursiveAddMenuItems(parent, GetAddMenuItems()) + + def _init_coll_HelpMenu_Items(self, parent): + def handler(event): + return wx.MessageBox( + version.GetCommunityHelpMsg(), + _(u'Community support'), + wx.OK | wx.ICON_INFORMATION) + + id = wx.NewId() + parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) + self.Bind(wx.EVT_MENU, handler, id=id) + + parent.Append(help='', id=wx.ID_ABOUT, + kind=wx.ITEM_NORMAL, text=_(u'About')) + self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) + + def _init_coll_ConnectionStatusBar_Fields(self, parent): + parent.SetFieldsCount(3) + + parent.SetStatusText(number=0, text='') + parent.SetStatusText(number=1, text='') + parent.SetStatusText(number=2, text='') + + parent.SetStatusWidths([-1, 300, 200]) + + def _init_ctrls(self, prnt): + IDEFrame._init_ctrls(self, prnt) + + self.EditMenuSize = self.EditMenu.GetMenuItemCount() + + inspectorID = wx.NewId() + self.Bind(wx.EVT_MENU, self.OnOpenWidgetInspector, id=inspectorID) + accels = [wx.AcceleratorEntry(wx.ACCEL_CTRL | wx.ACCEL_ALT, ord('I'), inspectorID)] + + keyID = wx.NewId() + self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=keyID) + accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, wx.WXK_F12, keyID)] + + for method, shortcut in [("Stop", wx.WXK_F4), + ("Run", wx.WXK_F5), + ("Transfer", wx.WXK_F6), + ("Connect", wx.WXK_F7), + ("Build", wx.WXK_F11)]: + def OnMethodGen(obj, meth): + def OnMethod(evt): + if obj.CTR is not None: + obj.CTR.CallMethod('_'+meth) + wx.CallAfter(self.RefreshStatusToolBar) + return OnMethod + newid = wx.NewId() + self.Bind(wx.EVT_MENU, OnMethodGen(self, method), id=newid) + accels += [wx.AcceleratorEntry(wx.ACCEL_NORMAL, shortcut, newid)] + + self.SetAcceleratorTable(wx.AcceleratorTable(accels)) + + self.LogConsole = CustomStyledTextCtrl( + name='LogConsole', parent=self.BottomNoteBook, pos=wx.Point(0, 0), + size=wx.Size(0, 0)) + self.LogConsole.Bind(wx.EVT_SET_FOCUS, self.OnLogConsoleFocusChanged) + self.LogConsole.Bind(wx.EVT_KILL_FOCUS, self.OnLogConsoleFocusChanged) + self.LogConsole.Bind(wx.stc.EVT_STC_UPDATEUI, self.OnLogConsoleUpdateUI) + self.LogConsole.SetReadOnly(True) + self.LogConsole.SetWrapMode(wx.stc.STC_WRAP_CHAR) + + # Define Log Console styles + self.LogConsole.StyleSetSpec(wx.stc.STC_STYLE_DEFAULT, "face:%(mono)s,size:%(size)d" % faces) + self.LogConsole.StyleClearAll() + self.LogConsole.StyleSetSpec(1, "face:%(mono)s,fore:#FF0000,size:%(size)d" % faces) + self.LogConsole.StyleSetSpec(2, "face:%(mono)s,fore:#FF0000,back:#FFFF00,size:%(size)d" % faces) + + # Define Log Console markers + self.LogConsole.SetMarginSensitive(1, True) + self.LogConsole.SetMarginType(1, wx.stc.STC_MARGIN_SYMBOL) + self.LogConsole.MarkerDefine(0, wx.stc.STC_MARK_CIRCLE, "BLACK", "RED") + + self.LogConsole.SetModEventMask(wx.stc.STC_MOD_INSERTTEXT) + + self.LogConsole.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnLogConsoleMarginClick) + self.LogConsole.Bind(wx.stc.EVT_STC_MODIFIED, self.OnLogConsoleModified) + + self.MainTabs["LogConsole"] = (self.LogConsole, _("Console")) + self.BottomNoteBook.AddPage(*self.MainTabs["LogConsole"]) + # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogConsole), wx.RIGHT) + + self.LogViewer = LogViewer(self.BottomNoteBook, self) + self.MainTabs["LogViewer"] = (self.LogViewer, _("PLC Log")) + self.BottomNoteBook.AddPage(*self.MainTabs["LogViewer"]) + # self.BottomNoteBook.Split(self.BottomNoteBook.GetPageIndex(self.LogViewer), wx.RIGHT) + + StatusToolBar = wx.ToolBar(self, -1, wx.DefaultPosition, wx.DefaultSize, + wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + StatusToolBar.SetToolBitmapSize(wx.Size(25, 25)) + StatusToolBar.Realize() + self.Panes["StatusToolBar"] = StatusToolBar + self.AUIManager.AddPane(StatusToolBar, wx.aui.AuiPaneInfo(). + Name("StatusToolBar").Caption(_("Status ToolBar")). + ToolbarPane().Top().Position(1). + LeftDockable(False).RightDockable(False)) + + self.AUIManager.Update() + + self.ConnectionStatusBar = esb.EnhancedStatusBar(self, style=wx.ST_SIZEGRIP) + self._init_coll_ConnectionStatusBar_Fields(self.ConnectionStatusBar) + self.ProgressStatusBar = wx.Gauge(self.ConnectionStatusBar, -1, range=100) + self.ConnectionStatusBar.AddWidget(self.ProgressStatusBar, esb.ESB_EXACT_FIT, esb.ESB_EXACT_FIT, 2) + self.ProgressStatusBar.Hide() + self.SetStatusBar(self.ConnectionStatusBar) + + def __init_execute_path(self): + if os.name == 'nt': + # on windows, desktop shortcut launches Beremiz.py + # with working dir set to mingw/bin. + # then we prefix CWD to PATH in order to ensure that + # commands invoked by build process by default are + # found here. + os.environ["PATH"] = os.getcwd()+';'+os.environ["PATH"] + + def __init__(self, parent, projectOpen=None, buildpath=None, ctr=None, debug=True): + # Add beremiz's icon in top left corner of the frame + self.icon = wx.Icon(Bpath("images", "brz.ico"), wx.BITMAP_TYPE_ICO) + self.__init_execute_path() + + IDEFrame.__init__(self, parent, debug) + self.Log = LogPseudoFile(self.LogConsole, self.SelectTab) + + self.local_runtime = None + self.runtime_port = None + self.local_runtime_tmpdir = None + + self.LastPanelSelected = None + + # Define Tree item icon list + self.LocationImageList = wx.ImageList(16, 16) + self.LocationImageDict = {} + + # Icons for location items + for imgname, itemtype in [ + ("CONFIGURATION", LOCATION_CONFNODE), + ("RESOURCE", LOCATION_MODULE), + ("PROGRAM", LOCATION_GROUP), + ("VAR_INPUT", LOCATION_VAR_INPUT), + ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), + ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: + self.LocationImageDict[itemtype] = self.LocationImageList.Add(GetBitmap(imgname)) + + # Icons for other items + for imgname, itemtype in [ + ("Extension", ITEM_CONFNODE)]: + self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) + + if projectOpen is not None: + projectOpen = DecodeFileSystemPath(projectOpen, False) + + if projectOpen is not None and os.path.isdir(projectOpen): + self.CTR = ProjectController(self, self.Log) + self.Controler = self.CTR + result, _err = self.CTR.LoadProject(projectOpen, buildpath) + if not result: + self.LibraryPanel.SetController(self.Controler) + self.ProjectTree.Enable(True) + self.PouInstanceVariablesPanel.SetController(self.Controler) + self.RefreshConfigRecentProjects(os.path.abspath(projectOpen)) + self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + else: + self.ResetView() + self.ShowErrorMessage(result) + else: + self.CTR = ctr + self.Controler = ctr + if ctr is not None: + self.LibraryPanel.SetController(self.Controler) + self.ProjectTree.Enable(True) + self.PouInstanceVariablesPanel.SetController(self.Controler) + self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + if self.EnableDebug: + self.DebugVariablePanel.SetDataProducer(self.CTR) + + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) + + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU) + self.RefreshAll() + self.LogConsole.SetFocus() + + def RefreshTitle(self): + name = _("Beremiz") + if self.CTR is not None: + projectname = self.CTR.GetProjectName() + if self.CTR.ProjectTestModified(): + projectname = "~%s~" % projectname + self.SetTitle("%s - %s" % (name, projectname)) + else: + self.SetTitle(name) + + def StartLocalRuntime(self, taskbaricon=True): + if (self.local_runtime is None) or (self.local_runtime.exitcode is not None): + # create temporary directory for runtime working directory + self.local_runtime_tmpdir = tempfile.mkdtemp() + # choose an arbitrary random port for runtime + self.runtime_port = int(random.random() * 1000) + 61131 + # launch local runtime + self.local_runtime = ProcessLogger( + self.Log, + "\"%s\" \"%s\" -p %s -i localhost %s %s" % ( + sys.executable, + Bpath("Beremiz_service.py"), + self.runtime_port, + {False: "-x 0", True: "-x 1"}[taskbaricon], + self.local_runtime_tmpdir), + no_gui=False, + timeout=500, keyword=self.local_runtime_tmpdir, + cwd=self.local_runtime_tmpdir) + self.local_runtime.spin() + return self.runtime_port + + def KillLocalRuntime(self): + if self.local_runtime is not None: + # shutdown local runtime + self.local_runtime.kill(gently=False) + # clear temp dir + shutil.rmtree(self.local_runtime_tmpdir) + + self.local_runtime = None + + def OnOpenWidgetInspector(self, evt): + # Activate the widget inspection tool + from wx.lib.inspection import InspectionTool + if not InspectionTool().initialized: + InspectionTool().Init() + + # Find a widget to be selected in the tree. Use either the + # one under the cursor, if any, or this frame. + wnd = wx.FindWindowAtPointer() + if not wnd: + wnd = self + InspectionTool().Show(wnd, True) + + def OnLogConsoleFocusChanged(self, event): + self.RefreshEditMenu() + event.Skip() + + def OnLogConsoleUpdateUI(self, event): + self.SetCopyBuffer(self.LogConsole.GetSelectedText(), True) + event.Skip() + + def OnLogConsoleMarginClick(self, event): + line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) + wx.CallAfter(self.SearchLineForError, self.LogConsole.GetLine(line_idx)) + event.Skip() + + def OnLogConsoleModified(self, event): + line_idx = self.LogConsole.LineFromPosition(event.GetPosition()) + line = self.LogConsole.GetLine(line_idx) + if line: + result = MATIEC_ERROR_MODEL.match(line) + if result is not None: + self.LogConsole.MarkerAdd(line_idx, 0) + event.Skip() + + def SearchLineForError(self, line): + if self.CTR is not None: + result = MATIEC_ERROR_MODEL.match(line) + if result is not None: + first_line, first_column, last_line, last_column, _error = result.groups() + self.CTR.ShowError(self.Log, + (int(first_line), int(first_column)), + (int(last_line), int(last_column))) + + def CheckSaveBeforeClosing(self, title=_("Close Project")): + """Function displaying an Error dialog in PLCOpenEditor. + + :returns: False if closing cancelled. + """ + if self.CTR.ProjectTestModified(): + dialog = wx.MessageDialog(self, + _("There are changes, do you want to save?"), + title, + wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION) + answer = dialog.ShowModal() + dialog.Destroy() + if answer == wx.ID_YES: + self.CTR.SaveProject() + elif answer == wx.ID_CANCEL: + return False + + for idx in xrange(self.TabsOpened.GetPageCount()): + window = self.TabsOpened.GetPage(idx) + if not window.CheckSaveBeforeClosing(): + return False + + return True + + def GetTabInfos(self, tab): + if isinstance(tab, EditorPanel) and \ + not isinstance(tab, (Viewer, + TextViewer, + ResourceEditor, + ConfigurationEditor, + DataTypeEditor)): + return ("confnode", tab.Controler.CTNFullName(), tab.GetTagName()) + elif (isinstance(tab, TextViewer) and + (tab.Controler is None or isinstance(tab.Controler, MiniTextControler))): + return ("confnode", None, tab.GetInstancePath()) + else: + return IDEFrame.GetTabInfos(self, tab) + + def LoadTab(self, notebook, page_infos): + if page_infos[0] == "confnode": + if page_infos[1] is None: + confnode = self.CTR + else: + confnode = self.CTR.GetChildByName(page_infos[1]) + return notebook.GetPageIndex(confnode._OpenView(*page_infos[2:])) + else: + return IDEFrame.LoadTab(self, notebook, page_infos) + + # Strange hack required by WAMP connector, using twisted. + # Twisted reactor needs to be stopped only before quit, + # since it cannot be restarted + ToDoBeforeQuit = [] + + def AddToDoBeforeQuit(self, Thing): + self.ToDoBeforeQuit.append(Thing) + + def OnCloseFrame(self, event): + for evt_type in [wx.EVT_SET_FOCUS, + wx.EVT_KILL_FOCUS, + wx.stc.EVT_STC_UPDATEUI]: + self.LogConsole.Unbind(evt_type) + if self.CTR is None or self.CheckSaveBeforeClosing(_("Close Application")): + if self.CTR is not None: + self.CTR.KillDebugThread() + self.KillLocalRuntime() + + self.SaveLastState() + + for Thing in self.ToDoBeforeQuit: + Thing() + self.ToDoBeforeQuit = [] + + event.Skip() + else: + event.Veto() + + def RefreshFileMenu(self): + self.RefreshRecentProjectsMenu() + + MenuToolBar = self.Panes["MenuToolBar"] + if self.CTR is not None: + selected = self.TabsOpened.GetSelection() + if selected >= 0: + window = self.TabsOpened.GetPage(selected) + viewer_is_modified = window.IsModified() + is_viewer = isinstance(window, Viewer) + else: + viewer_is_modified = is_viewer = False + if self.TabsOpened.GetPageCount() > 0: + self.FileMenu.Enable(wx.ID_CLOSE, True) + if is_viewer: + self.FileMenu.Enable(wx.ID_PREVIEW, True) + self.FileMenu.Enable(wx.ID_PRINT, True) + MenuToolBar.EnableTool(wx.ID_PRINT, True) + else: + self.FileMenu.Enable(wx.ID_PREVIEW, False) + self.FileMenu.Enable(wx.ID_PRINT, False) + MenuToolBar.EnableTool(wx.ID_PRINT, False) + else: + self.FileMenu.Enable(wx.ID_CLOSE, False) + self.FileMenu.Enable(wx.ID_PREVIEW, False) + self.FileMenu.Enable(wx.ID_PRINT, False) + MenuToolBar.EnableTool(wx.ID_PRINT, False) + self.FileMenu.Enable(wx.ID_PAGE_SETUP, True) + project_modified = self.CTR.ProjectTestModified() or viewer_is_modified + self.FileMenu.Enable(wx.ID_SAVE, project_modified) + MenuToolBar.EnableTool(wx.ID_SAVE, project_modified) + self.FileMenu.Enable(wx.ID_SAVEAS, True) + MenuToolBar.EnableTool(wx.ID_SAVEAS, True) + self.FileMenu.Enable(wx.ID_CLOSE_ALL, True) + else: + self.FileMenu.Enable(wx.ID_CLOSE, False) + self.FileMenu.Enable(wx.ID_PAGE_SETUP, False) + self.FileMenu.Enable(wx.ID_PREVIEW, False) + self.FileMenu.Enable(wx.ID_PRINT, False) + MenuToolBar.EnableTool(wx.ID_PRINT, False) + self.FileMenu.Enable(wx.ID_SAVE, False) + MenuToolBar.EnableTool(wx.ID_SAVE, False) + self.FileMenu.Enable(wx.ID_SAVEAS, False) + MenuToolBar.EnableTool(wx.ID_SAVEAS, False) + self.FileMenu.Enable(wx.ID_CLOSE_ALL, False) + + def RefreshRecentProjectsMenu(self): + try: + recent_projects = map(DecodeFileSystemPath, + self.GetConfigEntry("RecentProjects", [])) + except Exception: + recent_projects = [] + + while self.RecentProjectsMenu.GetMenuItemCount() > len(recent_projects): + item = self.RecentProjectsMenu.FindItemByPosition(0) + self.RecentProjectsMenu.RemoveItem(item) + + self.FileMenu.Enable(ID_FILEMENURECENTPROJECTS, len(recent_projects) > 0) + for idx, projectpath in enumerate(recent_projects): + text = u'&%d: %s' % (idx + 1, projectpath) + + if idx < self.RecentProjectsMenu.GetMenuItemCount(): + item = self.RecentProjectsMenu.FindItemByPosition(idx) + id = item.GetId() + item.SetItemLabel(text) + self.Disconnect(id, id, wx.EVT_BUTTON._getEvtType()) + else: + id = wx.NewId() + AppendMenu(self.RecentProjectsMenu, help='', id=id, + kind=wx.ITEM_NORMAL, text=text) + self.Bind(wx.EVT_MENU, self.GenerateOpenRecentProjectFunction(projectpath), id=id) + + def GenerateOpenRecentProjectFunction(self, projectpath): + def OpenRecentProject(event): + if self.CTR is not None and not self.CheckSaveBeforeClosing(): + return + + self.OpenProject(projectpath) + return OpenRecentProject + + def GenerateMenuRecursive(self, items, menu): + for kind, infos in items: + if isinstance(kind, types.ListType): + text, id = infos + submenu = wx.Menu('') + self.GenerateMenuRecursive(kind, submenu) + menu.AppendMenu(id, text, submenu) + elif kind == wx.ITEM_SEPARATOR: + menu.AppendSeparator() + else: + text, id, _help, callback = infos + AppendMenu(menu, help='', id=id, kind=kind, text=text) + if callback is not None: + self.Bind(wx.EVT_MENU, callback, id=id) + + def RefreshEditorToolBar(self): + IDEFrame.RefreshEditorToolBar(self) + self.AUIManager.GetPane("EditorToolBar").Position(2) + self.AUIManager.GetPane("StatusToolBar").Position(1) + self.AUIManager.Update() + + def RefreshStatusToolBar(self): + StatusToolBar = self.Panes["StatusToolBar"] + StatusToolBar.ClearTools() + + if self.CTR is not None: + + for confnode_method in self.CTR.StatusMethods: + if "method" in confnode_method and confnode_method.get("shown", True): + id = wx.NewId() + StatusToolBar.AddSimpleTool( + id, GetBitmap(confnode_method.get("bitmap", "Unknown")), + confnode_method["tooltip"]) + self.Bind(wx.EVT_MENU, self.GetMenuCallBackFunction(confnode_method["method"]), id=id) + + StatusToolBar.Realize() + self.AUIManager.GetPane("StatusToolBar").BestSize(StatusToolBar.GetBestSize()).Show() + else: + self.AUIManager.GetPane("StatusToolBar").Hide() + self.AUIManager.GetPane("EditorToolBar").Position(2) + self.AUIManager.GetPane("StatusToolBar").Position(1) + self.AUIManager.Update() + + def RefreshEditMenu(self): + IDEFrame.RefreshEditMenu(self) + if self.FindFocus() == self.LogConsole: + self.EditMenu.Enable(wx.ID_COPY, True) + self.Panes["MenuToolBar"].EnableTool(wx.ID_COPY, True) + + if self.CTR is not None: + selected = self.TabsOpened.GetSelection() + if selected >= 0: + panel = self.TabsOpened.GetPage(selected) + else: + panel = None + if panel != self.LastPanelSelected: + for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): + item = self.EditMenu.FindItemByPosition(self.EditMenuSize) + if item is not None: + if item.IsSeparator(): + self.EditMenu.RemoveItem(item) + else: + self.EditMenu.Delete(item.GetId()) + self.LastPanelSelected = panel + if panel is not None: + items = panel.GetConfNodeMenuItems() + else: + items = [] + if len(items) > 0: + self.EditMenu.AppendSeparator() + self.GenerateMenuRecursive(items, self.EditMenu) + if panel is not None: + panel.RefreshConfNodeMenu(self.EditMenu) + else: + for i in xrange(self.EditMenuSize, self.EditMenu.GetMenuItemCount()): + item = self.EditMenu.FindItemByPosition(i) + if item is not None: + if item.IsSeparator(): + self.EditMenu.RemoveItem(item) + else: + self.EditMenu.Delete(item.GetId()) + self.LastPanelSelected = None + self.MenuBar.UpdateMenus() + + def RefreshAll(self): + self.RefreshStatusToolBar() + + def GetMenuCallBackFunction(self, method): + """ Generate the callbackfunc for a given CTR method""" + def OnMenu(event): + # Disable button to prevent re-entrant call + event.GetEventObject().Disable() + # Call + getattr(self.CTR, method)() + # Re-enable button + event.GetEventObject().Enable() + return OnMenu + + def GetConfigEntry(self, entry_name, default): + return cPickle.loads(str(self.Config.Read(entry_name, cPickle.dumps(default)))) + + def ResetConnectionStatusBar(self): + for field in xrange(self.ConnectionStatusBar.GetFieldsCount()): + self.ConnectionStatusBar.SetStatusText('', field) + + def ResetView(self): + IDEFrame.ResetView(self) + self.ConfNodeInfos = {} + if self.CTR is not None: + self.CTR.CloseProject() + self.CTR = None + self.Log.flush() + if self.EnableDebug: + self.DebugVariablePanel.SetDataProducer(None) + self.ResetConnectionStatusBar() + + def RefreshConfigRecentProjects(self, projectpath, err=False): + try: + recent_projects = map(DecodeFileSystemPath, + self.GetConfigEntry("RecentProjects", [])) + except Exception: + recent_projects = [] + if projectpath in recent_projects: + recent_projects.remove(projectpath) + if not err: + recent_projects.insert(0, projectpath) + self.Config.Write("RecentProjects", cPickle.dumps( + map(EncodeFileSystemPath, recent_projects[:MAX_RECENT_PROJECTS]))) + self.Config.Flush() + + def ResetPerspective(self): + IDEFrame.ResetPerspective(self) + self.RefreshStatusToolBar() + + def OnNewProjectMenu(self, event): + if self.CTR is not None and not self.CheckSaveBeforeClosing(): + return + + try: + defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) + except Exception: + defaultpath = os.path.expanduser("~") + + dialog = wx.DirDialog(self, _("Choose an empty directory for new project"), defaultpath) + if dialog.ShowModal() == wx.ID_OK: + projectpath = dialog.GetPath() + self.Config.Write("lastopenedfolder", + EncodeFileSystemPath(os.path.dirname(projectpath))) + self.Config.Flush() + self.ResetView() + ctr = ProjectController(self, self.Log) + result = ctr.NewProject(projectpath) + if not result: + self.CTR = ctr + self.Controler = self.CTR + self.LibraryPanel.SetController(self.Controler) + self.ProjectTree.Enable(True) + self.PouInstanceVariablesPanel.SetController(self.Controler) + self.RefreshConfigRecentProjects(projectpath) + if self.EnableDebug: + self.DebugVariablePanel.SetDataProducer(self.CTR) + self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + IDEFrame.OnAddNewProject(self, event) + else: + self.ResetView() + self.ShowErrorMessage(result) + self.RefreshAll() + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) + dialog.Destroy() + + def OnOpenProjectMenu(self, event): + if self.CTR is not None and not self.CheckSaveBeforeClosing(): + return + + try: + defaultpath = DecodeFileSystemPath(self.Config.Read("lastopenedfolder")) + except Exception: + defaultpath = os.path.expanduser("~") + + dialog = wx.DirDialog(self, _("Choose a project"), defaultpath, + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + if dialog.ShowModal() == wx.ID_OK: + self.OpenProject(dialog.GetPath()) + dialog.Destroy() + + def OpenProject(self, projectpath): + if os.path.isdir(projectpath): + self.Config.Write("lastopenedfolder", + EncodeFileSystemPath(os.path.dirname(projectpath))) + self.Config.Flush() + self.ResetView() + self.CTR = ProjectController(self, self.Log) + self.Controler = self.CTR + result, err = self.CTR.LoadProject(projectpath) + if not result: + self.LibraryPanel.SetController(self.Controler) + self.ProjectTree.Enable(True) + self.PouInstanceVariablesPanel.SetController(self.Controler) + if self.EnableDebug: + self.DebugVariablePanel.SetDataProducer(self.CTR) + self._Refresh(PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + else: + self.ResetView() + self.ShowErrorMessage(result) + self.RefreshAll() + self.SearchResultPanel.ResetSearchResults() + else: + self.ShowErrorMessage(_("\"%s\" folder is not a valid Beremiz project\n") % projectpath) + err = True + self.RefreshConfigRecentProjects(projectpath, err) + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) + + def OnCloseProjectMenu(self, event): + if self.CTR is not None and not self.CheckSaveBeforeClosing(): + return + + self.ResetView() + self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU) + self.RefreshAll() + + def OnSaveProjectMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.Save() + if self.CTR is not None: + self.CTR.SaveProject() + self.RefreshAll() + self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) + + def OnSaveProjectAsMenu(self, event): + selected = self.TabsOpened.GetSelection() + if selected != -1: + window = self.TabsOpened.GetPage(selected) + window.SaveAs() + if self.CTR is not None: + self.CTR.SaveProjectAs() + self.RefreshAll() + self.RefreshConfigRecentProjects(self.CTR.ProjectPath) + self._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) + + def OnQuitMenu(self, event): + self.Close() + + def OnAboutMenu(self, event): + info = version.GetAboutDialogInfo() + ShowAboutDialog(self, info) + + def OnProjectTreeItemBeginEdit(self, event): + selected = event.GetItem() + if self.ProjectTree.GetPyData(selected)["type"] == ITEM_CONFNODE: + event.Veto() + else: + IDEFrame.OnProjectTreeItemBeginEdit(self, event) + + def OnProjectTreeRightUp(self, event): + item = event.GetItem() + item_infos = self.ProjectTree.GetPyData(item) + + if item_infos["type"] == ITEM_CONFNODE: + confnode_menu = wx.Menu(title='') + + confnode = item_infos["confnode"] + if confnode is not None: + menu_items = confnode.GetContextualMenuItems() + if menu_items is not None: + for text, help, callback in menu_items: + new_id = wx.NewId() + confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=text) + self.Bind(wx.EVT_MENU, callback, id=new_id) + else: + for name, XSDClass, help in confnode.CTNChildrenTypes: + if not hasattr(XSDClass, 'CTNMaxCount') or not confnode.Children.get(name) \ + or len(confnode.Children[name]) < XSDClass.CTNMaxCount: + new_id = wx.NewId() + confnode_menu.Append(help=help, id=new_id, kind=wx.ITEM_NORMAL, text=_("Add") + " " + name) + self.Bind(wx.EVT_MENU, self.GetAddConfNodeFunction(name, confnode), id=new_id) + new_id = wx.NewId() + AppendMenu(confnode_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Delete")) + self.Bind(wx.EVT_MENU, self.GetDeleteMenuFunction(confnode), id=new_id) + + self.PopupMenu(confnode_menu) + confnode_menu.Destroy() + + event.Skip() + elif item_infos["type"] == ITEM_RESOURCE: + # prevent last resource to be delted + parent = self.ProjectTree.GetItemParent(item) + parent_name = self.ProjectTree.GetItemText(parent) + if parent_name == _("Resources"): + IDEFrame.OnProjectTreeRightUp(self, event) + else: + IDEFrame.OnProjectTreeRightUp(self, event) + + def OnProjectTreeItemActivated(self, event): + selected = event.GetItem() + item_infos = self.ProjectTree.GetPyData(selected) + if item_infos["type"] == ITEM_CONFNODE: + item_infos["confnode"]._OpenView() + event.Skip() + elif item_infos["type"] == ITEM_PROJECT: + self.CTR._OpenView() + else: + IDEFrame.OnProjectTreeItemActivated(self, event) + + def ProjectTreeItemSelect(self, select_item): + if select_item is not None and select_item.IsOk(): + item_infos = self.ProjectTree.GetPyData(select_item) + if item_infos["type"] == ITEM_CONFNODE: + item_infos["confnode"]._OpenView(onlyopened=True) + elif item_infos["type"] == ITEM_PROJECT: + self.CTR._OpenView(onlyopened=True) + else: + IDEFrame.ProjectTreeItemSelect(self, select_item) + + def SelectProjectTreeItem(self, tagname): + if self.ProjectTree is not None: + root = self.ProjectTree.GetRootItem() + if root.IsOk(): + words = tagname.split("::") + if len(words) == 1: + if tagname == "Project": + self.SelectedItem = root + self.ProjectTree.SelectItem(root) + self.ResetSelectedItem() + else: + return self.RecursiveProjectTreeItemSelection( + root, + [(word, ITEM_CONFNODE) for word in tagname.split(".")]) + elif words[0] == "R": + return self.RecursiveProjectTreeItemSelection(root, [(words[2], ITEM_RESOURCE)]) + elif not os.path.exists(words[0]): + IDEFrame.SelectProjectTreeItem(self, tagname) + + def GetAddConfNodeFunction(self, name, confnode=None): + def AddConfNodeMenuFunction(event): + wx.CallAfter(self.AddConfNode, name, confnode) + return AddConfNodeMenuFunction + + def GetDeleteMenuFunction(self, confnode): + def DeleteMenuFunction(event): + wx.CallAfter(self.DeleteConfNode, confnode) + return DeleteMenuFunction + + def AddConfNode(self, ConfNodeType, confnode=None): + if self.CTR.CheckProjectPathPerm(): + ConfNodeName = "%s_0" % ConfNodeType + if confnode is not None: + confnode.CTNAddChild(ConfNodeName, ConfNodeType) + else: + self.CTR.CTNAddChild(ConfNodeName, ConfNodeType) + self._Refresh(TITLE, FILEMENU, PROJECTTREE) + + def DeleteConfNode(self, confnode): + if self.CTR.CheckProjectPathPerm(): + dialog = wx.MessageDialog( + self, + _("Really delete node '%s'?") % confnode.CTNName(), + _("Remove %s node") % confnode.CTNType, + wx.YES_NO | wx.NO_DEFAULT) + if dialog.ShowModal() == wx.ID_YES: + confnode.CTNRemove() + del confnode + self._Refresh(TITLE, FILEMENU, PROJECTTREE) + dialog.Destroy() + +# ------------------------------------------------------------------------------- +# Highlights showing functions +# ------------------------------------------------------------------------------- + + def ShowHighlight(self, infos, start, end, highlight_type): + config_name = self.Controler.GetProjectMainConfigurationName() + if config_name is not None and infos[0] == self.Controler.ComputeConfigurationName(config_name): + self.CTR._OpenView() + selected = self.TabsOpened.GetSelection() + if selected != -1: + viewer = self.TabsOpened.GetPage(selected) + viewer.AddHighlight(infos[1:], start, end, highlight_type) + else: + IDEFrame.ShowHighlight(self, infos, start, end, highlight_type) diff -r c1298e7ffe3a -r 8391c11477f4 Beremiz_service.py --- a/Beremiz_service.py Fri Mar 24 12:07:47 2017 +0000 +++ b/Beremiz_service.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,11 +23,24 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys, getopt -from threading import Thread + +from __future__ import absolute_import +from __future__ import print_function +import os +import sys +import getopt +import threading +from threading import Thread, currentThread, Semaphore +import traceback +import __builtin__ +import Pyro.core as pyro + +from runtime import PLCObject, ServicePublisher +import util.paths as paths + def usage(): - print """ + print(""" Usage of Beremiz PLC execution service :\n %s {[-n servicename] [-i IP] [-p port] [-x enabletaskbar] [-a autostart]|-h|--help} working_dir -n - zeroconf service name (default:disabled) @@ -38,16 +52,18 @@ -t - enable/disable Twisted web interface (0:disable 1:enable) (default:1) -w - web server port or "off" (default:8009) -c - WAMP client config file or "off" (default:wampconf.json) + -s - WAMP client secret, given as a file or "off" -e - python extension (absolute path .py) working_dir - directory where are stored PLC files -"""%sys.argv[0] +""" % sys.argv[0]) + try: - opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:w:c:e:h") + opts, argv = getopt.getopt(sys.argv[1:], "i:p:n:x:t:a:w:c:e:s:h") except getopt.GetoptError, err: # print help information and exit: - print str(err) # will print something like "option -a not recognized" + print(str(err)) # will print something like "option -a not recognized" usage() sys.exit(2) @@ -55,7 +71,8 @@ given_ip = None port = 3000 webport = 8009 -wampconf = "wampconf.json" +wampsecret = None +wampconf = None servicename = None autostart = False enablewx = True @@ -63,7 +80,7 @@ enabletwisted = True havetwisted = False -extensions=[] +extensions = [] for o, a in opts: if o == "-h": @@ -90,13 +107,18 @@ webport = None if a == "off" else int(a) elif o == "-c": wampconf = None if a == "off" else a + elif o == "-s": + wampsecret = None if a == "off" else a elif o == "-e": - extensions.append(a) + l = list(os.path.split(os.path.realpath(a))) + l.reverse() + extensions.append(l) else: usage() sys.exit() -beremiz_dir = os.path.dirname(os.path.realpath(__file__)) + +beremiz_dir = paths.AbsDir(__file__) if len(argv) > 1: usage() @@ -106,21 +128,19 @@ os.chdir(WorkingDir) elif len(argv) == 0: WorkingDir = os.getcwd() - argv=[WorkingDir] - -import __builtin__ + argv = [WorkingDir] + if __name__ == '__main__': __builtin__.__dict__['_'] = lambda x: x + def Bpath(*args): - return os.path.join(beremiz_dir,*args) + return os.path.join(beremiz_dir, *args) + def SetupI18n(): - # Import module for internationalization - import gettext - # Get folder containing translation files - localedir = os.path.join(beremiz_dir,"locale") + localedir = os.path.join(beremiz_dir, "locale") # Get the default language langid = wx.LANGUAGE_DEFAULT # Define translation domain (name of translation files) @@ -136,12 +156,10 @@ # Define locale domain loc.AddCatalog(domain) - import locale global default_locale default_locale = locale.getdefaultlocale()[1] - # sys.stdout.encoding = default_locale # if Beremiz_service is started from Beremiz IDE # sys.stdout.encoding is None (that means 'ascii' encoding'). @@ -154,19 +172,17 @@ __builtin__.__dict__['_'] = unicode_translation # __builtin__.__dict__['_'] = wx.GetTranslation + if enablewx: try: - import wxversion - wxversion.select(['2.8', '3.0']) import wx havewx = True - except: - print "Wx unavailable !" + except ImportError: + print("Wx unavailable !") havewx = False if havewx: import re - from threading import Thread, currentThread from types import * if wx.VERSION >= (3, 0, 0): @@ -184,15 +200,14 @@ class ParamsEntryDialog(wx.TextEntryDialog): if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: event(self, function) - - def __init__(self, parent, message, caption = _("Please enter text"), defaultValue = "", - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): + def __init__(self, parent, message, caption=_("Please enter text"), defaultValue="", + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition): wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) self.Tests = [] @@ -205,10 +220,10 @@ def OnOK(self, event): value = self.GetValue() - texts = {"value" : value} + texts = {"value": value} for function, message in self.Tests: if not function(value): - message = wx.MessageDialog(self, message%texts, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, message % texts, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() return @@ -260,7 +275,7 @@ menu = wx.Menu() menu.Append(self.TBMENU_START, _("Start PLC")) menu.Append(self.TBMENU_STOP, _("Stop PLC")) - if self.level==1: + if self.level == 1: menu.AppendSeparator() menu.Append(self.TBMENU_CHANGE_NAME, _("Change Name")) menu.Append(self.TBMENU_CHANGE_INTERFACE, _("Change IP of interface to bind")) @@ -283,60 +298,60 @@ elif "wxGTK" in wx.PlatformInfo: img = img.Scale(22, 22) # wxMac can be any size upto 128x128, so leave the source img alone.... - icon = wx.IconFromBitmap(img.ConvertToBitmap() ) + icon = wx.IconFromBitmap(img.ConvertToBitmap()) return icon def OnTaskBarStartPLC(self, evt): if self.pyroserver.plcobj is not None: plcstatus = self.pyroserver.plcobj.GetPLCstatus()[0] - if plcstatus is "Stopped": + if plcstatus is "Stopped": self.pyroserver.plcobj.StartPLC() else: - print _("PLC is empty or already started.") + print(_("PLC is empty or already started.")) def OnTaskBarStopPLC(self, evt): if self.pyroserver.plcobj is not None: if self.pyroserver.plcobj.GetPLCstatus()[0] == "Started": Thread(target=self.pyroserver.plcobj.StopPLC).start() else: - print _("PLC is not started.") + print(_("PLC is not started.")) def OnTaskBarChangeInterface(self, evt): ip_addr = self.pyroserver.ip_addr ip_addr = '' if ip_addr is None else ip_addr - dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr) + dlg = ParamsEntryDialog(None, _("Enter the IP of the interface to bind"), defaultValue=ip_addr) dlg.SetTests([(re.compile('\d{1,3}(?:\.\d{1,3}){3}$').match, _("IP is not valid!")), - ( lambda x :len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, _("IP is not valid!")) - ]) + (lambda x:len([x for x in x.split(".") if 0 <= int(x) <= 255]) == 4, + _("IP is not valid!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.ip_addr = dlg.GetValue() - self.pyroserver.Stop() + self.pyroserver.Restart() def OnTaskBarChangePort(self, evt): dlg = ParamsEntryDialog(None, _("Enter a port number "), defaultValue=str(self.pyroserver.port)) - dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port : 0 <= int(port) <= 65535 , _("Port number must be 0 <= port <= 65535!"))]) + dlg.SetTests([(UnicodeType.isdigit, _("Port number must be an integer!")), (lambda port: 0 <= int(port) <= 65535, _("Port number must be 0 <= port <= 65535!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.port = int(dlg.GetValue()) - self.pyroserver.Stop() + self.pyroserver.Restart() def OnTaskBarChangeWorkingDir(self, evt): dlg = wx.DirDialog(None, _("Choose a working directory "), self.pyroserver.workdir, wx.DD_NEW_DIR_BUTTON) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.workdir = dlg.GetPath() - self.pyroserver.Stop() + self.pyroserver.Restart() def OnTaskBarChangeName(self, evt): servicename = self.pyroserver.servicename servicename = '' if servicename is None else servicename dlg = ParamsEntryDialog(None, _("Enter a name "), defaultValue=servicename) - dlg.SetTests([(lambda name : len(name) is not 0 , _("Name must not be null!"))]) + dlg.SetTests([(lambda name: len(name) is not 0, _("Name must not be null!"))]) if dlg.ShowModal() == wx.ID_OK: self.pyroserver.servicename = dlg.GetValue() self.pyroserver.Restart() def _LiveShellLocals(self): if self.pyroserver.plcobj is not None: - return {"locals":self.pyroserver.plcobj.python_runtime_vars} + return {"locals": self.pyroserver.plcobj.python_runtime_vars} else: return {} @@ -361,7 +376,7 @@ wx.CallAfter(wx.GetApp().ExitMainLoop) def UpdateIcon(self, plcstatus): - if plcstatus is "Started" : + if plcstatus is "Started": currenticon = self.MakeIcon(starticon) elif plcstatus is "Stopped": currenticon = self.MakeIcon(stopicon) @@ -369,20 +384,20 @@ currenticon = self.MakeIcon(defaulticon) self.SetIcon(currenticon, "Beremiz Service") -from runtime import PLCObject, PLCprint, ServicePublisher -import Pyro.core as pyro if not os.path.isdir(WorkingDir): os.mkdir(WorkingDir) + def default_evaluator(tocall, *args, **kwargs): try: - res=(tocall(*args,**kwargs), None) + res = (tocall(*args, **kwargs), None) except Exception: - res=(None, sys.exc_info()) + res = (None, sys.exc_info()) return res -class Server(): + +class Server(object): def __init__(self, servicename, ip_addr, port, workdir, argv, autostart=False, statuschange=None, evaluator=default_evaluator, @@ -403,56 +418,58 @@ def Loop(self): while self.continueloop: + pyro.initServer() + self.daemon = pyro.Daemon(host=self.ip_addr, port=self.port) + # pyro never frees memory after connection close if no timeout set + self.daemon.setTimeout(1) self.Start() + self.daemon.requestLoop() + self.daemon.sock.close() def Restart(self): - self.Stop() + self._stop() def Quit(self): self.continueloop = False if self.plcobj is not None: self.plcobj.StopPLC() self.plcobj.UnLoadPLC() - self.Stop() + self._stop() def Start(self): - pyro.initServer() - self.daemon=pyro.Daemon(host=self.ip_addr, port=self.port) self.plcobj = PLCObject(self.workdir, self.daemon, self.argv, self.statuschange, self.evaluator, self.pyruntimevars) - uri = self.daemon.connect(self.plcobj,"PLCObject") - - print _("Pyro port :"), self.port - print _("Pyro object's uri :"), uri + + uri = self.daemon.connect(self.plcobj, "PLCObject") + + print(_("Pyro port :"), self.port) + print(_("Pyro object's uri :"), uri) # Beremiz IDE detects daemon start by looking # for self.workdir in the daemon's stdout. # Therefore don't delete the following line - print _("Current working directory :"), self.workdir + print(_("Current working directory :"), self.workdir) # Configure and publish service # Not publish service if localhost in address params - if (self.servicename is not None and - self.ip_addr is not None and - self.ip_addr != "localhost" and - self.ip_addr != "127.0.0.1"): - print _("Publishing service on local network") + if self.servicename is not None and \ + self.ip_addr is not None and \ + self.ip_addr != "localhost" and \ + self.ip_addr != "127.0.0.1": + print(_("Publishing service on local network")) self.servicepublisher = ServicePublisher.ServicePublisher() self.servicepublisher.RegisterService(self.servicename, self.ip_addr, self.port) self.plcobj.AutoLoad() if self.plcobj.GetPLCstatus()[0] != "Empty": - if self.autostart : + if self.autostart: self.plcobj.StartPLC() self.plcobj.StatusChange() sys.stdout.flush() - self.daemon.requestLoop() - self.daemon.sock.close() - - def Stop(self): + def _stop(self): if self.plcobj is not None: self.plcobj.StopPLC() if self.servicepublisher is not None: @@ -460,53 +477,50 @@ self.servicepublisher = None self.daemon.shutdown(True) + if enabletwisted: import warnings with warnings.catch_warnings(): warnings.simplefilter("ignore") try: - from threading import Thread, currentThread if havewx: from twisted.internet import wxreactor wxreactor.install() from twisted.internet import reactor havetwisted = True - except: - print _("Twisted unavailable.") + except ImportError: + print(_("Twisted unavailable.")) havetwisted = False pyruntimevars = {} statuschange = [] if havetwisted: - if havewx: reactor.registerWxApp(app) if havewx: - from threading import Semaphore wx_eval_lock = Semaphore(0) main_thread = currentThread() def statuschangeTskBar(status): - wx.CallAfter(taskbar_instance.UpdateIcon,status) + wx.CallAfter(taskbar_instance.UpdateIcon, status) statuschange.append(statuschangeTskBar) def wx_evaluator(obj, *args, **kwargs): - tocall,args,kwargs = obj.call + tocall, args, kwargs = obj.call obj.res = default_evaluator(tocall, *args, **kwargs) wx_eval_lock.release() def evaluator(tocall, *args, **kwargs): - global main_thread - if(main_thread == currentThread()): + if main_thread == currentThread(): # avoid dead lock if called from the wx mainloop return default_evaluator(tocall, *args, **kwargs) else: - o=type('',(object,),dict(call=(tocall, args, kwargs), res=None)) - wx.CallAfter(wx_evaluator,o) + o = type('', (object,), dict(call=(tocall, args, kwargs), res=None)) + wx.CallAfter(wx_evaluator, o) wx_eval_lock.acquire() return o.res @@ -522,72 +536,97 @@ # Exception hooks s -import threading, traceback + + + +def LogMessageAndException(msg, exp=None): + if exp is None: + exp = sys.exc_info() + if pyroserver.plcobj is not None: + pyroserver.plcobj.LogMessage(0, msg + '\n'.join(traceback.format_exception(*exp))) + else: + print(msg) + traceback.print_exception(*exp) + def LogException(*exp): - if pyroserver.plcobj is not None: - pyroserver.plcobj.LogMessage(0,'\n'.join(traceback.format_exception(*exp))) - else: - traceback.print_exception(*exp) + LogExceptionAndMessage("",exp) sys.excepthook = LogException + + def installThreadExcepthook(): init_old = threading.Thread.__init__ + def init(self, *args, **kwargs): init_old(self, *args, **kwargs) run_old = self.run + def run_with_except_hook(*args, **kw): try: run_old(*args, **kw) except (KeyboardInterrupt, SystemExit): raise - except: + except Exception: sys.excepthook(*sys.exc_info()) self.run = run_with_except_hook threading.Thread.__init__ = init + + installThreadExcepthook() if havetwisted: - if webport is not None : + if webport is not None: try: - import runtime.NevowServer as NS + import runtime.NevowServer as NS # pylint: disable=ungrouped-imports except Exception, e: - print _("Nevow/Athena import failed :"), e + print(_("Nevow/Athena import failed :"), e) webport = None NS.WorkingDir = WorkingDir - if wampconf is not None : + if wampconf is None: + _wampconf = os.path.join(WorkingDir, "wampconf.json") + if os.path.exists(_wampconf): + wampconf = _wampconf + + if wampconf is not None: try: - import runtime.WampClient as WC + import runtime.WampClient as WC # pylint: disable=ungrouped-imports except Exception, e: - print _("WAMP import failed :"), e + print(_("WAMP import failed :"), e) wampconf = None # Load extensions -for extfilename in extensions: - extension_folder = os.path.split(os.path.realpath(extfilename))[0] +for extention_file, extension_folder in extensions: sys.path.append(extension_folder) - execfile(extfilename, locals()) + execfile(os.path.join(extension_folder, extention_file), locals()) if havetwisted: - if webport is not None : + if webport is not None: try: website = NS.RegisterWebsite(webport) pyruntimevars["website"] = website statuschange.append(NS.website_statuslistener_factory(website)) - except Exception, e: - print _("Nevow Web service failed. "), e - - if wampconf is not None : + except Exception: + LogMessageAndException(_("Nevow Web service failed. ")) + + if wampconf is not None: try: - WC.RegisterWampClient(wampconf) - pyruntimevars["wampsession"] = WC.GetSession - WC.SetServer(pyroserver) - except Exception, e: - print _("WAMP client startup failed. "), e + _wampconf = WC.LoadWampClientConf(wampconf) + if _wampconf: + if _wampconf["url"]: # TODO : test more ? + WC.RegisterWampClient(wampconf, wampsecret) + pyruntimevars["wampsession"] = WC.GetSession + WC.SetServer(pyroserver) + else: + raise Exception(_("WAMP config is incomplete.")) + else: + raise Exception(_("WAMP config is missing.")) + except Exception: + LogMessageAndException(_("WAMP client startup failed. ")) if havetwisted or havewx: - pyro_thread=Thread(target=pyroserver.Loop) + pyro_thread = Thread(target=pyroserver.Loop) pyro_thread.start() if havetwisted: @@ -595,9 +634,9 @@ elif havewx: app.MainLoop() else: - try : + try: pyroserver.Loop() - except KeyboardInterrupt,e: + except KeyboardInterrupt: pass pyroserver.Quit() sys.exit(0) diff -r c1298e7ffe3a -r 8391c11477f4 COPYING.Runtime --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING.Runtime Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff -r c1298e7ffe3a -r 8391c11477f4 CodeFileTreeNode.py --- a/CodeFileTreeNode.py Fri Mar 24 12:07:47 2017 +0000 +++ b/CodeFileTreeNode.py Tue Jan 30 16:06:58 2018 +0100 @@ -23,7 +23,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re, traceback +from __future__ import absolute_import +import os +import re +import traceback from copy import deepcopy from lxml import etree @@ -80,11 +83,12 @@ SECTION_TAG_ELEMENT = "" -class CodeFile: - + +class CodeFile(object): + CODEFILE_NAME = "CodeFile" SECTIONS_NAMES = [] - + def __init__(self): sections_str = {"codefile_name": self.CODEFILE_NAME} if "includes" in self.SECTIONS_NAMES: @@ -94,36 +98,36 @@ sections_str["sections"] = "\n".join( [SECTION_TAG_ELEMENT % name for name in self.SECTIONS_NAMES if name != "includes"]) - + self.CodeFileParser = GenerateParserFromXSDstring( CODEFILE_XSD % sections_str) self.CodeFileVariables = etree.XPath("variables/variable") - + filepath = self.CodeFileName() - if os.path.isfile(filepath): xmlfile = open(filepath, 'r') codefile_xml = xmlfile.read() xmlfile.close() - + codefile_xml = codefile_xml.replace( - '<%s>' % self.CODEFILE_NAME, + '<%s>' % self.CODEFILE_NAME, '<%s xmlns:xhtml="http://www.w3.org/1999/xhtml">' % self.CODEFILE_NAME) for cre, repl in [ - (re.compile("(?)(?:)(?!)"), "]]>")]: + (re.compile("(?)(?:)(?!)"), "]]>")]: codefile_xml = cre.sub(repl, codefile_xml) - + try: self.CodeFile, error = self.CodeFileParser.LoadXMLString(codefile_xml) if error is not None: (fname, lnum, src) = ((self.CODEFILE_NAME,) + error) - self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname, a2 = lnum, a3 = src)) + self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) self.CreateCodeFileBuffer(True) except Exception, exc: - msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1 = CTNName, a2 = unicode(exc)) + msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=self.CTNName(), a2=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) + raise Exception else: self.CodeFile = self.CodeFileParser.CreateRoot() self.CreateCodeFileBuffer(False) @@ -132,13 +136,13 @@ def GetBaseTypes(self): return self.GetCTRoot().GetBaseTypes() - def GetDataTypes(self, basetypes = False): + def GetDataTypes(self, basetypes=False): return self.GetCTRoot().GetDataTypes(basetypes=basetypes) def GenerateNewName(self, format, start_idx): return self.GetCTRoot().GenerateNewName( None, None, format, start_idx, - dict([(var.getname().upper(), True) + dict([(var.getname().upper(), True) for var in self.CodeFile.variables.getvariable()])) def SetVariables(self, variables): @@ -152,17 +156,18 @@ variable.setonchange(var["OnChange"]) variable.setopts(var["Options"]) self.CodeFile.variables.appendvariable(variable) - + def GetVariables(self): datas = [] for var in self.CodeFileVariables(self.CodeFile): - datas.append({"Name" : var.getname(), - "Type" : var.gettype(), - "Initial" : var.getinitial(), - "Description" : var.getdesc(), - "OnChange" : var.getonchange(), - "Options" : var.getopts(), - }) + datas.append({ + "Name": var.getname(), + "Type": var.gettype(), + "Initial": var.getinitial(), + "Description": var.getdesc(), + "OnChange": var.getonchange(), + "Options": var.getopts(), + }) return datas def SetTextParts(self, parts): @@ -170,47 +175,47 @@ section_code = parts.get(section) if section_code is not None: getattr(self.CodeFile, section).setanyText(section_code) - + def GetTextParts(self): return dict([(section, getattr(self.CodeFile, section).getanyText()) for section in self.SECTIONS_NAMES]) - + def CTNTestModified(self): - return self.ChangesToSave or not self.CodeFileIsSaved() + return self.ChangesToSave or not self.CodeFileIsSaved() def OnCTNSave(self, from_project_path=None): filepath = self.CodeFileName() - - xmlfile = open(filepath,"w") + + xmlfile = open(filepath, "w") xmlfile.write(etree.tostring( - self.CodeFile, - pretty_print=True, - xml_declaration=True, + self.CodeFile, + pretty_print=True, + xml_declaration=True, encoding='utf-8')) xmlfile.close() - + self.MarkCodeFileAsSaved() return True def CTNGlobalInstances(self): variables = self.CodeFileVariables(self.CodeFile) - ret = [(variable.getname(), - variable.gettype(), - variable.getinitial()) - for variable in variables] + ret = [(variable.getname(), + variable.gettype(), + variable.getinitial()) + for variable in variables] ret.extend([("On"+variable.getname()+"Change", "python_poll", "") - for variable in variables - if variable.getonchange()]) + for variable in variables + if variable.getonchange()]) return ret -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Current Buffering Management Functions -#------------------------------------------------------------------------------- - - """ - Return a copy of the codefile model - """ +# ------------------------------------------------------------------------------- + def Copy(self, model): + """ + Return a copy of the codefile model + """ return deepcopy(model) def CreateCodeFileBuffer(self, saved): @@ -219,31 +224,30 @@ def BufferCodeFile(self): self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile)) - + def StartBuffering(self): self.Buffering = True - + def EndBuffering(self): if self.Buffering: self.CodeFileBuffer.Buffering(self.CodeFileParser.Dumps(self.CodeFile)) self.Buffering = False - + def MarkCodeFileAsSaved(self): self.EndBuffering() self.CodeFileBuffer.CurrentSaved() - + def CodeFileIsSaved(self): return self.CodeFileBuffer.IsCurrentSaved() and not self.Buffering - + def LoadPrevious(self): self.EndBuffering() self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Previous()) - + def LoadNext(self): self.CodeFile = self.CodeFileParser.Loads(self.CodeFileBuffer.Next()) - + def GetBufferState(self): first = self.CodeFileBuffer.IsFirst() and not self.Buffering last = self.CodeFileBuffer.IsLast() return not first, not last - diff -r c1298e7ffe3a -r 8391c11477f4 ConfigTreeNode.py --- a/ConfigTreeNode.py Fri Mar 24 12:07:47 2017 +0000 +++ b/ConfigTreeNode.py Tue Jan 30 16:06:58 2018 +0100 @@ -31,14 +31,16 @@ - ... TODO : document """ -import os,traceback,types +from __future__ import absolute_import +import os +import traceback +import types import shutil from lxml import etree from xmlclass import GenerateParserFromXSDstring -from util.misc import GetClassImporter - -from PLCControler import PLCControler, LOCATION_CONFNODE + +from PLCControler import LOCATION_CONFNODE from editors.ConfTreeNodeEditor import ConfTreeNodeEditor _BaseParamsParser = GenerateParserFromXSDstring(""" @@ -53,9 +55,10 @@ """) NameTypeSeparator = '@' -XSDSchemaErrorMessage = _("{a1} XML file doesn't follow XSD schema at line %{a2}:\n{a3}") - -class ConfigTreeNode: +XSDSchemaErrorMessage = _("{a1} XML file doesn't follow XSD schema at line {a2}:\n{a3}") + + +class ConfigTreeNode(object): """ This class is the one that define confnodes. """ @@ -67,7 +70,7 @@ LibraryControler = None EditorType = ConfTreeNodeEditor IconPath = None - + def _AddParamsMembers(self): self.CTNParams = None if self.XSD: @@ -78,7 +81,7 @@ setattr(self, name, obj) def __init__(self): - # Create BaseParam + # Create BaseParam self.BaseParams = _BaseParamsParser.CreateRoot() self.MandatoryParams = ("BaseParams", self.BaseParams) self._AddParamsMembers() @@ -86,39 +89,39 @@ self._View = None # copy ConfNodeMethods so that it can be later customized self.ConfNodeMethods = [dic.copy() for dic in self.ConfNodeMethods] - + def ConfNodeBaseXmlFilePath(self, CTNName=None): return os.path.join(self.CTNPath(CTNName), "baseconfnode.xml") - + def ConfNodeXmlFilePath(self, CTNName=None): return os.path.join(self.CTNPath(CTNName), "confnode.xml") def ConfNodePath(self): return os.path.join(self.CTNParent.ConfNodePath(), self.CTNType) - def CTNPath(self,CTNName=None,project_path=None): + def CTNPath(self, CTNName=None, project_path=None): if not CTNName: CTNName = self.CTNName() if not project_path: project_path = self.CTNParent.CTNPath() return os.path.join(project_path, CTNName + NameTypeSeparator + self.CTNType) - + def CTNName(self): return self.BaseParams.getName() - + def CTNEnabled(self): return self.BaseParams.getEnabled() - + def CTNFullName(self): parent = self.CTNParent.CTNFullName() if parent != "": return parent + "." + self.CTNName() return self.BaseParams.getName() - + def GetIconName(self): return None - + def CTNTestModified(self): return self.ChangesToSave @@ -134,15 +137,15 @@ return True return False - + def RemoteExec(self, script, **kwargs): return self.CTNParent.RemoteExec(script, **kwargs) - + def OnCTNSave(self, from_project_path=None): - #Default, do nothing and return success + """Default, do nothing and return success""" return True - def GetParamsAttributes(self, path = None): + def GetParamsAttributes(self, path=None): if path: parts = path.split(".", 1) if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: @@ -154,7 +157,7 @@ if self.CTNParams: params.append(self.CTNParams[1].getElementInfos(self.CTNParams[0])) return params - + def SetParamsAttribute(self, path, value): self.ChangesToSave = True # Filter IEC_Channel and Name, that have specific behavior @@ -169,7 +172,7 @@ res = self.FindNewName(value) self.CTNRequestSave() return res, True - + parts = path.split(".", 1) if self.MandatoryParams and parts[0] == self.MandatoryParams[0]: self.MandatoryParams[1].setElementValue(parts[1], value) @@ -189,32 +192,32 @@ if not os.path.isdir(ctnpath): # Create it os.mkdir(ctnpath) - + # generate XML for base XML parameters controller of the confnode if self.MandatoryParams: - BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(),'w') + BaseXMLFile = open(self.ConfNodeBaseXmlFilePath(), 'w') BaseXMLFile.write(etree.tostring( - self.MandatoryParams[1], - pretty_print=True, - xml_declaration=True, + self.MandatoryParams[1], + pretty_print=True, + xml_declaration=True, encoding='utf-8')) BaseXMLFile.close() - + # generate XML for XML parameters controller of the confnode if self.CTNParams: - XMLFile = open(self.ConfNodeXmlFilePath(),'w') + XMLFile = open(self.ConfNodeXmlFilePath(), 'w') XMLFile.write(etree.tostring( - self.CTNParams[1], - pretty_print=True, - xml_declaration=True, + self.CTNParams[1], + pretty_print=True, + xml_declaration=True, encoding='utf-8')) XMLFile.close() - + # Call the confnode specific OnCTNSave method result = self.OnCTNSave(from_project_path) if not result: - return _("Error while saving \"%s\"\n")%self.CTNPath() - + return _("Error while saving \"%s\"\n") % self.CTNPath() + # mark confnode as saved self.ChangesToSave = False # go through all children and do the same @@ -226,7 +229,7 @@ if result: return result return None - + def CTNImport(self, src_CTNPath): shutil.copytree(src_CTNPath, self.CTNPath) return True @@ -236,13 +239,13 @@ @return: [(instance_name, instance_type),...] """ return [] - + def _GlobalInstances(self): instances = self.CTNGlobalInstances() for CTNChild in self.IECSortedChildren(): instances.extend(CTNChild._GlobalInstances()) return instances - + def CTNGenerate_C(self, buildpath, locations): """ Generate C code @@ -255,9 +258,9 @@ }, ...] @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND """ - self.GetCTRoot().logger.write_warning(".".join(map(lambda x:str(x), self.GetCurrentLocation())) + " -> Nothing to do\n") - return [],"",False - + self.GetCTRoot().logger.write_warning(".".join(map(str, self.GetCurrentLocation())) + " -> Nothing to do\n") + return [], "", False + def _Generate_C(self, buildpath, locations): # Generate confnodes [(Cfiles, CFLAGS)], LDFLAGS, DoCalls, extra_files # extra_files = [(fname,fobject), ...] @@ -273,46 +276,46 @@ # confnode asks for some LDFLAGS if CTNLDFLAGS: # LDFLAGS can be either string - if type(CTNLDFLAGS)==type(str()): - LDFLAGS=[CTNLDFLAGS] - #or list of strings - elif type(CTNLDFLAGS)==type(list()): - LDFLAGS=CTNLDFLAGS[:] + if isinstance(CTNLDFLAGS, str): + LDFLAGS = [CTNLDFLAGS] + # or list of strings + elif isinstance(CTNLDFLAGS, list): + LDFLAGS = CTNLDFLAGS[:] else: - LDFLAGS=[] - + LDFLAGS = [] + # recurse through all children, and stack their results for CTNChild in self.IECSortedChildren(): new_location = CTNChild.GetCurrentLocation() # How deep are we in the tree ? - depth=len(new_location) + depth = len(new_location) _LocationCFilesAndCFLAGS, _LDFLAGS, _extra_files = \ CTNChild._Generate_C( - #keep the same path + # keep the same path buildpath, # filter locations that start with current IEC location - [loc for loc in locations if loc["LOC"][0:depth] == new_location ]) + [loc for loc in locations if loc["LOC"][0:depth] == new_location]) # stack the result LocationCFilesAndCFLAGS += _LocationCFilesAndCFLAGS LDFLAGS += _LDFLAGS extra_files += _extra_files - + return LocationCFilesAndCFLAGS, LDFLAGS, extra_files def IterChildren(self): - for CTNType, Children in self.Children.items(): + for _CTNType, Children in self.Children.items(): for CTNInstance in Children: yield CTNInstance - + def IECSortedChildren(self): # reorder children by IEC_channels - ordered = [(chld.BaseParams.getIEC_Channel(),chld) for chld in self.IterChildren()] + ordered = [(chld.BaseParams.getIEC_Channel(), chld) for chld in self.IterChildren()] if ordered: ordered.sort() return zip(*ordered)[1] else: return [] - + def _GetChildBySomething(self, something, toks): for CTNInstance in self.IterChildren(): # if match component of the name @@ -320,7 +323,7 @@ # if Name have other components if len(toks) >= 2: # Recurse in order to find the latest object - return CTNInstance._GetChildBySomething( something, toks[1:]) + return CTNInstance._GetChildBySomething(something, toks[1:]) # No sub name -> found return CTNInstance # Not found @@ -338,7 +341,7 @@ return self._GetChildBySomething("IEC_Channel", Location) else: return self - + def GetCurrentLocation(self): """ @return: Tupple containing confnode IEC location of current confnode : %I0.0.4.5 => (0,0,4,5) @@ -349,13 +352,13 @@ """ @return: String "ParentParentName.ParentName.Name" """ - return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + return self.CTNParent._GetCurrentName() + self.BaseParams.getName() def _GetCurrentName(self): """ @return: String "ParentParentName.ParentName.Name." """ - return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + "." + return self.CTNParent._GetCurrentName() + self.BaseParams.getName() + "." def GetCTRoot(self): return self.CTNParent.GetCTRoot() @@ -372,7 +375,7 @@ This function is meant to be overridden by confnodes. It should returns an list of dictionaries - + - IEC_type is an IEC type like BOOL/BYTE/SINT/... - location is a string of this variable's location, like "%IX0.0.0" ''' @@ -389,12 +392,9 @@ Changes Name to DesiredName if available, Name-N if not. @param DesiredName: The desired Name (string) """ - # Get Current Name - CurrentName = self.BaseParams.getName() - # Do nothing if no change - #if CurrentName == DesiredName: return CurrentName + # Build a list of used Name out of parent's Children - AllNames=[] + AllNames = [] for CTNInstance in self.CTNParent.IterChildren(): if CTNInstance != self: AllNames.append(CTNInstance.BaseParams.getName()) @@ -407,9 +407,9 @@ BaseDesiredName = DesiredName suffix = 1 while res in AllNames: - res = "%s_%d"%(BaseDesiredName, suffix) + res = "%s_%d" % (BaseDesiredName, suffix) suffix += 1 - + # Get old path oldname = self.CTNPath() # Check previous confnode existance @@ -421,12 +421,12 @@ shutil.move(oldname, self.CTNPath()) # warn user he has two left hands if DesiredName != res: - msg = _("A child named \"{a1}\" already exists -> \"{a2}\"\n").format(a1 = DesiredName, a2 = res) + msg = _("A child named \"{a1}\" already exists -> \"{a2}\"\n").format(a1=DesiredName, a2=res) self.GetCTRoot().logger.write_warning(msg) return res def GetAllChannels(self): - AllChannels=[] + AllChannels = [] for CTNInstance in self.CTNParent.IterChildren(): if CTNInstance != self: AllChannels.append(CTNInstance.BaseParams.getIEC_Channel()) @@ -441,20 +441,20 @@ # Get Current IEC channel CurrentChannel = self.BaseParams.getIEC_Channel() # Do nothing if no change - #if CurrentChannel == DesiredChannel: return CurrentChannel + # if CurrentChannel == DesiredChannel: return CurrentChannel # Build a list of used Channels out of parent's Children AllChannels = self.GetAllChannels() - + # Now, try to guess the nearest available channel res = DesiredChannel - while res in AllChannels: # While channel not free - if res < CurrentChannel: # Want to go down ? - res -= 1 # Test for n-1 - if res < 0 : - self.GetCTRoot().logger.write_warning(_("Cannot find lower free IEC channel than %d\n")%CurrentChannel) - return CurrentChannel # Can't go bellow 0, do nothing - else : # Want to go up ? - res += 1 # Test for n-1 + while res in AllChannels: # While channel not free + if res < CurrentChannel: # Want to go down ? + res -= 1 # Test for n-1 + if res < 0: + self.GetCTRoot().logger.write_warning(_("Cannot find lower free IEC channel than %d\n") % CurrentChannel) + return CurrentChannel # Can't go bellow 0, do nothing + else: # Want to go up ? + res += 1 # Test for n-1 # Finally set IEC Channel self.BaseParams.setIEC_Channel(res) return res @@ -466,14 +466,14 @@ if self.EditorType is not None: app_frame = self.GetCTRoot().AppFrame if self._View is None and not onlyopened: - + self._View = self.EditorType(app_frame.TabsOpened, self, app_frame) - + if self._View is not None: if name is None: name = self.CTNFullName() app_frame.EditProjectElement(self._View, name) - + return self._View return None @@ -508,7 +508,7 @@ def CTNRemove(self): # Fetch the confnode - #CTNInstance = self.GetChildByName(CTNName) + # CTNInstance = self.GetChildByName(CTNName) # Ask to his parent to remove it self.CTNParent._doRemoveChild(self) @@ -521,97 +521,98 @@ # reorganize self.CTNChildrenTypes tuples from (name, CTNClass, Help) # to ( name, (CTNClass, Help)), an make a dict transpose = zip(*self.CTNChildrenTypes) - CTNChildrenTypes = dict(zip(transpose[0],zip(transpose[1],transpose[2]))) + CTNChildrenTypes = dict(zip(transpose[0], zip(transpose[1], transpose[2]))) # Check that adding this confnode is allowed try: CTNClass, CTNHelp = CTNChildrenTypes[CTNType] except KeyError: - raise Exception, _("Cannot create child {a1} of type {a2} ").format(a1 = CTNName, a2 = CTNType) - + raise Exception(_("Cannot create child {a1} of type {a2} "). + format(a1=CTNName, a2=CTNType)) + # if CTNClass is a class factory, call it. (prevent unneeded imports) - if type(CTNClass) == types.FunctionType: + if isinstance(CTNClass, types.FunctionType): CTNClass = CTNClass() - + # Eventualy Initialize child instance list for this class of confnode ChildrenWithSameClass = self.Children.setdefault(CTNType, list()) # Check count if getattr(CTNClass, "CTNMaxCount", None) and len(ChildrenWithSameClass) >= CTNClass.CTNMaxCount: - msg = _("Max count ({a1}) reached for this confnode of type {a2} ").format(a1 = CTNClass.CTNMaxCount, a2 = CTNType) - raise Exception, msg - + raise Exception( + _("Max count ({a1}) reached for this confnode of type {a2} "). + format(a1=CTNClass.CTNMaxCount, a2=CTNType)) + # create the final class, derived of provided confnode and template class FinalCTNClass(CTNClass, ConfigTreeNode): """ ConfNode class is derivated into FinalCTNClass before being instanciated - This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called + This way __init__ is overloaded to ensure ConfigTreeNode.__init__ is called before CTNClass.__init__, and to do the file related stuff. """ - def __init__(_self): - # self is the parent - _self.CTNParent = self + def __init__(self, parent): + self.CTNParent = parent # Keep track of the confnode type name - _self.CTNType = CTNType + self.CTNType = CTNType # remind the help string, for more fancy display - _self.CTNHelp = CTNHelp + self.CTNHelp = CTNHelp # Call the base confnode template init - change XSD into class members - ConfigTreeNode.__init__(_self) + ConfigTreeNode.__init__(self) # check name is unique - NewCTNName = _self.FindNewName(CTNName) + NewCTNName = self.FindNewName(CTNName) # If dir have already be made, and file exist - if os.path.isdir(_self.CTNPath(NewCTNName)): #and os.path.isfile(_self.ConfNodeXmlFilePath(CTNName)): - #Load the confnode.xml file into parameters members - _self.LoadXMLParams(NewCTNName) + if os.path.isdir(self.CTNPath(NewCTNName)): # and os.path.isfile(self.ConfNodeXmlFilePath(CTNName)): + # Load the confnode.xml file into parameters members + self.LoadXMLParams(NewCTNName) # Basic check. Better to fail immediately. - if (_self.BaseParams.getName() != NewCTNName): - msg = _("Project tree layout do not match confnode.xml {a1}!={a2} ").\ - format(a1 = NewCTNName, a2 = _self.BaseParams.getName()) - raise Exception, msg + if self.BaseParams.getName() != NewCTNName: + raise Exception( + _("Project tree layout do not match confnode.xml {a1}!={a2} "). + format(a1=NewCTNName, a2=self.BaseParams.getName())) # Now, self.CTNPath() should be OK - + # Check that IEC_Channel is not already in use. - _self.FindNewIEC_Channel(_self.BaseParams.getIEC_Channel()) + self.FindNewIEC_Channel(self.BaseParams.getIEC_Channel()) # Call the confnode real __init__ if getattr(CTNClass, "__init__", None): - CTNClass.__init__(_self) - #Load and init all the children - _self.LoadChildren() - #just loaded, nothing to saved - _self.ChangesToSave = False + CTNClass.__init__(self) + # Load and init all the children + self.LoadChildren() + # just loaded, nothing to saved + self.ChangesToSave = False else: # If confnode do not have corresponding file/dirs - they will be created on Save - _self.CTNMakeDir() + self.CTNMakeDir() # Find an IEC number - _self.FindNewIEC_Channel(IEC_Channel) + self.FindNewIEC_Channel(IEC_Channel) # Call the confnode real __init__ if getattr(CTNClass, "__init__", None): - CTNClass.__init__(_self) - _self.CTNRequestSave() - #just created, must be saved - _self.ChangesToSave = True - - def _getBuildPath(_self): - return self._getBuildPath() - + CTNClass.__init__(self) + self.CTNRequestSave() + # just created, must be saved + self.ChangesToSave = True + + def _getBuildPath(self): + return self.CTNParent._getBuildPath() + # Create the object out of the resulting class - newConfNodeOpj = FinalCTNClass() + newConfNodeOpj = FinalCTNClass(self) # Store it in CTNgedChils ChildrenWithSameClass.append(newConfNodeOpj) - + return newConfNodeOpj - + def ClearChildren(self): for child in self.IterChildren(): child.ClearChildren() self.Children = {} - - def LoadXMLParams(self, CTNName = None): + + def LoadXMLParams(self, CTNName=None): methode_name = os.path.join(self.CTNPath(CTNName), "methods.py") if os.path.isfile(methode_name): execfile(methode_name) - + ConfNodeName = CTNName if CTNName is not None else self.CTNName() - + # Get the base xml tree if self.MandatoryParams: try: @@ -619,14 +620,14 @@ self.BaseParams, error = _BaseParamsParser.LoadXMLString(basexmlfile.read()) if error is not None: (fname, lnum, src) = ((ConfNodeName + " BaseParams",) + error) - self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname, a2 = lnum, a3 = src)) + self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) self.MandatoryParams = ("BaseParams", self.BaseParams) basexmlfile.close() except Exception, exc: - msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1 = ConfNodeName, a2 = unicode(exc)) + msg = _("Couldn't load confnode base parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) - + # Get the xml tree if self.CTNParams: try: @@ -634,16 +635,16 @@ obj, error = self.Parser.LoadXMLString(xmlfile.read()) if error is not None: (fname, lnum, src) = ((ConfNodeName,) + error) - self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname, a2 = lnum, a3 = src)) + self.GetCTRoot().logger.write_warning(XSDSchemaErrorMessage.format(a1=fname, a2=lnum, a3=src)) name = obj.getLocalTag() setattr(self, name, obj) self.CTNParams = (name, obj) xmlfile.close() except Exception, exc: - msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1 = ConfNodeName, a2 = unicode(exc)) + msg = _("Couldn't load confnode parameters {a1} :\n {a2}").format(a1=ConfNodeName, a2=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) - + def LoadChildren(self): # Iterate over all CTNName@CTNType in confnode directory, and try to open them for CTNDir in os.listdir(self.CTNPath()): @@ -653,7 +654,6 @@ try: self.CTNAddChild(pname, ptype) except Exception, exc: - msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1 = pname, a2 = ptype, a3 = unicode(exc)) + msg = _("Could not add child \"{a1}\", type {a2} :\n{a3}\n").format(a1=pname, a2=ptype, a3=unicode(exc)) self.GetCTRoot().logger.write_error(msg) self.GetCTRoot().logger.write_error(traceback.format_exc()) - diff -r c1298e7ffe3a -r 8391c11477f4 IDEFrame.py --- a/IDEFrame.py Fri Mar 24 12:07:47 2017 +0000 +++ b/IDEFrame.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,11 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys +from __future__ import absolute_import +import sys import cPickle from types import TupleType - -import wx, wx.grid +import base64 + +import wx +import wx.grid import wx.aui from editors.EditorPanel import EditorPanel @@ -43,48 +46,51 @@ from util.BitmapLibrary import GetBitmap # Define PLCOpenEditor controls id -[ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK, - ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK, - ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER, - ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER, - ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL, - ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT, - ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED, - ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR, - ID_PLCOPENEDITORPROJECTPANEL, +[ + ID_PLCOPENEDITOR, ID_PLCOPENEDITORLEFTNOTEBOOK, + ID_PLCOPENEDITORBOTTOMNOTEBOOK, ID_PLCOPENEDITORRIGHTNOTEBOOK, + ID_PLCOPENEDITORPROJECTTREE, ID_PLCOPENEDITORMAINSPLITTER, + ID_PLCOPENEDITORSECONDSPLITTER, ID_PLCOPENEDITORTHIRDSPLITTER, + ID_PLCOPENEDITORLIBRARYPANEL, ID_PLCOPENEDITORLIBRARYSEARCHCTRL, + ID_PLCOPENEDITORLIBRARYTREE, ID_PLCOPENEDITORLIBRARYCOMMENT, + ID_PLCOPENEDITORTABSOPENED, ID_PLCOPENEDITORTABSOPENED, + ID_PLCOPENEDITOREDITORMENUTOOLBAR, ID_PLCOPENEDITOREDITORTOOLBAR, + ID_PLCOPENEDITORPROJECTPANEL, ] = [wx.NewId() for _init_ctrls in range(17)] # Define PLCOpenEditor EditMenu extra items id -[ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, - ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, - ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, - ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, - ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE +[ + ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, ID_PLCOPENEDITOREDITMENUADDDATATYPE, + ID_PLCOPENEDITOREDITMENUADDFUNCTION, ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, + ID_PLCOPENEDITOREDITMENUADDPROGRAM, ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, + ID_PLCOPENEDITOREDITMENUFINDNEXT, ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, + ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, ID_PLCOPENEDITOREDITMENUADDRESOURCE ] = [wx.NewId() for _init_coll_EditMenu_Items in range(10)] # Define PLCOpenEditor DisplayMenu extra items id -[ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, - ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, +[ + ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, + ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE, ] = [wx.NewId() for _init_coll_DisplayMenu_Items in range(2)] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # EditorToolBar definitions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Define PLCOpenEditor Toolbar items id -[ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK, - ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE, - ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG, - ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT, - ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, - ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, - ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, - ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION, +[ + ID_PLCOPENEDITOREDITORTOOLBARSELECTION, ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, ID_PLCOPENEDITOREDITORTOOLBARBLOCK, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, ID_PLCOPENEDITOREDITORTOOLBARWIRE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, ID_PLCOPENEDITOREDITORTOOLBARRUNG, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, ID_PLCOPENEDITOREDITORTOOLBARCONTACT, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, + ID_PLCOPENEDITOREDITORTOOLBARJUMP, ID_PLCOPENEDITOREDITORTOOLBARMOTION, ] = [wx.NewId() for _init_coll_DefaultEditorToolBar_Items in range(18)] - # Define behaviour of each Toolbar item according to current POU body type # Informations meaning are in this order: # - Item is toggled @@ -94,102 +100,101 @@ # - Item icon filename # - Item tooltip text EditorToolBarItems = { - "FBD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", - "move", _("Move the view")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", - "add_comment", _("Create a new comment")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", - "add_variable", _("Create a new variable")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", - "add_block", _("Create a new block")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", - "add_connection", _("Create a new connection"))], - "LD" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", - "move", _("Move the view")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", - "add_comment", _("Create a new comment")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", - "add_powerrail", _("Create a new power rail")), - (False, DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", - "add_rung", _("Create a new rung")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", - "add_coil", _("Create a new coil")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", - "add_contact", _("Create a new contact")), - (False, DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", - "add_branch", _("Create a new branch")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", - "add_variable", _("Create a new variable")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", - "add_block", _("Create a new block")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", - "add_connection", _("Create a new connection"))], - "SFC" : [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", - "move", _("Move the view")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", - "add_comment", _("Create a new comment")), - (True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", - "add_initial_step", _("Create a new initial step")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", - "add_step", _("Create a new step")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", - "add_transition", _("Create a new transition")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", - "add_action", _("Create a new action block")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", - "add_divergence", _("Create a new divergence")), - (False, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", - "add_jump", _("Create a new jump")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", - "add_variable", _("Create a new variable")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", - "add_block", _("Create a new block")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", - "add_connection", _("Create a new connection")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", - "add_powerrail", _("Create a new power rail")), - (True, FREEDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", - "add_contact", _("Create a new contact"))], - "ST" : [], - "IL" : [], - "debug": [(True, FREEDRAWING_MODE|DRIVENDRAWING_MODE, - ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", - "move", _("Move the view"))], + "FBD": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection"))], + "LD": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", + "add_powerrail", _("Create a new power rail")), + (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARRUNG, "OnRungTool", + "add_rung", _("Create a new rung")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOIL, "OnCoilTool", + "add_coil", _("Create a new coil")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", + "add_contact", _("Create a new contact")), + (False, DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBRANCH, "OnBranchTool", + "add_branch", _("Create a new branch")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection"))], + "SFC": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCOMMENT, "OnCommentTool", + "add_comment", _("Create a new comment")), + (True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARINITIALSTEP, "OnInitialStepTool", + "add_initial_step", _("Create a new initial step")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARSTEP, "OnStepTool", + "add_step", _("Create a new step")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARTRANSITION, "OnTransitionTool", + "add_transition", _("Create a new transition")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARACTIONBLOCK, "OnActionBlockTool", + "add_action", _("Create a new action block")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARDIVERGENCE, "OnDivergenceTool", + "add_divergence", _("Create a new divergence")), + (False, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARJUMP, "OnJumpTool", + "add_jump", _("Create a new jump")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARVARIABLE, "OnVariableTool", + "add_variable", _("Create a new variable")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARBLOCK, "OnBlockTool", + "add_block", _("Create a new block")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONNECTION, "OnConnectionTool", + "add_connection", _("Create a new connection")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARPOWERRAIL, "OnPowerRailTool", + "add_powerrail", _("Create a new power rail")), + (True, FREEDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARCONTACT, "OnContactTool", + "add_contact", _("Create a new contact"))], + "ST": [], + "IL": [], + "debug": [(True, FREEDRAWING_MODE | DRIVENDRAWING_MODE, + ID_PLCOPENEDITOREDITORTOOLBARMOTION, "OnMotionTool", + "move", _("Move the view"))], } -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helper Functions -#------------------------------------------------------------------------------- - -import base64 +# ------------------------------------------------------------------------------- + def EncodeFileSystemPath(path, use_base64=True): path = path.encode(sys.getfilesystemencoding()) @@ -197,22 +202,29 @@ return base64.encodestring(path) return path + def DecodeFileSystemPath(path, is_base64=True): if is_base64: path = base64.decodestring(path) return unicode(path, sys.getfilesystemencoding()) -# Compatibility function for wx versions < 2.6 + def AppendMenu(parent, help, id, kind, text): + """ + Compatibility function for wx versions < 2.6 + """ if wx.VERSION >= (2, 6, 0): parent.Append(help=help, id=id, kind=kind, text=text) else: parent.Append(helpString=help, id=id, kind=kind, item=text) -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, - POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES + +[ + TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES ] = range(10) + def GetShortcutKeyCallbackFunction(viewer_function): def ShortcutKeyFunction(self, event): control = self.FindFocus() @@ -224,6 +236,7 @@ control.ProcessEvent(event) return ShortcutKeyFunction + def GetDeleteElementFunction(remove_function, parent_type=None, check_function=None): def DeleteElementFunction(self, selected): name = self.ProjectTree.GetItemText(selected) @@ -236,6 +249,7 @@ remove_function(self.Controler, name) return DeleteElementFunction + if wx.Platform == '__WXMSW__': TAB_BORDER = 6 NOTEBOOK_BORDER = 6 @@ -243,15 +257,16 @@ TAB_BORDER = 7 NOTEBOOK_BORDER = 2 + def SimplifyTabLayout(tabs, rect): for tab in tabs: if tab["pos"][0] == rect.x: others = [t for t in tabs if t != tab] - others.sort(lambda x,y: cmp(x["pos"][0], y["pos"][0])) + others.sort(lambda x, y: cmp(x["pos"][0], y["pos"][0])) for other in others: - if (other["pos"][1] == tab["pos"][1] and - other["size"][1] == tab["size"][1] and - other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER): + if other["pos"][1] == tab["pos"][1] and \ + other["size"][1] == tab["size"][1] and \ + other["pos"][0] == tab["pos"][0] + tab["size"][0] + TAB_BORDER: tab["size"] = (tab["size"][0] + other["size"][0] + TAB_BORDER, tab["size"][1]) tab["pages"].extend(other["pages"]) @@ -262,11 +277,11 @@ elif tab["pos"][1] == rect.y: others = [t for t in tabs if t != tab] - others.sort(lambda x,y: cmp(x["pos"][1], y["pos"][1])) + others.sort(lambda x, y: cmp(x["pos"][1], y["pos"][1])) for other in others: - if (other["pos"][0] == tab["pos"][0] and - other["size"][0] == tab["size"][0] and - other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER): + if other["pos"][0] == tab["pos"][0] and \ + other["size"][0] == tab["size"][0] and \ + other["pos"][1] == tab["pos"][1] + tab["size"][1] + TAB_BORDER: tab["size"] = (tab["size"][0], tab["size"][1] + other["size"][1] + TAB_BORDER) tab["pages"].extend(other["pages"]) @@ -276,15 +291,17 @@ return True return False + def ComputeTabsLayout(tabs, rect): if len(tabs) == 0: return tabs if len(tabs) == 1: return tabs[0] split = None + split_id = None for idx, tab in enumerate(tabs): if len(tab["pages"]) == 0: - raise ValueError, "Not possible" + raise ValueError("Not possible") if tab["size"][0] == rect.width: if tab["pos"][1] == rect.y: split = (wx.TOP, float(tab["size"][1]) / float(rect.height)) @@ -294,6 +311,7 @@ split = (wx.BOTTOM, 1.0 - float(tab["size"][1]) / float(rect.height)) split_rect = wx.Rect(rect.x, rect.y, rect.width, rect.height - tab["size"][1] - TAB_BORDER) + split_id = idx break elif tab["size"][1] == rect.height: if tab["pos"][0] == rect.x: @@ -304,9 +322,10 @@ split = (wx.RIGHT, 1.0 - float(tab["size"][0]) / float(rect.width)) split_rect = wx.Rect(rect.x, rect.y, rect.width - tab["size"][0] - TAB_BORDER, rect.height) + split_id = id break - if split != None: - split_tab = tabs.pop(idx) + if split is not None: + split_tab = tabs.pop(split_id) return {"split": split, "tab": split_tab, "others": ComputeTabsLayout(tabs, split_rect)} @@ -315,17 +334,15 @@ return ComputeTabsLayout(tabs, rect) return tabs -#------------------------------------------------------------------------------- -# IDEFrame Base Class -#------------------------------------------------------------------------------- - -UNEDITABLE_NAMES_DICT = dict([(_(name), name) for name in UNEDITABLE_NAMES]) + +UNEDITABLE_NAMES_DICT = dict([(_(n), n) for n in UNEDITABLE_NAMES]) + class IDEFrame(wx.Frame): - + """IDEFrame Base Class""" # Compatibility function for wx versions < 2.6 if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -342,79 +359,76 @@ def _init_coll_AddMenu_Items(self, parent, add_config=True): AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDDATATYPE, - kind=wx.ITEM_NORMAL, text=_(u'&Data Type')) + kind=wx.ITEM_NORMAL, text=_(u'&Data Type')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTION, - kind=wx.ITEM_NORMAL, text=_(u'&Function')) + kind=wx.ITEM_NORMAL, text=_(u'&Function')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK, - kind=wx.ITEM_NORMAL, text=_(u'Function &Block')) + kind=wx.ITEM_NORMAL, text=_(u'Function &Block')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDPROGRAM, - kind=wx.ITEM_NORMAL, text=_(u'&Program')) + kind=wx.ITEM_NORMAL, text=_(u'&Program')) AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDRESOURCE, - kind=wx.ITEM_NORMAL, text=_(u'&Resource')) + kind=wx.ITEM_NORMAL, text=_(u'&Resource')) if add_config: AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION, - kind=wx.ITEM_NORMAL, text=_(u'&Configuration')) + kind=wx.ITEM_NORMAL, text=_(u'&Configuration')) def _init_coll_EditMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_UNDO, - kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z') + kind=wx.ITEM_NORMAL, text=_(u'Undo') + '\tCTRL+Z') AppendMenu(parent, help='', id=wx.ID_REDO, - kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y') - #AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, - # kind=wx.ITEM_CHECK, text=_(u'Enable Undo/Redo')) - enable_undo_redo = _(u'Enable Undo/Redo') # Keeping text in translations for possible menu reactivation + kind=wx.ITEM_NORMAL, text=_(u'Redo') + '\tCTRL+Y') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_CUT, - kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X') + kind=wx.ITEM_NORMAL, text=_(u'Cut') + '\tCTRL+X') AppendMenu(parent, help='', id=wx.ID_COPY, - kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C') + kind=wx.ITEM_NORMAL, text=_(u'Copy') + '\tCTRL+C') AppendMenu(parent, help='', id=wx.ID_PASTE, - kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V') + kind=wx.ITEM_NORMAL, text=_(u'Paste') + '\tCTRL+V') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_FIND, - kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F') + kind=wx.ITEM_NORMAL, text=_(u'Find') + '\tCTRL+F') AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDNEXT, - kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K') + kind=wx.ITEM_NORMAL, text=_(u'Find Next') + '\tCTRL+K') AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, - kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K') + kind=wx.ITEM_NORMAL, text=_(u'Find Previous') + '\tCTRL+SHIFT+K') parent.AppendSeparator() AppendMenu(parent, help='', id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, - kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F') + kind=wx.ITEM_NORMAL, text=_(u'Search in Project') + '\tCTRL+SHIFT+F') parent.AppendSeparator() add_menu = wx.Menu(title='') self._init_coll_AddMenu_Items(add_menu) parent.AppendMenu(wx.ID_ADD, _(u"&Add Element"), add_menu) AppendMenu(parent, help='', id=wx.ID_SELECTALL, - kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A') + kind=wx.ITEM_NORMAL, text=_(u'Select All') + '\tCTRL+A') AppendMenu(parent, help='', id=wx.ID_DELETE, - kind=wx.ITEM_NORMAL, text=_(u'&Delete')) + kind=wx.ITEM_NORMAL, text=_(u'&Delete')) self.Bind(wx.EVT_MENU, self.OnUndoMenu, id=wx.ID_UNDO) self.Bind(wx.EVT_MENU, self.OnRedoMenu, id=wx.ID_REDO) - #self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO) + # self.Bind(wx.EVT_MENU, self.OnEnableUndoRedoMenu, id=ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO) self.Bind(wx.EVT_MENU, self.OnCutMenu, id=wx.ID_CUT) self.Bind(wx.EVT_MENU, self.OnCopyMenu, id=wx.ID_COPY) self.Bind(wx.EVT_MENU, self.OnPasteMenu, id=wx.ID_PASTE) self.Bind(wx.EVT_MENU, self.OnFindMenu, id=wx.ID_FIND) self.Bind(wx.EVT_MENU, self.OnFindNextMenu, - id=ID_PLCOPENEDITOREDITMENUFINDNEXT) + id=ID_PLCOPENEDITOREDITMENUFINDNEXT) self.Bind(wx.EVT_MENU, self.OnFindPreviousMenu, - id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS) + id=ID_PLCOPENEDITOREDITMENUFINDPREVIOUS) self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, - id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) self.Bind(wx.EVT_MENU, self.OnSearchInProjectMenu, - id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) + id=ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT) self.Bind(wx.EVT_MENU, self.OnAddDataTypeMenu, - id=ID_PLCOPENEDITOREDITMENUADDDATATYPE) + id=ID_PLCOPENEDITOREDITMENUADDDATATYPE) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("function"), - id=ID_PLCOPENEDITOREDITMENUADDFUNCTION) + id=ID_PLCOPENEDITOREDITMENUADDFUNCTION) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("functionBlock"), - id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK) + id=ID_PLCOPENEDITOREDITMENUADDFUNCTIONBLOCK) self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction("program"), - id=ID_PLCOPENEDITOREDITMENUADDPROGRAM) + id=ID_PLCOPENEDITOREDITMENUADDPROGRAM) self.Bind(wx.EVT_MENU, self.AddResourceMenu, - id=ID_PLCOPENEDITOREDITMENUADDRESOURCE) + id=ID_PLCOPENEDITOREDITMENUADDRESOURCE) self.Bind(wx.EVT_MENU, self.OnAddConfigurationMenu, - id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION) + id=ID_PLCOPENEDITOREDITMENUADDCONFIGURATION) self.Bind(wx.EVT_MENU, self.OnSelectAllMenu, id=wx.ID_SELECTALL) self.Bind(wx.EVT_MENU, self.OnDeleteMenu, id=wx.ID_DELETE) @@ -429,17 +443,17 @@ def _init_coll_DisplayMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_REFRESH, - kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R') + kind=wx.ITEM_NORMAL, text=_(u'Refresh') + '\tCTRL+R') if self.EnableDebug: AppendMenu(parent, help='', id=wx.ID_CLEAR, - kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K') + kind=wx.ITEM_NORMAL, text=_(u'Clear Errors') + '\tCTRL+K') parent.AppendSeparator() zoommenu = wx.Menu(title='') parent.AppendMenu(wx.ID_ZOOM_FIT, _("Zoom"), zoommenu) for idx, value in enumerate(ZOOM_FACTORS): new_id = wx.NewId() AppendMenu(zoommenu, help='', id=new_id, - kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%") + kind=wx.ITEM_RADIO, text=str(int(round(value * 100))) + "%") self.Bind(wx.EVT_MENU, self.GenerateZoomFunction(idx), id=new_id) parent.AppendSeparator() @@ -448,7 +462,7 @@ self.Bind(wx.EVT_MENU, self.SwitchFullScrMode, id=ID_PLCOPENEDITORDISPLAYMENUSWITCHPERSPECTIVE) AppendMenu(parent, help='', id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE, - kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective')) + kind=wx.ITEM_NORMAL, text=_(u'Reset Perspective')) self.Bind(wx.EVT_MENU, self.OnResetPerspective, id=ID_PLCOPENEDITORDISPLAYMENURESETPERSPECTIVE) self.Bind(wx.EVT_MENU, self.OnRefreshMenu, id=wx.ID_REFRESH) @@ -474,14 +488,11 @@ def _init_icon(self, parent): if self.icon: - self.SetIcon(self.icon) + self.SetIcon(self.icon) elif parent and parent.icon: - self.SetIcon(parent.icon) - + self.SetIcon(parent.icon) + def _init_ctrls(self, prnt): - wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame', - parent=prnt, pos=wx.DefaultPosition, size=wx.Size(1000, 600), - style=wx.DEFAULT_FRAME_STYLE) self._init_icon(prnt) self.SetClientSize(wx.Size(1000, 600)) self.Bind(wx.EVT_ACTIVATE, self.OnActivated) @@ -489,95 +500,113 @@ self.TabsImageList = wx.ImageList(31, 16) self.TabsImageListIndexes = {} - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Creating main structure - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.AUIManager = wx.aui.AuiManager(self) self.AUIManager.SetDockSizeConstraint(0.5, 0.5) self.Panes = {} - self.LeftNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORLEFTNOTEBOOK, - style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE| - wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE) + self.LeftNoteBook = wx.aui.AuiNotebook( + self, ID_PLCOPENEDITORLEFTNOTEBOOK, + style=(wx.aui.AUI_NB_TOP | + wx.aui.AUI_NB_TAB_SPLIT | + wx.aui.AUI_NB_TAB_MOVE | + wx.aui.AUI_NB_SCROLL_BUTTONS | + wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)) self.LeftNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, - self.OnAllowNotebookDnD) - self.AUIManager.AddPane(self.LeftNoteBook, - wx.aui.AuiPaneInfo().Name("ProjectPane"). - Left().Layer(1). - BestSize(wx.Size(300, 500)).CloseButton(False)) - - self.BottomNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORBOTTOMNOTEBOOK, - style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE| - wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE) + self.OnAllowNotebookDnD) + self.AUIManager.AddPane( + self.LeftNoteBook, + wx.aui.AuiPaneInfo().Name("ProjectPane").Left().Layer(1). + BestSize(wx.Size(300, 500)).CloseButton(False)) + + self.BottomNoteBook = wx.aui.AuiNotebook( + self, ID_PLCOPENEDITORBOTTOMNOTEBOOK, + style=(wx.aui.AUI_NB_TOP | + wx.aui.AUI_NB_TAB_SPLIT | + wx.aui.AUI_NB_TAB_MOVE | + wx.aui.AUI_NB_SCROLL_BUTTONS | + wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)) self.BottomNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, - self.OnAllowNotebookDnD) - self.AUIManager.AddPane(self.BottomNoteBook, - wx.aui.AuiPaneInfo().Name("ResultPane"). - Bottom().Layer(0). - BestSize(wx.Size(800, 300)).CloseButton(False)) - - self.RightNoteBook = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORRIGHTNOTEBOOK, - style=wx.aui.AUI_NB_TOP|wx.aui.AUI_NB_TAB_SPLIT|wx.aui.AUI_NB_TAB_MOVE| - wx.aui.AUI_NB_SCROLL_BUTTONS|wx.aui.AUI_NB_TAB_EXTERNAL_MOVE) + self.OnAllowNotebookDnD) + self.AUIManager.AddPane( + self.BottomNoteBook, + wx.aui.AuiPaneInfo().Name("ResultPane").Bottom().Layer(0). + BestSize(wx.Size(800, 300)).CloseButton(False)) + + self.RightNoteBook = wx.aui.AuiNotebook( + self, ID_PLCOPENEDITORRIGHTNOTEBOOK, + style=(wx.aui.AUI_NB_TOP | + wx.aui.AUI_NB_TAB_SPLIT | + wx.aui.AUI_NB_TAB_MOVE | + wx.aui.AUI_NB_SCROLL_BUTTONS | + wx.aui.AUI_NB_TAB_EXTERNAL_MOVE)) self.RightNoteBook.Bind(wx.aui.EVT_AUINOTEBOOK_ALLOW_DND, - self.OnAllowNotebookDnD) - self.AUIManager.AddPane(self.RightNoteBook, - wx.aui.AuiPaneInfo().Name("LibraryPane"). - Right().Layer(0). - BestSize(wx.Size(250, 400)).CloseButton(False)) - - self.TabsOpened = wx.aui.AuiNotebook(self, ID_PLCOPENEDITORTABSOPENED, - style=wx.aui.AUI_NB_DEFAULT_STYLE|wx.aui.AUI_NB_WINDOWLIST_BUTTON) + self.OnAllowNotebookDnD) + self.AUIManager.AddPane( + self.RightNoteBook, + wx.aui.AuiPaneInfo().Name("LibraryPane").Right().Layer(0). + BestSize(wx.Size(250, 400)).CloseButton(False)) + + self.TabsOpened = wx.aui.AuiNotebook( + self, ID_PLCOPENEDITORTABSOPENED, + style=(wx.aui.AUI_NB_DEFAULT_STYLE | + wx.aui.AUI_NB_WINDOWLIST_BUTTON)) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGING, - self.OnPouSelectedChanging) + self.OnPouSelectedChanging) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CHANGED, - self.OnPouSelectedChanged) + self.OnPouSelectedChanged) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_PAGE_CLOSE, - self.OnPageClose) + self.OnPageClose) self.TabsOpened.Bind(wx.aui.EVT_AUINOTEBOOK_END_DRAG, - self.OnPageDragged) + self.OnPageDragged) self.AUIManager.AddPane(self.TabsOpened, - wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane")) - - #----------------------------------------------------------------------- + wx.aui.AuiPaneInfo().CentrePane().Name("TabsPane")) + + # ----------------------------------------------------------------------- # Creating PLCopen Project Types Tree - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.MainTabs = {} - self.ProjectPanel = wx.SplitterWindow(id=ID_PLCOPENEDITORPROJECTPANEL, - name='ProjectPanel', parent=self.LeftNoteBook, point=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.SP_3D) + self.ProjectPanel = wx.SplitterWindow( + id=ID_PLCOPENEDITORPROJECTPANEL, + name='ProjectPanel', parent=self.LeftNoteBook, point=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.SP_3D) self.ProjectTree = CustomTree(id=ID_PLCOPENEDITORPROJECTTREE, - name='ProjectTree', parent=self.ProjectPanel, - pos=wx.Point(0, 0), size=wx.Size(0, 0), - style=wx.SUNKEN_BORDER, - agwStyle=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.TR_EDIT_LABELS) + name='ProjectTree', + parent=self.ProjectPanel, + pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.SUNKEN_BORDER, + agwStyle=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.TR_EDIT_LABELS)) self.ProjectTree.SetBackgroundBitmap(GetBitmap("custom_tree_background"), - wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM) + wx.ALIGN_RIGHT | wx.ALIGN_BOTTOM) add_menu = wx.Menu() self._init_coll_AddMenu_Items(add_menu) self.ProjectTree.SetAddMenu(add_menu) self.Bind(wx.EVT_TREE_ITEM_RIGHT_CLICK, self.OnProjectTreeRightUp, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.ProjectTree.Bind(wx.EVT_LEFT_UP, self.OnProjectTreeLeftUp) self.Bind(wx.EVT_TREE_SEL_CHANGING, self.OnProjectTreeItemChanging, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnProjectTreeBeginDrag, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnProjectTreeItemBeginEdit, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnProjectTreeItemEndEdit, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnProjectTreeItemActivated, - id=ID_PLCOPENEDITORPROJECTTREE) + id=ID_PLCOPENEDITORPROJECTTREE) self.ProjectTree.Bind(wx.EVT_MOTION, self.OnProjectTreeMotion) - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Creating PLCopen Project POU Instance Variables Panel - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.PouInstanceVariablesPanel = PouInstanceVariablesPanel(self.ProjectPanel, self, self.Controler, self.EnableDebug) @@ -586,46 +615,50 @@ self.ProjectPanel.SplitHorizontally(self.ProjectTree, self.PouInstanceVariablesPanel, 300) - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Creating Tool Bar - #----------------------------------------------------------------------- - - MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + # ----------------------------------------------------------------------- + + MenuToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORMENUTOOLBAR, + wx.DefaultPosition, wx.DefaultSize, + wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) MenuToolBar.SetToolBitmapSize(wx.Size(25, 25)) MenuToolBar.Realize() self.Panes["MenuToolBar"] = MenuToolBar self.AUIManager.AddPane(MenuToolBar, wx.aui.AuiPaneInfo(). - Name("MenuToolBar").Caption(_("Menu ToolBar")). - ToolbarPane().Top(). - LeftDockable(False).RightDockable(False)) - - EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, wx.DefaultPosition, wx.DefaultSize, - wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) + Name("MenuToolBar").Caption(_("Menu ToolBar")). + ToolbarPane().Top(). + LeftDockable(False).RightDockable(False)) + + EditorToolBar = wx.ToolBar(self, ID_PLCOPENEDITOREDITORTOOLBAR, + wx.DefaultPosition, wx.DefaultSize, + wx.TB_FLAT | wx.TB_NODIVIDER | wx.NO_BORDER) EditorToolBar.SetToolBitmapSize(wx.Size(25, 25)) EditorToolBar.AddRadioTool(ID_PLCOPENEDITOREDITORTOOLBARSELECTION, - GetBitmap("select"), wx.NullBitmap, _("Select an object")) + GetBitmap("select"), + wx.NullBitmap, + _("Select an object")) EditorToolBar.Realize() self.Panes["EditorToolBar"] = EditorToolBar self.AUIManager.AddPane(EditorToolBar, wx.aui.AuiPaneInfo(). - Name("EditorToolBar").Caption(_("Editor ToolBar")). - ToolbarPane().Top().Position(1). - LeftDockable(False).RightDockable(False)) + Name("EditorToolBar").Caption(_("Editor ToolBar")). + ToolbarPane().Top().Position(1). + LeftDockable(False).RightDockable(False)) self.Bind(wx.EVT_MENU, self.OnSelectionTool, - id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION) - - #----------------------------------------------------------------------- + id=ID_PLCOPENEDITOREDITORTOOLBARSELECTION) + + # ----------------------------------------------------------------------- # Creating Search Panel - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.SearchResultPanel = SearchResultPanel(self.BottomNoteBook, self) self.MainTabs["SearchResultPanel"] = (self.SearchResultPanel, _("Search")) self.BottomNoteBook.AddPage(*self.MainTabs["SearchResultPanel"]) - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- # Creating Library Panel - #----------------------------------------------------------------------- + # ----------------------------------------------------------------------- self.LibraryPanel = LibraryPanel(self, True) self.MainTabs["LibraryPanel"] = (self.LibraryPanel, _("Library")) @@ -641,15 +674,12 @@ self.AUIManager.Update() - self.FindDialog = FindInPouDialog(self) - self.FindDialog.Hide() - - ## Constructor of the PLCOpenEditor class. - # @param parent The parent window. - # @param controler The controler been used by PLCOpenEditor (default: None). - # @param fileOpen The filepath to open if no controler defined (default: None). - # @param debug The filepath to open if no controler defined (default: False). - def __init__(self, parent, enable_debug = False): + def __init__(self, parent, enable_debug=False): + wx.Frame.__init__(self, id=ID_PLCOPENEDITOR, name='IDEFrame', + parent=parent, pos=wx.DefaultPosition, + size=wx.Size(1000, 600), + style=wx.DEFAULT_FRAME_STYLE) + self.Controler = None self.Config = wx.ConfigBase.Get() self.EnableDebug = enable_debug @@ -666,32 +696,32 @@ # Icons for other items for imgname, itemtype in [ - #editables - ("PROJECT", ITEM_PROJECT), - #("POU", ITEM_POU), - #("VARIABLE", ITEM_VARIABLE), - ("TRANSITION", ITEM_TRANSITION), - ("ACTION", ITEM_ACTION), - ("CONFIGURATION", ITEM_CONFIGURATION), - ("RESOURCE", ITEM_RESOURCE), - ("DATATYPE", ITEM_DATATYPE), - # uneditables - ("DATATYPES", ITEM_DATATYPES), - ("FUNCTION", ITEM_FUNCTION), - ("FUNCTIONBLOCK", ITEM_FUNCTIONBLOCK), - ("PROGRAM", ITEM_PROGRAM), - ("VAR_LOCAL", ITEM_VAR_LOCAL), - ("VAR_LOCAL", ITEM_VAR_GLOBAL), - ("VAR_LOCAL", ITEM_VAR_EXTERNAL), - ("VAR_LOCAL", ITEM_VAR_TEMP), - ("VAR_INPUT", ITEM_VAR_INPUT), - ("VAR_OUTPUT", ITEM_VAR_OUTPUT), - ("VAR_INOUT", ITEM_VAR_INOUT), - ("TRANSITIONS", ITEM_TRANSITIONS), - ("ACTIONS", ITEM_ACTIONS), - ("CONFIGURATIONS", ITEM_CONFIGURATIONS), - ("RESOURCES", ITEM_RESOURCES), - ("PROPERTIES", ITEM_PROPERTIES)]: + # editables + ("PROJECT", ITEM_PROJECT), + # ("POU", ITEM_POU), + # ("VARIABLE", ITEM_VARIABLE), + ("TRANSITION", ITEM_TRANSITION), + ("ACTION", ITEM_ACTION), + ("CONFIGURATION", ITEM_CONFIGURATION), + ("RESOURCE", ITEM_RESOURCE), + ("DATATYPE", ITEM_DATATYPE), + # uneditables + ("DATATYPES", ITEM_DATATYPES), + ("FUNCTION", ITEM_FUNCTION), + ("FUNCTIONBLOCK", ITEM_FUNCTIONBLOCK), + ("PROGRAM", ITEM_PROGRAM), + ("VAR_LOCAL", ITEM_VAR_LOCAL), + ("VAR_LOCAL", ITEM_VAR_GLOBAL), + ("VAR_LOCAL", ITEM_VAR_EXTERNAL), + ("VAR_LOCAL", ITEM_VAR_TEMP), + ("VAR_INPUT", ITEM_VAR_INPUT), + ("VAR_OUTPUT", ITEM_VAR_OUTPUT), + ("VAR_INOUT", ITEM_VAR_INOUT), + ("TRANSITIONS", ITEM_TRANSITIONS), + ("ACTIONS", ITEM_ACTIONS), + ("CONFIGURATIONS", ITEM_CONFIGURATIONS), + ("RESOURCES", ITEM_RESOURCES), + ("PROPERTIES", ITEM_PROPERTIES)]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) # Assign icon list to TreeCtrls @@ -705,7 +735,7 @@ self.SearchParams = None self.Highlights = {} self.DrawingMode = FREEDRAWING_MODE - #self.DrawingMode = DRIVENDRAWING_MODE + # self.DrawingMode = DRIVENDRAWING_MODE self.AuiTabCtrl = [] # Save default perspective @@ -719,7 +749,6 @@ "notebooks": notebooks, } - # Initialize Printing configuring elements self.PrintData = wx.PrintData() self.PrintData.SetPaperId(wx.PAPER_A4) @@ -731,9 +760,15 @@ self.SetRefreshFunctions() self.SetDeleteFunctions() + wx.CallAfter(self.InitFindDialog) + def __del__(self): self.FindDialog.Destroy() + def InitFindDialog(self): + self.FindDialog = FindInPouDialog(self) + self.FindDialog.Hide() + def Show(self): wx.Frame.Show(self) wx.CallAfter(self.RestoreLastState) @@ -750,12 +785,12 @@ notebook.SetSelection(idx) return -#------------------------------------------------------------------------------- -# Saving and restoring frame organization functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Saving and restoring frame organization functions + # ------------------------------------------------------------------------------- def GetTabInfos(self, tab): - for page_name, (page_ref, page_title) in self.MainTabs.iteritems(): + for page_name, (page_ref, _page_title) in self.MainTabs.iteritems(): if page_ref == tab: return ("main", page_name) return None @@ -770,7 +805,7 @@ tab_size = child.GetSize() for page_idx in xrange(child.GetPageCount()): page = child.GetWindowFromIdx(page_idx) - if not tab.has_key("size"): + if "size" not in tab: tab["size"] = (tab_size[0], tab_size[1] + page.GetSize()[1]) tab_infos = self.GetTabInfos(page) if tab_infos is not None: @@ -804,12 +839,12 @@ if isinstance(tabs, ListType): if len(tabs) == 0: return - raise ValueError, "Not supported" - - if tabs.has_key("split"): + raise ValueError("Not supported") + + if "split" in tabs: self.LoadTabLayout(notebook, tabs["others"]) - split_dir, split_ratio = tabs["split"] + split_dir, _split_ratio = tabs["split"] first_index = self.LoadTabLayout(notebook, tabs["tab"], mode="first") notebook.Split(first_index, split_dir) self.LoadTabLayout(notebook, tabs["tab"], mode="others", first_index=first_index) @@ -834,7 +869,7 @@ self.AUIManager.LoadPerspective(self.DefaultPerspective["perspective"]) for notebook in [self.LeftNoteBook, self.BottomNoteBook, self.RightNoteBook]: - for idx in xrange(notebook.GetPageCount()): + for dummy in xrange(notebook.GetPageCount()): notebook.RemovePage(0) notebooks = self.DefaultPerspective["notebooks"] @@ -863,26 +898,28 @@ self.Config.Flush() -#------------------------------------------------------------------------------- -# General Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # General Functions + # ------------------------------------------------------------------------------- def SetRefreshFunctions(self): self.RefreshFunctions = { - TITLE : self.RefreshTitle, - EDITORTOOLBAR : self.RefreshEditorToolBar, - FILEMENU : self.RefreshFileMenu, - EDITMENU : self.RefreshEditMenu, - DISPLAYMENU : self.RefreshDisplayMenu, - PROJECTTREE : self.RefreshProjectTree, - POUINSTANCEVARIABLESPANEL : self.RefreshPouInstanceVariablesPanel, - LIBRARYTREE : self.RefreshLibraryPanel, - SCALING : self.RefreshScaling, + TITLE: self.RefreshTitle, + EDITORTOOLBAR: self.RefreshEditorToolBar, + FILEMENU: self.RefreshFileMenu, + EDITMENU: self.RefreshEditMenu, + DISPLAYMENU: self.RefreshDisplayMenu, + PROJECTTREE: self.RefreshProjectTree, + POUINSTANCEVARIABLESPANEL: self.RefreshPouInstanceVariablesPanel, + LIBRARYTREE: self.RefreshLibraryPanel, + SCALING: self.RefreshScaling, PAGETITLES: self.RefreshPageTitles} - ## Call PLCOpenEditor refresh functions. - # @param elements List of elements to refresh. def _Refresh(self, *elements): + """Call Editor refresh functions. + + :param elements: List of elements to refresh. + """ try: for element in elements: self.RefreshFunctions[element]() @@ -890,9 +927,11 @@ # ignore exceptions caused by refresh while quitting pass - ## Callback function when AUINotebook Page closed with CloseButton - # @param event AUINotebook Event. def OnPageClose(self, event): + """Callback function when AUINotebook Page closed with CloseButton + + :param event: AUINotebook Event. + """ selected = self.TabsOpened.GetSelection() if selected > -1: window = self.TabsOpened.GetPage(selected) @@ -907,7 +946,6 @@ else: event.Veto() - def GetCopyBuffer(self, primary_selection=False): data = None if primary_selection and wx.Platform == '__WXMSW__': @@ -955,22 +993,20 @@ PROJECTTREE, POUINSTANCEVARIABLESPANEL, SCALING) dialog.Destroy() -#------------------------------------------------------------------------------- -# Notebook Unified Functions -#------------------------------------------------------------------------------- - - ## Function that add a tab in Notebook, calling refresh for tab DClick event - # for wx.aui.AUINotebook. - # @param window Panel to display in tab. - # @param text title for the tab ctrl. + # ------------------------------------------------------------------------------- + # Notebook Unified Functions + # ------------------------------------------------------------------------------- + def AddPage(self, window, text): + """Function that add a tab in Notebook, calling refresh for tab DClick event + for wx.aui.AUINotebook. + + :param window: Panel to display in tab. + :param text: title for the tab ctrl. + """ self.TabsOpened.AddPage(window, text) self.RefreshTabCtrlEvent() - ## Function that add a tab in Notebook, calling refresh for tab DClick event - # for wx.aui.AUINotebook. - # @param window Panel to display in tab. - # @param text title for the tab ctrl. def DeletePage(self, window): for idx in xrange(self.TabsOpened.GetPageCount()): if self.TabsOpened.GetPage(idx) == window: @@ -978,37 +1014,44 @@ self.RefreshTabCtrlEvent() return - ## Function that fix difference in deleting all tabs between - # wx.Notebook and wx.aui.AUINotebook. def DeleteAllPages(self): - for idx in xrange(self.TabsOpened.GetPageCount()): + """Function that fix difference in deleting all tabs between + wx.Notebook and wx.aui.AUINotebook. + """ + for dummy in xrange(self.TabsOpened.GetPageCount()): self.TabsOpened.DeletePage(0) self.RefreshTabCtrlEvent() - ## Function that fix difference in setting picture on tab between - # wx.Notebook and wx.aui.AUINotebook. - # @param idx Tab index. - # @param bitmap wx.Bitmap to define on tab. - # @return True if operation succeeded def SetPageBitmap(self, idx, bitmap): + """Function that fix difference in setting picture on tab between + wx.Notebook and wx.aui.AUINotebook. + + :param idx: Tab index. + :param bitmap: wx.Bitmap to define on tab. + :returns: True if operation succeeded + """ return self.TabsOpened.SetPageBitmap(idx, bitmap) -#------------------------------------------------------------------------------- -# Dialog Message Functions -#------------------------------------------------------------------------------- - - ## Function displaying an Error dialog in PLCOpenEditor. - # @param message The message to display. + # ------------------------------------------------------------------------------- + # Dialog Message Functions + # ------------------------------------------------------------------------------- + def ShowErrorMessage(self, message): - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + """Function displaying an Error dialog in editor. + + :param message: The message to display. + """ + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() - ## Function displaying an Error dialog in PLCOpenEditor. - # @return False if closing cancelled. def CheckSaveBeforeClosing(self, title=_("Close Project")): + """Function displaying an question dialog if project is not saved" + + :returns: False if closing cancelled. + """ if not self.Controler.ProjectIsSaved(): - dialog = wx.MessageDialog(self, _("There are changes, do you want to save?"), title, wx.YES_NO|wx.CANCEL|wx.ICON_QUESTION) + dialog = wx.MessageDialog(self, _("There are changes, do you want to save?"), title, wx.YES_NO | wx.CANCEL | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() if answer == wx.ID_YES: @@ -1023,9 +1066,9 @@ return True -#------------------------------------------------------------------------------- -# File Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # File Menu Functions + # ------------------------------------------------------------------------------- def RefreshFileMenu(self): pass @@ -1072,7 +1115,7 @@ preview = wx.PrintPreview(printout, printout2, data) if preview.Ok(): - preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE|wx.FRAME_FLOAT_ON_PARENT) + preview_frame = wx.PreviewFrame(preview, self, _("Print preview"), style=wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT) preview_frame.Initialize() @@ -1104,9 +1147,9 @@ def OnQuitMenu(self, event): self.Close() -#------------------------------------------------------------------------------- -# Edit Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Edit Menu Functions + # ------------------------------------------------------------------------------- def RefreshEditMenu(self): MenuToolBar = self.Panes["MenuToolBar"] @@ -1121,14 +1164,14 @@ MenuToolBar.EnableTool(wx.ID_UNDO, undo) self.EditMenu.Enable(wx.ID_REDO, redo) MenuToolBar.EnableTool(wx.ID_REDO, redo) - #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True) - #self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, + # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, True) + # self.EditMenu.Check(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, # self.Controler.IsProjectBufferEnabled()) self.EditMenu.Enable(wx.ID_FIND, selected > -1) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDNEXT, - selected > -1 and self.SearchParams is not None) + selected > -1 and self.SearchParams is not None) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUFINDPREVIOUS, - selected > -1 and self.SearchParams is not None) + selected > -1 and self.SearchParams is not None) self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) MenuToolBar.EnableTool(ID_PLCOPENEDITOREDITMENUSEARCHINPROJECT, True) self.EditMenu.Enable(wx.ID_ADD, True) @@ -1158,7 +1201,7 @@ MenuToolBar.EnableTool(wx.ID_UNDO, False) self.EditMenu.Enable(wx.ID_REDO, False) MenuToolBar.EnableTool(wx.ID_REDO, False) - #self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False) + # self.EditMenu.Enable(ID_PLCOPENEDITOREDITMENUENABLEUNDOREDO, False) self.EditMenu.Enable(wx.ID_CUT, False) MenuToolBar.EnableTool(wx.ID_CUT, False) self.EditMenu.Enable(wx.ID_COPY, False) @@ -1226,19 +1269,19 @@ def SetDeleteFunctions(self): self.DeleteFunctions = { ITEM_DATATYPE: GetDeleteElementFunction( - PLCControler.ProjectRemoveDataType, - check_function=self.CheckDataTypeIsUsedBeforeDeletion), + PLCControler.ProjectRemoveDataType, + check_function=self.CheckDataTypeIsUsedBeforeDeletion), ITEM_POU: GetDeleteElementFunction( - PLCControler.ProjectRemovePou, - check_function=self.CheckPouIsUsedBeforeDeletion), + PLCControler.ProjectRemovePou, + check_function=self.CheckPouIsUsedBeforeDeletion), ITEM_TRANSITION: GetDeleteElementFunction( - PLCControler.ProjectRemovePouTransition, ITEM_POU), + PLCControler.ProjectRemovePouTransition, ITEM_POU), ITEM_ACTION: GetDeleteElementFunction( - PLCControler.ProjectRemovePouAction, ITEM_POU), + PLCControler.ProjectRemovePouAction, ITEM_POU), ITEM_CONFIGURATION: GetDeleteElementFunction( - PLCControler.ProjectRemoveConfiguration), + PLCControler.ProjectRemoveConfiguration), ITEM_RESOURCE: GetDeleteElementFunction( - PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION) + PLCControler.ProjectRemoveConfigurationResource, ITEM_CONFIGURATION) } def OnDeleteMenu(self, event): @@ -1291,9 +1334,9 @@ self.SearchResultPanel.SetSearchResults(criteria, result) self.SelectTab(self.SearchResultPanel) -#------------------------------------------------------------------------------- -# Display Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Display Menu Functions + # ------------------------------------------------------------------------------- def RefreshDisplayMenu(self): if self.Controler is not None: @@ -1342,9 +1385,9 @@ def OnResetPerspective(self, event): self.ResetPerspective() -#------------------------------------------------------------------------------- -# Project Editor Panels Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project Editor Panels Management Functions + # ------------------------------------------------------------------------------- def OnPageDragged(self, event): wx.CallAfter(self.RefreshTabCtrlEvent) @@ -1449,7 +1492,7 @@ event.Skip() return OnTabsOpenedDClick - def SwitchFullScrMode(self,evt): + def SwitchFullScrMode(self, evt): pane = self.AUIManager.GetPane(self.TabsOpened) if pane.IsMaximized(): self.AUIManager.RestorePane(pane) @@ -1457,9 +1500,9 @@ self.AUIManager.MaximizePane(pane) self.AUIManager.Update() -#------------------------------------------------------------------------------- -# Types Tree Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Types Tree Management Functions + # ------------------------------------------------------------------------------- def RefreshProjectTree(self): # Extract current selected item tagname @@ -1471,12 +1514,13 @@ tagname = None # Refresh treectrl items according to project infos - infos = self.Controler.GetProjectInfos() - root = self.ProjectTree.GetRootItem() - if root is None or not root.IsOk(): - root = self.ProjectTree.AddRoot(infos["name"]) - self.GenerateProjectTreeBranch(root, infos) - self.ProjectTree.Expand(root) + if self.Controler: + infos = self.Controler.GetProjectInfos() + root = self.ProjectTree.GetRootItem() + if root is None or not root.IsOk(): + root = self.ProjectTree.AddRoot(infos["name"]) + self.GenerateProjectTreeBranch(root, infos) + self.ProjectTree.Expand(root) # Select new item corresponding to previous selected item if tagname is not None: @@ -1495,16 +1539,16 @@ self.ProjectTree.SetItemTextColour(root, highlight_colours[1]) self.ProjectTree.SetItemExtraImage(root, None) if infos["type"] == ITEM_POU: - self.ProjectTree.SetItemImage(root, - self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])]) + self.ProjectTree.SetItemImage( + root, self.TreeImageDict[self.Controler.GetPouBodyType(infos["name"])]) if item_alone: self.ProjectTree.SetItemExtraImage(root, self.Controler.GetPouType(infos["name"])) - elif infos.has_key("icon") and infos["icon"] is not None: + elif "icon" in infos and infos["icon"] is not None: icon_name = infos["icon"] - if not self.TreeImageDict.has_key(icon_name): + if icon_name not in self.TreeImageDict: self.TreeImageDict[icon_name] = self.TreeImageList.Add(GetBitmap(icon_name)) self.ProjectTree.SetItemImage(root, self.TreeImageDict[icon_name]) - elif self.TreeImageDict.has_key(infos["type"]): + elif infos["type"] in self.TreeImageDict: self.ProjectTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) item, root_cookie = self.ProjectTree.GetFirstChild(root) @@ -1535,8 +1579,8 @@ root = self.ProjectTree.GetRootItem() if root is not None and root.IsOk(): words = tagname.split("::") - result = self.RecursiveProjectTreeItemSelection(root, - zip(words[1:], self.TagNamePartsItemTypes.get(words[0], []))) + result = self.RecursiveProjectTreeItemSelection( + root, zip(words[1:], self.TagNamePartsItemTypes.get(words[0], []))) return result def RecursiveProjectTreeItemSelection(self, root, items): @@ -1587,18 +1631,18 @@ new_name = event.GetLabel() if new_name != "": if not TestIdentifier(new_name): - message = _("\"%s\" is not a valid identifier!")%new_name + message = _("\"%s\" is not a valid identifier!") % new_name elif new_name.upper() in IEC_KEYWORDS: - message = _("\"%s\" is a keyword. It can't be used!")%new_name + message = _("\"%s\" is a keyword. It can't be used!") % new_name else: item = event.GetItem() old_name = self.ProjectTree.GetItemText(item) item_infos = self.ProjectTree.GetPyData(item) if item_infos["type"] == ITEM_PROJECT: - self.Controler.SetProjectProperties(name = new_name) + self.Controler.SetProjectProperties(name=new_name) elif item_infos["type"] == ITEM_DATATYPE: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectDataTypeNames() if name != old_name]: - message = _("\"%s\" data type already exists!")%new_name + message = _("\"%s\" data type already exists!") % new_name abort = True if not abort: self.Controler.ChangeDataTypeName(old_name, new_name) @@ -1607,10 +1651,10 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_POU: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames() if name != old_name]: - message = _("\"%s\" pou already exists!")%new_name + message = _("\"%s\" pou already exists!") % new_name abort = True elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]: - messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() @@ -1622,11 +1666,11 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_TRANSITION: pou_item = self.ProjectTree.GetItemParent(event.GetItem()) - pou_name = self.ProjectTree.GetItemText(pou_item) + pou_name = self.ProjectTree.GetItemText(pou_item) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - message = _("A POU named \"%s\" already exists!")%new_name + message = _("A POU named \"%s\" already exists!") % new_name elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]: - message = _("A variable with \"%s\" as name already exists in this pou!")%new_name + message = _("A variable with \"%s\" as name already exists in this pou!") % new_name else: words = item_infos["tagname"].split("::") self.Controler.ChangePouTransitionName(words[1], old_name, new_name) @@ -1637,9 +1681,9 @@ pou_item = self.ProjectTree.GetItemParent(event.GetItem()) pou_name = self.ProjectTree.GetItemText(pou_item) if new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - message = _("A POU named \"%s\" already exists!")%new_name + message = _("A POU named \"%s\" already exists!") % new_name elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames(pou_name) if name != old_name]: - message = _("A variable with \"%s\" as name already exists in this pou!")%new_name + message = _("A variable with \"%s\" as name already exists in this pou!") % new_name else: words = item_infos["tagname"].split("::") self.Controler.ChangePouActionName(words[1], old_name, new_name) @@ -1648,15 +1692,15 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_CONFIGURATION: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames() if name != old_name]: - message = _("\"%s\" config already exists!")%new_name + message = _("\"%s\" config already exists!") % new_name abort = True elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]: - messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() @@ -1667,15 +1711,15 @@ self.RefreshPageTitles() elif item_infos["type"] == ITEM_RESOURCE: if new_name.upper() in [name.upper() for name in self.Controler.GetProjectConfigNames()]: - message = _("\"%s\" config already exists!")%new_name + message = _("\"%s\" config already exists!") % new_name abort = True elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames()]: - messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() elif new_name.upper() in [name.upper() for name in self.Controler.GetProjectPouVariableNames()]: - messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?")%new_name, _("Error"), wx.YES_NO|wx.ICON_QUESTION) + messageDialog = wx.MessageDialog(self, _("A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?") % new_name, _("Error"), wx.YES_NO | wx.ICON_QUESTION) if messageDialog.ShowModal() == wx.ID_NO: abort = True messageDialog.Destroy() @@ -1697,20 +1741,18 @@ def OnProjectTreeItemActivated(self, event): selected = event.GetItem() - name = self.ProjectTree.GetItemText(selected) item_infos = self.ProjectTree.GetPyData(selected) if item_infos["type"] == ITEM_PROJECT: self.EditProjectSettings() else: if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU, - ITEM_CONFIGURATION, ITEM_RESOURCE, - ITEM_TRANSITION, ITEM_ACTION]: + ITEM_CONFIGURATION, ITEM_RESOURCE, + ITEM_TRANSITION, ITEM_ACTION]: self.EditProjectElement(item_infos["type"], item_infos["tagname"]) event.Skip() def ProjectTreeItemSelect(self, select_item): if select_item is not None and select_item.IsOk(): - name = self.ProjectTree.GetItemText(select_item) item_infos = self.ProjectTree.GetPyData(select_item) if item_infos["type"] in [ITEM_DATATYPE, ITEM_POU, ITEM_CONFIGURATION, ITEM_RESOURCE, @@ -1734,10 +1776,10 @@ if item != self.LastToolTipItem and self.LastToolTipItem is not None: self.ProjectTree.SetToolTip(None) self.LastToolTipItem = None - if (self.LastToolTipItem != item and - item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]): + if self.LastToolTipItem != item and \ + item_infos["type"] in [ITEM_POU, ITEM_TRANSITION, ITEM_ACTION]: bodytype = self.Controler.GetEditedElementBodyType( - item_infos["tagname"]) + item_infos["tagname"]) if item_infos["type"] == ITEM_POU: block_type = { "program": _("Program"), @@ -1750,8 +1792,8 @@ block_type = "Action" self.LastToolTipItem = item wx.CallAfter(self.ProjectTree.SetToolTipString, - "%s : %s : %s" % ( - block_type, bodytype, item_infos["name"])) + "%s : %s : %s" % ( + block_type, bodytype, item_infos["name"])) elif self.LastToolTipItem is not None: self.ProjectTree.SetToolTip(None) self.LastToolTipItem = None @@ -1764,7 +1806,7 @@ else: event.Skip() - def EditProjectElement(self, element, tagname, onlyopened = False): + def EditProjectElement(self, element, tagname, onlyopened=False): openedidx = self.IsOpened(tagname) if openedidx is not None: old_selected = self.TabsOpened.GetSelection() @@ -1858,7 +1900,7 @@ if name != "Project": new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Add POU")) - self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions" : "function", "Function Blocks" : "functionBlock", "Programs" : "program"}[name]), id=new_id) + self.Bind(wx.EVT_MENU, self.GenerateAddPouFunction({"Functions": "function", "Function Blocks": "functionBlock", "Programs": "program"}[name]), id=new_id) new_id = wx.NewId() AppendMenu(menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Paste POU")) @@ -1966,10 +2008,9 @@ event.Skip() - -#------------------------------------------------------------------------------- -# Instances Tree Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Instances Tree Management Functions + # ------------------------------------------------------------------------------- def GetTreeImage(self, var_class): return self.TreeImageDict[var_class] @@ -2043,7 +2084,7 @@ self.TabsOpened.DeletePage(idx) else: editor.SubscribeAllDataConsumers() - elif editor.IsDebugging(): + elif editor.IsDebugging() and hasattr(editor, 'SubscribeAllDataConsumers'): editor.SubscribeAllDataConsumers() self.DebugVariablePanel.SubscribeAllDataConsumers() @@ -2052,16 +2093,16 @@ self.DebugVariablePanel.InsertValue(iec_path, force=force, graph=graph) self.EnsureTabVisible(self.DebugVariablePanel) -#------------------------------------------------------------------------------- -# Library Panel Management Function -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Library Panel Management Function + # ------------------------------------------------------------------------------- def RefreshLibraryPanel(self): self.LibraryPanel.RefreshTree() -#------------------------------------------------------------------------------- -# ToolBars Management Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # ToolBars Management Functions + # ------------------------------------------------------------------------------- def AddToMenuToolBar(self, items): MenuToolBar = self.Panes["MenuToolBar"] @@ -2129,10 +2170,9 @@ self.CurrentMenu = menu self.ResetCurrentMode() - -#------------------------------------------------------------------------------- -# EditorToolBar Items Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # EditorToolBar Items Functions + # ------------------------------------------------------------------------------- def ResetCurrentMode(self): selected = self.TabsOpened.GetSelection() @@ -2268,10 +2308,16 @@ else: self.TabsOpened.GetPage(selected).AddJump() - -#------------------------------------------------------------------------------- -# Add Project Elements Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Add Project Elements Functions + # ------------------------------------------------------------------------------- + + def OnAddNewProject(self, event): + # Asks user to create main program after creating new project + AddProgramDialog = self.GenerateAddPouFunction('program', True) + # Checks that user created main program + if AddProgramDialog(event): + self.Controler.SetProjectDefaultConfiguration() def OnAddDataTypeMenu(self, event): tagname = self.Controler.ProjectAddDataType() @@ -2279,19 +2325,23 @@ self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE) self.EditProjectElement(ITEM_DATATYPE, tagname) - def GenerateAddPouFunction(self, pou_type): + def GenerateAddPouFunction(self, pou_type, type_readonly=False): def OnAddPouMenu(event): - dialog = PouDialog(self, pou_type) + dialog = PouDialog(self, pou_type, type_readonly) dialog.SetPouNames(self.Controler.GetProjectPouNames()) dialog.SetPouElementNames(self.Controler.GetProjectPouVariableNames()) dialog.SetValues({"pouName": self.Controler.GenerateNewName(None, None, "%s%%d" % pou_type)}) + pou_created = False if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() tagname = self.Controler.ProjectAddPou(values["pouName"], values["pouType"], values["language"]) if tagname is not None: self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) self.EditProjectElement(ITEM_POU, tagname) + dialog.Destroy() + pou_created = True dialog.Destroy() + return pou_created return OnAddPouMenu def GenerateAddTransitionFunction(self, pou_name): @@ -2368,7 +2418,7 @@ if self.ProjectTree.GetPyData(selected)["type"] != ITEM_PROJECT: pou_type = self.ProjectTree.GetItemText(selected) - pou_type = UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs' + pou_type = UNEDITABLE_NAMES_DICT[pou_type] # one of 'Functions', 'Function Blocks' or 'Programs' pou_type = {'Functions': 'function', 'Function Blocks': 'functionBlock', 'Programs': 'program'}[pou_type] else: pou_type = None @@ -2383,17 +2433,18 @@ self._Refresh(TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, PROJECTTREE, LIBRARYTREE) self.EditProjectElement(ITEM_POU, result[0]) -#------------------------------------------------------------------------------- -# Remove Project Elements Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Remove Project Elements Functions + # ------------------------------------------------------------------------------- def CheckElementIsUsedBeforeDeletion(self, check_function, title, name): if not check_function(name): return True - dialog = wx.MessageDialog(self, + dialog = wx.MessageDialog( + self, _("\"%s\" is used by one or more POUs. Do you wish to continue?") % name, - title, wx.YES_NO|wx.ICON_QUESTION) + title, wx.YES_NO | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() return answer == wx.ID_YES @@ -2487,9 +2538,9 @@ self.TabsOpened.DeletePage(idx) self._Refresh(TITLE, FILEMENU, EDITMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL) -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def ShowHighlight(self, infos, start, end, highlight_type): self.SelectProjectTreeItem(infos[0]) @@ -2526,14 +2577,17 @@ def ClearSearchResults(self): self.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Viewer Printout -#------------------------------------------------------------------------------- - -UPPER_DIV = lambda x, y: (x / y) + {True : 0, False : 1}[(x % y) == 0] +# ------------------------------------------------------------------------------- + + +def UPPER_DIV(x, y): + return (x / y) + {True: 0, False: 1}[(x % y) == 0] + class GraphicPrintout(wx.Printout): - def __init__(self, viewer, page_size, margins, preview = False): + def __init__(self, viewer, page_size, margins, preview=False): wx.Printout.__init__(self) self.Viewer = viewer self.PageSize = page_size @@ -2572,14 +2626,12 @@ # Get the size of the DC in pixels ppiPrinterX, ppiPrinterY = self.GetPPIPrinter() - ppiScreenX, ppiScreenY = self.GetPPIScreen() pw, ph = self.GetPageSizePixels() dw, dh = dc.GetSizeTuple() Xscale = (float(dw) * float(ppiPrinterX)) / (float(pw) * 25.4) Yscale = (float(dh) * float(ppiPrinterY)) / (float(ph) * 25.4) fontsize = self.FontSize * Yscale - text_margin = self.TextMargin * Yscale margin_left = self.Margins[0].x * Xscale margin_top = self.Margins[0].y * Yscale @@ -2593,7 +2645,7 @@ dc.SetFont(wx.Font(fontsize, wx.DEFAULT, wx.NORMAL, wx.NORMAL)) dc.SetTextForeground(wx.BLACK) block_name = " - ".join(self.Viewer.GetTagName().split("::")[1:]) - text_width, text_height = dc.GetTextExtent(block_name) + _text_width, text_height = dc.GetTextExtent(block_name) dc.DrawText(block_name, margin_left, margin_top - text_height - self.TextMargin) dc.DrawText(_("Page: %d") % page, margin_left, margin_top + area_height + self.TextMargin) @@ -2610,9 +2662,6 @@ dc.SetClippingRegion(posX, posY, self.PageSize[0] * scale, self.PageSize[1] * scale) dc.SetUserScale(scale, scale) - #------------------------------------------- - self.Viewer.DoDrawing(dc, True) return True - diff -r c1298e7ffe3a -r 8391c11477f4 NativeLib.py --- a/NativeLib.py Fri Mar 24 12:07:47 2017 +0000 +++ b/NativeLib.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,10 +23,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os + +from __future__ import absolute_import +import util.paths as paths from POULibrary import POULibrary + class NativeLibrary(POULibrary): def GetLibraryPath(self): - return os.path.join(os.path.split(__file__)[0], "NativeLib.xml") - + return paths.AbsNeighbourFile(__file__, "NativeLib.xml") diff -r c1298e7ffe3a -r 8391c11477f4 PLCControler.py --- a/PLCControler.py Fri Mar 24 12:07:47 2017 +0000 +++ b/PLCControler.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,50 +23,58 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from xml.dom import minidom -from types import StringType, UnicodeType, TupleType -from lxml import etree + +from __future__ import absolute_import +from types import TupleType from copy import deepcopy -import os,sys,re +import os +import re import datetime from time import localtime from collections import OrderedDict, namedtuple +from lxml import etree + +import util.paths as paths +from util.TranslationCatalogs import NoTranslate from plcopen import * from graphics.GraphicCommons import * from PLCGenerator import * duration_model = re.compile("(?:([0-9]{1,2})h)?(?:([0-9]{1,2})m(?!s))?(?:([0-9]{1,2})s)?(?:([0-9]{1,3}(?:\.[0-9]*)?)ms)?") -ITEMS_EDITABLE = [ITEM_PROJECT, - ITEM_POU, - ITEM_VARIABLE, - ITEM_TRANSITION, - ITEM_ACTION, - ITEM_CONFIGURATION, - ITEM_RESOURCE, - ITEM_DATATYPE - ] = range(8) - -ITEMS_UNEDITABLE = [ITEM_DATATYPES, - ITEM_FUNCTION, - ITEM_FUNCTIONBLOCK, - ITEM_PROGRAM, - ITEM_TRANSITIONS, - ITEM_ACTIONS, - ITEM_CONFIGURATIONS, - ITEM_RESOURCES, - ITEM_PROPERTIES - ] = range(8, 17) - -ITEMS_VARIABLE = [ITEM_VAR_LOCAL, - ITEM_VAR_GLOBAL, - ITEM_VAR_EXTERNAL, - ITEM_VAR_TEMP, - ITEM_VAR_INPUT, - ITEM_VAR_OUTPUT, - ITEM_VAR_INOUT - ] = range(17, 24) +ITEMS_EDITABLE = [ + ITEM_PROJECT, + ITEM_POU, + ITEM_VARIABLE, + ITEM_TRANSITION, + ITEM_ACTION, + ITEM_CONFIGURATION, + ITEM_RESOURCE, + ITEM_DATATYPE +] = range(8) + +ITEMS_UNEDITABLE = [ + ITEM_DATATYPES, + ITEM_FUNCTION, + ITEM_FUNCTIONBLOCK, + ITEM_PROGRAM, + ITEM_TRANSITIONS, + ITEM_ACTIONS, + ITEM_CONFIGURATIONS, + ITEM_RESOURCES, + ITEM_PROPERTIES +] = range(8, 17) + +ITEMS_VARIABLE = [ + ITEM_VAR_LOCAL, + ITEM_VAR_GLOBAL, + ITEM_VAR_EXTERNAL, + ITEM_VAR_TEMP, + ITEM_VAR_INPUT, + ITEM_VAR_OUTPUT, + ITEM_VAR_INOUT +] = range(17, 24) VAR_CLASS_INFOS = { "Local": ("localVars", ITEM_VAR_LOCAL), @@ -76,10 +85,11 @@ "Output": ("outputVars", ITEM_VAR_OUTPUT), "InOut": ("inOutVars", ITEM_VAR_INOUT)} -POU_TYPES = {"program": ITEM_PROGRAM, - "functionBlock": ITEM_FUNCTIONBLOCK, - "function": ITEM_FUNCTION, - } +POU_TYPES = { + "program": ITEM_PROGRAM, + "functionBlock": ITEM_FUNCTIONBLOCK, + "function": ITEM_FUNCTION, +} LOCATIONS_ITEMS = [LOCATION_CONFNODE, LOCATION_MODULE, @@ -88,23 +98,24 @@ LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY] = range(6) -ScriptDirectory = os.path.split(os.path.realpath(__file__))[0] +ScriptDirectory = paths.AbsDir(__file__) + def GetUneditableNames(): - _ = lambda x:x + _ = NoTranslate return [_("User-defined POUs"), _("Functions"), _("Function Blocks"), _("Programs"), _("Data Types"), _("Transitions"), _("Actions"), _("Configurations"), _("Resources"), _("Properties")] + + UNEDITABLE_NAMES = GetUneditableNames() [USER_DEFINED_POUS, FUNCTIONS, FUNCTION_BLOCKS, PROGRAMS, DATA_TYPES, TRANSITIONS, ACTIONS, CONFIGURATIONS, RESOURCES, PROPERTIES] = UNEDITABLE_NAMES -#------------------------------------------------------------------------------- -# Helper object for loading library in xslt stylesheets -#------------------------------------------------------------------------------- class LibraryResolver(etree.Resolver): + """Helper object for loading library in xslt stylesheets""" def __init__(self, controller, debug=False): self.Controller = controller @@ -124,33 +135,43 @@ lib_el.append(deepcopy(ctn["types"])) return self.resolve_string(etree.tostring(lib_el), context) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers functions for translating list of arguments # from xslt to valid arguments -#------------------------------------------------------------------------------- - -_StringValue = lambda x: x -_BoolValue = lambda x: x in ["true", "0"] +# ------------------------------------------------------------------------------- + + +def _StringValue(x): + return x + + +def _BoolValue(x): + return x in ["true", "0"] + def _translate_args(translations, args): return [translate(arg[0]) if len(arg) > 0 else None for translate, arg in zip(translations, args)] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers object for generating pou var list -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class _VariableInfos(object): __slots__ = ["Name", "Class", "Option", "Location", "InitialValue", "Edit", "Documentation", "Type", "Tree", "Number"] + def __init__(self, *args): for attr, value in zip(self.__slots__, args): setattr(self, attr, value if value is not None else "") + def copy(self): return _VariableInfos(*[getattr(self, attr) for attr in self.__slots__]) -class VariablesInfosFactory: + +class VariablesInfosFactory(object): def __init__(self, variables): self.Variables = variables @@ -182,13 +203,14 @@ self.TreeStack[-1].append(var) def AddVariable(self, context, *args): - self.Variables.append(_VariableInfos(*(_translate_args( - [_StringValue] * 5 + [_BoolValue] + [_StringValue], args) + + self.Variables.append(_VariableInfos(*( + _translate_args([_StringValue] * 5 + [_BoolValue] + [_StringValue], args) + [self.GetType(), self.GetTree()]))) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers object for generating pou variable instance list -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def class_extraction(value): class_type = { @@ -210,15 +232,19 @@ return None + class _VariablesTreeItemInfos(object): __slots__ = ["name", "var_class", "type", "edit", "debug", "variables"] + def __init__(self, *args): for attr, value in zip(self.__slots__, args): setattr(self, attr, value if value is not None else "") + def copy(self): - return _VariableTreeItem(*[getattr(self, attr) for attr in self.__slots__]) - -class VariablesTreeInfosFactory: + return _VariablesTreeItemInfos(*[getattr(self, attr) for attr in self.__slots__]) + + +class VariablesTreeInfosFactory(object): def __init__(self): self.Root = None @@ -239,23 +265,18 @@ [_StringValue, class_extraction, _StringValue] + [_BoolValue] * 2, args) + [[]]))) -#------------------------------------------------------------------------------- -# Helpers object for generating instances path list -#------------------------------------------------------------------------------- - -class InstancesPathFactory: - + +class InstancesPathFactory(object): + """Helpers object for generating instances path list""" def __init__(self, instances): self.Instances = instances def AddInstance(self, context, *args): self.Instances.append(args[0][0]) -#------------------------------------------------------------------------------- -# Helpers object for generating instance tagname -#------------------------------------------------------------------------------- - -class InstanceTagName: + +class InstanceTagName(object): + """Helpers object for generating instance tagname""" def __init__(self, controller): self.Controller = controller @@ -279,13 +300,16 @@ def TransitionTagName(self, context, *args): self.TagName = self.Controller.ComputePouTransitionName(args[0][0], args[0][1]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Helpers object for generating pou block instances list -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + _Point = namedtuple("Point", ["x", "y"]) -_BlockInstanceInfos = namedtuple("BlockInstanceInfos", +_BlockInstanceInfos = namedtuple( + "BlockInstanceInfos", ["type", "id", "x", "y", "width", "height", "specific_values", "inputs", "outputs"]) _BlockSpecificValues = ( @@ -345,21 +369,27 @@ [lambda x: x]), } -_InstanceConnectionInfos = namedtuple("InstanceConnectionInfos", +_InstanceConnectionInfos = namedtuple( + "InstanceConnectionInfos", ["name", "negated", "edge", "position", "links"]) -_ConnectionLinkInfos = namedtuple("ConnectionLinkInfos", +_ConnectionLinkInfos = namedtuple( + "ConnectionLinkInfos", ["refLocalId", "formalParameter", "points"]) + class _ActionInfos(object): __slots__ = ["qualifier", "type", "value", "duration", "indicator"] + def __init__(self, *args): for attr, value in zip(self.__slots__, args): setattr(self, attr, value if value is not None else "") + def copy(self): return _ActionInfos(*[getattr(self, attr) for attr in self.__slots__]) -class BlockInstanceFactory: + +class BlockInstanceFactory(object): def __init__(self, block_instances): self.BlockInstances = block_instances @@ -378,8 +408,8 @@ specific_values_tuple, specific_values_translation = \ _SpecificValuesTuples.get(args[0][0], _BlockSpecificValues) - if (args[0][0] == "step" and len(self.SpecificValues) < 3 or - args[0][0] == "transition" and len(self.SpecificValues) < 4): + if args[0][0] == "step" and len(self.SpecificValues) < 3 or \ + args[0][0] == "transition" and len(self.SpecificValues) < 4: self.SpecificValues.append([None]) elif args[0][0] == "actionBlock" and len(self.SpecificValues) < 1: self.SpecificValues.append([[]]) @@ -424,23 +454,25 @@ translated_args = _translate_args([_StringValue] * 5, args) self.SpecificValues[0][0].append(_ActionInfos(*translated_args)) + pou_block_instances_xslt = etree.parse( os.path.join(ScriptDirectory, "plcopen", "pou_block_instances.xslt")) -#------------------------------------------------------------------------------- -# Undo Buffer for PLCOpenEditor -#------------------------------------------------------------------------------- # Length of the buffer UNDO_BUFFER_LENGTH = 20 -""" -Class implementing a buffer of changes made on the current editing model -""" -class UndoBuffer: - - # Constructor initialising buffer - def __init__(self, currentstate, issaved = False): + +class UndoBuffer(object): + """ + Undo Buffer for PLCOpenEditor + Class implementing a buffer of changes made on the current editing model + """ + + def __init__(self, currentstate, issaved=False): + """ + Constructor initialising buffer + """ self.Buffer = [] self.CurrentIndex = -1 self.MinIndex = -1 @@ -462,8 +494,10 @@ else: self.LastSave = -1 - # Add a new state in buffer def Buffering(self, currentstate): + """ + Add a new state in buffer + """ self.CurrentIndex = (self.CurrentIndex + 1) % UNDO_BUFFER_LENGTH self.Buffer[self.CurrentIndex] = currentstate # Actualising buffer limits @@ -475,8 +509,10 @@ self.MinIndex = (self.MinIndex + 1) % UNDO_BUFFER_LENGTH self.MinIndex = max(self.MinIndex, 0) - # Return current state of buffer def Current(self): + """ + Return current state of buffer + """ return self.Buffer[self.CurrentIndex] # Change current state to previous in buffer and return new current state @@ -510,14 +546,11 @@ return self.LastSave == self.CurrentIndex -#------------------------------------------------------------------------------- -# Controler for PLCOpenEditor -#------------------------------------------------------------------------------- - -""" -Class which controls the operations made on the plcopen model and answers to view requests -""" -class PLCControler: +class PLCControler(object): + """ + Controler for PLCOpenEditor + Class which controls the operations made on the plcopen model and answers to view requests + """ # Create a new PLCControler def __init__(self): @@ -545,15 +578,15 @@ def GetQualifierTypes(self): return QualifierList - def GetProject(self, debug = False): + def GetProject(self, debug=False): if debug and self.CurrentCompiledProject is not None: return self.CurrentCompiledProject else: return self.Project -#------------------------------------------------------------------------------- -# Project management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project management functions + # ------------------------------------------------------------------------------- # Return if a project is opened def HasOpenedProject(self): @@ -577,28 +610,28 @@ self.Buffering = False # Return project data type names - def GetProjectDataTypeNames(self, debug = False): + def GetProjectDataTypeNames(self, debug=False): project = self.GetProject(debug) if project is not None: return [datatype.getname() for datatype in project.getdataTypes()] return [] # Return project pou names - def GetProjectPouNames(self, debug = False): + def GetProjectPouNames(self, debug=False): project = self.GetProject(debug) if project is not None: return [pou.getname() for pou in project.getpous()] return [] # Return project pou names - def GetProjectConfigNames(self, debug = False): + def GetProjectConfigNames(self, debug=False): project = self.GetProject(debug) if project is not None: return [config.getname() for config in project.getconfigurations()] return [] # Return project pou variable names - def GetProjectPouVariableNames(self, pou_name = None, debug = False): + def GetProjectPouVariableNames(self, pou_name=None, debug=False): variables = [] project = self.GetProject(debug) if project is not None: @@ -625,7 +658,7 @@ if self.ProjectIsSaved(): return self.FileName else: - return "~%s~"%self.FileName + return "~%s~" % self.FileName return "" # Change file path and save file name or create a default one if file path not defined @@ -633,12 +666,12 @@ self.FilePath = filepath if filepath == "": self.LastNewIndex += 1 - self.FileName = _("Unnamed%d")%self.LastNewIndex + self.FileName = _("Unnamed%d") % self.LastNewIndex else: self.FileName = os.path.splitext(os.path.basename(filepath))[0] # Change project properties - def SetProjectProperties(self, name = None, properties = None, buffer = True): + def SetProjectProperties(self, name=None, properties=None, buffer=True): if self.Project is not None: if name is not None: self.Project.setname(name) @@ -656,7 +689,7 @@ return None # Return project properties - def GetProjectProperties(self, debug = False): + def GetProjectProperties(self, debug=False): project = self.GetProject(debug) if project is not None: properties = project.getfileHeader() @@ -665,17 +698,34 @@ return None # Return project informations - def GetProjectInfos(self, debug = False): + def GetProjectInfos(self, debug=False): project = self.GetProject(debug) if project is not None: infos = {"name": project.getname(), "type": ITEM_PROJECT} - datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values":[]} + datatypes = {"name": DATA_TYPES, "type": ITEM_DATATYPES, "values": []} for datatype in project.getdataTypes(): - datatypes["values"].append({"name": datatype.getname(), "type": ITEM_DATATYPE, - "tagname": self.ComputeDataTypeName(datatype.getname()), "values": []}) - pou_types = {"function": {"name": FUNCTIONS, "type": ITEM_FUNCTION, "values":[]}, - "functionBlock": {"name": FUNCTION_BLOCKS, "type": ITEM_FUNCTIONBLOCK, "values":[]}, - "program": {"name": PROGRAMS, "type": ITEM_PROGRAM, "values":[]}} + datatypes["values"].append({ + "name": datatype.getname(), + "type": ITEM_DATATYPE, + "tagname": self.ComputeDataTypeName(datatype.getname()), + "values": []}) + pou_types = { + "function": { + "name": FUNCTIONS, + "type": ITEM_FUNCTION, + "values": [] + }, + "functionBlock": { + "name": FUNCTION_BLOCKS, + "type": ITEM_FUNCTIONBLOCK, + "values": [] + }, + "program": { + "name": PROGRAMS, + "type": ITEM_PROGRAM, + "values": [] + } + } for pou in project.getpous(): pou_type = pou.getpouType() pou_infos = {"name": pou.getname(), "type": ITEM_POU, @@ -684,13 +734,17 @@ if pou.getbodyType() == "SFC": transitions = [] for transition in pou.gettransitionList(): - transitions.append({"name": transition.getname(), "type": ITEM_TRANSITION, + transitions.append({ + "name": transition.getname(), + "type": ITEM_TRANSITION, "tagname": self.ComputePouTransitionName(pou.getname(), transition.getname()), "values": []}) pou_values.append({"name": TRANSITIONS, "type": ITEM_TRANSITIONS, "values": transitions}) actions = [] for action in pou.getactionList(): - actions.append({"name": action.getname(), "type": ITEM_ACTION, + actions.append({ + "name": action.getname(), + "type": ITEM_ACTION, "tagname": self.ComputePouActionName(pou.getname(), action.getname()), "values": []}) pou_values.append({"name": ACTIONS, "type": ITEM_ACTIONS, "values": actions}) @@ -700,13 +754,17 @@ configurations = {"name": CONFIGURATIONS, "type": ITEM_CONFIGURATIONS, "values": []} for config in project.getconfigurations(): config_name = config.getname() - config_infos = {"name": config_name, "type": ITEM_CONFIGURATION, + config_infos = { + "name": config_name, + "type": ITEM_CONFIGURATION, "tagname": self.ComputeConfigurationName(config.getname()), "values": []} resources = {"name": RESOURCES, "type": ITEM_RESOURCES, "values": []} for resource in config.getresource(): resource_name = resource.getname() - resource_infos = {"name": resource_name, "type": ITEM_RESOURCE, + resource_infos = { + "name": resource_name, + "type": ITEM_RESOURCE, "tagname": self.ComputeConfigurationResourceName(config.getname(), resource.getname()), "values": []} resources["values"].append(resource_infos) @@ -717,8 +775,7 @@ return infos return None - def GetPouVariables(self, tagname, debug = False): - pou_type = None + def GetPouVariables(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: factory = VariablesTreeInfosFactory() @@ -730,8 +787,8 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "pou_variables.xslt"), parser), - extensions = {("pou_vars_ns", name): getattr(factory, name) - for name in ["SetRoot", "AddVariable"]}) + extensions={("pou_vars_ns", name): getattr(factory, name) + for name in ["SetRoot", "AddVariable"]}) obj = None words = tagname.split("::") @@ -745,7 +802,7 @@ return None - def GetInstanceList(self, root, name, debug = False): + def GetInstanceList(self, root, name, debug=False): instances = [] project = self.GetProject(debug) if project is not None: @@ -758,15 +815,15 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "instances_path.xslt"), parser), - extensions = { + extensions={ ("instances_ns", "AddInstance"): factory.AddInstance}) - instances_path_xslt_tree(root, - instance_type=etree.XSLT.strparam(name)) + instances_path_xslt_tree( + root, instance_type=etree.XSLT.strparam(name)) return instances - def SearchPouInstances(self, tagname, debug = False): + def SearchPouInstances(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: words = tagname.split("::") @@ -782,7 +839,7 @@ self.ComputePouName(words[1]), debug)] return [] - def GetPouInstanceTagName(self, instance_path, debug = False): + def GetPouInstanceTagName(self, instance_path, debug=False): project = self.GetProject(debug) factory = InstanceTagName(self) @@ -793,17 +850,19 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "instance_tagname.xslt"), parser), - extensions = {("instance_tagname_ns", name): getattr(factory, name) - for name in ["ConfigTagName", "ResourceTagName", - "PouTagName", "ActionTagName", - "TransitionTagName"]}) - - instance_tagname_xslt_tree(project, - instance_path=etree.XSLT.strparam(instance_path)) + extensions={("instance_tagname_ns", name): getattr(factory, name) + for name in ["ConfigTagName", + "ResourceTagName", + "PouTagName", + "ActionTagName", + "TransitionTagName"]}) + + instance_tagname_xslt_tree( + project, instance_path=etree.XSLT.strparam(instance_path)) return factory.GetTagName() - def GetInstanceInfos(self, instance_path, debug = False): + def GetInstanceInfos(self, instance_path, debug=False): tagname = self.GetPouInstanceTagName(instance_path) if tagname is not None: infos = self.GetPouVariables(tagname, debug) @@ -820,21 +879,21 @@ return None # Return if data type given by name is used by another data type or pou - def DataTypeIsUsed(self, name, debug = False): + def DataTypeIsUsed(self, name, debug=False): project = self.GetProject(debug) if project is not None: return len(self.GetInstanceList(project, name, debug)) > 0 return False # Return if pou given by name is used by another pou - def PouIsUsed(self, name, debug = False): + def PouIsUsed(self, name, debug=False): project = self.GetProject(debug) if project is not None: return len(self.GetInstanceList(project, name, debug)) > 0 return False # Return if pou given by name is directly or undirectly used by the reference pou - def PouIsUsedBy(self, name, reference, debug = False): + def PouIsUsedBy(self, name, reference, debug=False): pou_infos = self.GetPou(reference, debug) if pou_infos is not None: return len(self.GetInstanceList(pou_infos, name, debug)) > 0 @@ -854,8 +913,8 @@ programfile.close() self.ProgramFilePath = filepath return program_text, errors, warnings - except PLCGenException, e: - errors.append(e.message) + except PLCGenException, ex: + errors.append(ex.message) else: errors.append("No project opened") return "", errors, warnings @@ -888,9 +947,9 @@ row, col = next_row, next_col return infos -#------------------------------------------------------------------------------- -# Project Pous management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project Pous management functions + # ------------------------------------------------------------------------------- # Add a Data Type to Project def ProjectAddDataType(self, datatype_name=None): @@ -940,7 +999,7 @@ ''' try: new_pou, error = LoadPou(pou_xml) - except: + except Exception: error = "" if error is not None: return _("Couldn't paste non-POU object.") @@ -967,7 +1026,7 @@ # programs cannot be pasted as functions or function blocks if orig_type == 'functionBlock' and pou_type == 'function' or \ orig_type == 'program' and pou_type in ['function', 'functionBlock']: - msg = _('''{a1} "{a2}" can't be pasted as a {a3}.''').format(a1 = orig_type, a2 = name, a3 = pou_type) + msg = _('''{a1} "{a2}" can't be pasted as a {a3}.''').format(a1=orig_type, a2=name, a3=pou_type) return msg new_pou.setpouType(pou_type) @@ -1112,7 +1171,7 @@ # Found the pou action corresponding to old name and change its name to new name pou = self.Project.getpou(pou_name) if pou is not None: - for type, varlist in pou.getvars(): + for _type, varlist in pou.getvars(): for var in varlist.getvariable(): if var.getname() == old_name: var.setname(new_name) @@ -1137,7 +1196,7 @@ self.BufferProject() # Return the description of the pou given by its name - def GetPouDescription(self, name, debug = False): + def GetPouDescription(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its type @@ -1147,7 +1206,7 @@ return "" # Return the description of the pou given by its name - def SetPouDescription(self, name, description, debug = False): + def SetPouDescription(self, name, description, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its type @@ -1157,7 +1216,7 @@ self.BufferProject() # Return the type of the pou given by its name - def GetPouType(self, name, debug = False): + def GetPouType(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its type @@ -1167,7 +1226,7 @@ return None # Return pous with SFC language - def GetSFCPous(self, debug = False): + def GetSFCPous(self, debug=False): list = [] project = self.GetProject(debug) if project is not None: @@ -1177,7 +1236,7 @@ return list # Return the body language of the pou given by its name - def GetPouBodyType(self, name, debug = False): + def GetPouBodyType(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its body language @@ -1187,7 +1246,7 @@ return None # Return the actions of a pou - def GetPouTransitions(self, pou_name, debug = False): + def GetPouTransitions(self, pou_name, debug=False): transitions = [] project = self.GetProject(debug) if project is not None: @@ -1199,7 +1258,7 @@ return transitions # Return the body language of the transition given by its name - def GetTransitionBodyType(self, pou_name, pou_transition, debug = False): + def GetTransitionBodyType(self, pou_name, pou_transition, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name @@ -1212,7 +1271,7 @@ return None # Return the actions of a pou - def GetPouActions(self, pou_name, debug = False): + def GetPouActions(self, pou_name, debug=False): actions = [] project = self.GetProject(debug) if project is not None: @@ -1224,7 +1283,7 @@ return actions # Return the body language of the pou given by its name - def GetActionBodyType(self, pou_name, pou_action, debug = False): + def GetActionBodyType(self, pou_name, pou_action, debug=False): project = self.GetProject(debug) if project is not None: # Found the pou correponding to name and return its body language @@ -1269,7 +1328,7 @@ var_type = PLCOpenParser.CreateElement("type", "variable") if isinstance(var.Type, TupleType): if var.Type[0] == "array": - array_type, base_type_name, dimensions = var.Type + _array_type, base_type_name, dimensions = var.Type array = PLCOpenParser.CreateElement("array", "dataType") baseType = PLCOpenParser.CreateElement("baseType", "array") array.setbaseType(baseType) @@ -1330,17 +1389,17 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"), parser), - extensions = {("var_infos_ns", name): getattr(factory, name) - for name in ["SetType", "AddDimension", "AddTree", - "AddVarToTree", "AddVariable"]}) - variables_infos_xslt_tree(object_with_vars, - tree=etree.XSLT.strparam(str(tree))) + extensions={("var_infos_ns", name): getattr(factory, name) + for name in ["SetType", "AddDimension", "AddTree", + "AddVarToTree", "AddVariable"]}) + variables_infos_xslt_tree( + object_with_vars, tree=etree.XSLT.strparam(str(tree))) return variables # Add a global var to configuration to configuration def AddConfigurationGlobalVar(self, config_name, var_type, var_name, - location="", description=""): + location="", description=""): if self.Project is not None: # Found the configuration corresponding to name configuration = self.Project.getconfiguration(config_name) @@ -1358,11 +1417,11 @@ if configuration is not None: # Set configuration global vars configuration.setglobalVars([ - varlist for vartype, varlist + varlist for _vartype, varlist in self.ExtractVarLists(vars)]) # Return the configuration globalvars - def GetConfigurationGlobalVars(self, name, debug = False): + def GetConfigurationGlobalVars(self, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the configuration corresponding to name @@ -1374,7 +1433,7 @@ return [] # Return configuration variable names - def GetConfigurationVariableNames(self, config_name = None, debug = False): + def GetConfigurationVariableNames(self, config_name=None, debug=False): variables = [] project = self.GetProject(debug) if project is not None: @@ -1382,7 +1441,8 @@ if config_name is None or config_name == configuration.getname(): variables.extend( [var.getname() for var in reduce( - lambda x, y: x + y, [varlist.getvariable() + lambda x, y: x + y, [ + varlist.getvariable() for varlist in configuration.globalVars], [])]) return variables @@ -1395,11 +1455,11 @@ # Set resource global vars if resource is not None: resource.setglobalVars([ - varlist for vartype, varlist + varlist for _vartype, varlist in self.ExtractVarLists(vars)]) # Return the resource globalvars - def GetConfigurationResourceGlobalVars(self, config_name, name, debug = False): + def GetConfigurationResourceGlobalVars(self, config_name, name, debug=False): project = self.GetProject(debug) if project is not None: # Found the resource corresponding to name @@ -1411,8 +1471,8 @@ return [] # Return resource variable names - def GetConfigurationResourceVariableNames(self, - config_name = None, resource_name = None, debug = False): + def GetConfigurationResourceVariableNames( + self, config_name=None, resource_name=None, debug=False): variables = [] project = self.GetProject(debug) if project is not None: @@ -1422,13 +1482,14 @@ if resource_name is None or resource.getname() == resource_name: variables.extend( [var.getname() for var in reduce( - lambda x, y: x + y, [varlist.getvariable() + lambda x, y: x + y, [ + varlist.getvariable() for varlist in resource.globalVars], [])]) return variables # Return the interface for the given pou - def GetPouInterfaceVars(self, pou, tree=False, debug = False): + def GetPouInterfaceVars(self, pou, tree=False, debug=False): interface = pou.interface # Verify that the pou has an interface if interface is not None: @@ -1445,7 +1506,7 @@ if pou.interface is None: pou.interface = PLCOpenParser.CreateElement("interface", "pou") # Set Pou interface - pou.setvars([varlist for varlist_type, varlist in self.ExtractVarLists(vars)]) + pou.setvars([varlist for _varlist_type, varlist in self.ExtractVarLists(vars)]) # Replace the return type of the pou given by its name (only for functions) def SetPouInterfaceReturnType(self, name, return_type): @@ -1495,11 +1556,11 @@ etree.parse( os.path.join(ScriptDirectory, "plcopen", "variables_infos.xslt"), parser), - extensions = {("var_infos_ns", name): getattr(factory, name) - for name in ["SetType", "AddDimension", - "AddTree", "AddVarToTree"]}) - return_type_infos_xslt_tree(return_type, - tree=etree.XSLT.strparam(str(tree))) + extensions={("var_infos_ns", name): getattr(factory, name) + for name in ["SetType", "AddDimension", + "AddTree", "AddVarToTree"]}) + return_type_infos_xslt_tree( + return_type, tree=etree.XSLT.strparam(str(tree))) if tree: return [factory.GetType(), factory.GetTree()] return factory.GetType() @@ -1514,11 +1575,11 @@ addedcat = [{"name": _("%s POUs") % confnodetypes["name"], "list": [pou.getblockInfos() for pou in confnodetypes["types"].getpous()]} - for confnodetypes in typeslist] + for confnodetypes in typeslist] self.TotalTypes.extend(addedcat) for cat in addedcat: for desc in cat["list"]: - BlkLst = self.TotalTypesDict.setdefault(desc["name"],[]) + BlkLst = self.TotalTypesDict.setdefault(desc["name"], []) BlkLst.append((section["name"], desc)) # Function that clear the confnode list @@ -1527,12 +1588,12 @@ self.TotalTypesDict = StdBlckDct.copy() self.TotalTypes = StdBlckLst[:] - def GetConfNodeDataTypes(self, exclude = None, only_locatables = False): + def GetConfNodeDataTypes(self, exclude=None, only_locatables=False): return [{"name": _("%s Data Types") % confnodetypes["name"], "list": [ - datatype.getname() - for datatype in confnodetypes["types"].getdataTypes() - if not only_locatables or self.IsLocatableDataType(datatype, debug)]} + datatype.getname() + for datatype in confnodetypes["types"].getdataTypes() + if not only_locatables or self.IsLocatableDataType(datatype)]} for confnodetypes in self.ConfNodeTypes] def GetVariableLocationTree(self): @@ -1568,15 +1629,15 @@ return global_vars # Function that returns the block definition associated to the block type given - def GetBlockType(self, typename, inputs = None, debug = False): - result_blocktype = None - for sectioname, blocktype in self.TotalTypesDict.get(typename,[]): + def GetBlockType(self, typename, inputs=None, debug=False): + result_blocktype = {} + for _sectioname, blocktype in self.TotalTypesDict.get(typename, []): if inputs is not None and inputs != "undefined": - block_inputs = tuple([var_type for name, var_type, modifier in blocktype["inputs"]]) + block_inputs = tuple([var_type for _name, var_type, _modifier in blocktype["inputs"]]) if reduce(lambda x, y: x and y, map(lambda x: x[0] == "ANY" or self.IsOfType(*x), zip(inputs, block_inputs)), True): return blocktype else: - if result_blocktype is not None: + if result_blocktype: if inputs == "undefined": return None else: @@ -1584,7 +1645,7 @@ result_blocktype["outputs"] = [(o[0], "ANY", o[2]) for o in result_blocktype["outputs"]] return result_blocktype result_blocktype = blocktype.copy() - if result_blocktype is not None: + if result_blocktype: return result_blocktype project = self.GetProject(debug) if project is not None: @@ -1595,20 +1656,19 @@ return blocktype_infos if inputs == tuple([var_type - for name, var_type, modifier in blocktype_infos["inputs"]]): + for _name, var_type, _modifier in blocktype_infos["inputs"]]): return blocktype_infos return None # Return Block types checking for recursion - def GetBlockTypes(self, tagname = "", debug = False): - typename = None + def GetBlockTypes(self, tagname="", debug=False): words = tagname.split("::") name = None project = self.GetProject(debug) if project is not None: pou_type = None - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: name = words[1] pou_type = self.GetPouType(name, debug) filter = (["function"] @@ -1619,35 +1679,38 @@ "list": [block for block in category["list"] if block["type"] in filter]} for category in self.TotalTypes] - blocktypes.append({"name" : USER_DEFINED_POUS, + blocktypes.append({ + "name": USER_DEFINED_POUS, "list": [pou.getblockInfos() for pou in project.getpous(name, filter) if (name is None or - len(self.GetInstanceList(pou, name, debug)) == 0)]}) + len(self.GetInstanceList(pou, name, debug)) == 0)] + }) return blocktypes return self.TotalTypes # Return Function Block types checking for recursion - def GetFunctionBlockTypes(self, tagname = "", debug = False): + def GetFunctionBlockTypes(self, tagname="", debug=False): project = self.GetProject(debug) words = tagname.split("::") name = None - if project is not None and words[0] in ["P","T","A"]: + if project is not None and words[0] in ["P", "T", "A"]: name = words[1] blocktypes = [] for blocks in self.TotalTypesDict.itervalues(): - for sectioname,block in blocks: + for _sectioname, block in blocks: if block["type"] == "functionBlock": blocktypes.append(block["name"]) if project is not None: - blocktypes.extend([pou.getname() + blocktypes.extend([ + pou.getname() for pou in project.getpous(name, ["functionBlock"]) if (name is None or len(self.GetInstanceList(pou, name, debug)) == 0)]) return blocktypes # Return Block types checking for recursion - def GetBlockResource(self, debug = False): + def GetBlockResource(self, debug=False): blocktypes = [] for category in StdBlckLst[:-1]: for blocktype in category["list"]: @@ -1661,7 +1724,7 @@ return blocktypes # Return Data Types checking for recursion - def GetDataTypes(self, tagname = "", basetypes = True, confnodetypes = True, only_locatables = False, debug = False): + def GetDataTypes(self, tagname="", basetypes=True, confnodetypes=True, only_locatables=False, debug=False): if basetypes: datatypes = self.GetBaseTypes() else: @@ -1675,16 +1738,15 @@ datatypes.extend([ datatype.getname() for datatype in project.getdataTypes(name) - if (not only_locatables or self.IsLocatableDataType(datatype, debug)) - and (name is None or - len(self.GetInstanceList(datatype, name, debug)) == 0)]) + if ((not only_locatables or self.IsLocatableDataType(datatype, debug)) and + (name is None or len(self.GetInstanceList(datatype, name, debug)) == 0))]) if confnodetypes: for category in self.GetConfNodeDataTypes(name, only_locatables): datatypes.extend(category["list"]) return datatypes # Return Data Type Object - def GetPou(self, typename, debug = False): + def GetPou(self, typename, debug=False): project = self.GetProject(debug) if project is not None: result = project.getpou(typename) @@ -1700,9 +1762,8 @@ return result return None - # Return Data Type Object - def GetDataType(self, typename, debug = False): + def GetDataType(self, typename, debug=False): project = self.GetProject(debug) if project is not None: result = project.getdataType(typename) @@ -1725,11 +1786,10 @@ else basetype_type.upper()) return (basetype_content.getname() if basetype_content_type == "derived" else basetype_content_type.upper()) - return None # Return Base Type of given possible derived type - def GetBaseType(self, typename, debug = False): - if TypeHierarchy.has_key(typename): + def GetBaseType(self, typename, debug=False): + if typename in TypeHierarchy: return typename datatype = self.GetDataType(typename, debug) @@ -1747,9 +1807,9 @@ TypeHierarchy_list has a rough order to it (e.g. SINT, INT, DINT, ...), which makes it easy for a user to find a type in a menu. ''' - return [x for x,y in TypeHierarchy_list if not x.startswith("ANY")] - - def IsOfType(self, typename, reference, debug = False): + return [x for x, _y in TypeHierarchy_list if not x.startswith("ANY")] + + def IsOfType(self, typename, reference, debug=False): if reference is None or typename == reference: return True @@ -1770,7 +1830,7 @@ return not typename.startswith("ANY") return True - def IsLocatableDataType(self, datatype, debug = False): + def IsLocatableDataType(self, datatype, debug=False): basetype_content = datatype.baseType.getcontent() basetype_content_type = basetype_content.getLocalTag() if basetype_content_type in ["enum", "struct"]: @@ -1783,16 +1843,20 @@ return self.IsLocatableType(array_base_type.getname(), debug) return True - def IsLocatableType(self, typename, debug = False): + def IsLocatableType(self, typename, debug=False): if isinstance(typename, TupleType) or self.GetBlockType(typename) is not None: return False + # the size of these types is implementation dependend + if typename in ["TIME", "DATE", "DT", "TOD"]: + return False + datatype = self.GetDataType(typename, debug) if datatype is not None: return self.IsLocatableDataType(datatype) return True - def IsEnumeratedType(self, typename, debug = False): + def IsEnumeratedType(self, typename, debug=False): if isinstance(typename, TupleType): typename = typename[1] datatype = self.GetDataType(typename, debug) @@ -1804,7 +1868,7 @@ return basetype_content_type == "enum" return False - def IsSubrangeType(self, typename, exclude=None, debug = False): + def IsSubrangeType(self, typename, exclude=None, debug=False): if typename == exclude: return False if isinstance(typename, TupleType): @@ -1820,11 +1884,11 @@ self.GetDataTypeBaseType(datatype), exclude) return False - def IsNumType(self, typename, debug = False): + def IsNumType(self, typename, debug=False): return self.IsOfType(typename, "ANY_NUM", debug) or\ self.IsOfType(typename, "ANY_BIT", debug) - def GetDataTypeRange(self, typename, debug = False): + def GetDataTypeRange(self, typename, debug=False): range = DataTypeRange.get(typename) if range is not None: return range @@ -1840,7 +1904,7 @@ return None # Return Subrange types - def GetSubrangeBaseTypes(self, exclude, debug = False): + def GetSubrangeBaseTypes(self, exclude, debug=False): subrange_basetypes = DataTypeRange.keys() project = self.GetProject(debug) if project is not None: @@ -1854,7 +1918,7 @@ return subrange_basetypes # Return Enumerated Values - def GetEnumeratedDataValues(self, typename = None, debug = False): + def GetEnumeratedDataValues(self, typename=None, debug=False): values = [] if typename is not None: datatype_obj = self.GetDataType(typename, debug) @@ -1876,9 +1940,9 @@ values.extend(confnodetype["types"].GetEnumeratedDataTypeValues()) return values -#------------------------------------------------------------------------------- -# Project Element tag name computation functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project Element tag name computation functions + # ------------------------------------------------------------------------------- # Compute a data type name def ComputeDataTypeName(self, datatype): @@ -1906,16 +1970,21 @@ def GetElementType(self, tagname): words = tagname.split("::") - return {"D" : ITEM_DATATYPE, "P" : ITEM_POU, - "T" : ITEM_TRANSITION, "A" : ITEM_ACTION, - "C" : ITEM_CONFIGURATION, "R" : ITEM_RESOURCE}[words[0]] - -#------------------------------------------------------------------------------- -# Project opened Data types management functions -#------------------------------------------------------------------------------- + return { + "D": ITEM_DATATYPE, + "P": ITEM_POU, + "T": ITEM_TRANSITION, + "A": ITEM_ACTION, + "C": ITEM_CONFIGURATION, + "R": ITEM_RESOURCE + }[words[0]] + + # ------------------------------------------------------------------------------- + # Project opened Data types management functions + # ------------------------------------------------------------------------------- # Return the data type informations - def GetDataTypeInfos(self, tagname, debug = False): + def GetDataTypeInfos(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: words = tagname.split("::") @@ -1933,8 +2002,8 @@ base_type = basetype_content.baseType.getcontent() base_type_type = base_type.getLocalTag() infos["base_type"] = (base_type.getname() - if base_type_type == "derived" - else base_type_type) + if base_type_type == "derived" + else base_type_type) elif basetype_content_type == "enum": infos["type"] = "Enumerated" infos["values"] = [] @@ -1948,8 +2017,8 @@ base_type = basetype_content.baseType.getcontent() base_type_type = base_type.getLocalTag() infos["base_type"] = (base_type.getname() - if base_type_type == "derived" - else base_type_type.upper()) + if base_type_type == "derived" + else base_type_type.upper()) elif basetype_content_type == "struct": infos["type"] = "Structure" infos["elements"] = [] @@ -1965,9 +2034,10 @@ base_type = element_type.baseType.getcontent() base_type_type = base_type.getLocalTag() element_infos["Type"] = ("array", - base_type.getname() - if base_type_type == "derived" - else base_type_type.upper(), dimensions) + base_type.getname() + if base_type_type == "derived" + else base_type_type.upper(), + dimensions) elif element_type_type == "derived": element_infos["Type"] = element_type.getname() else: @@ -1980,8 +2050,8 @@ else: infos["type"] = "Directly" infos["base_type"] = (basetype_content.getname() - if basetype_content_type == "derived" - else basetype_content_type.upper()) + if basetype_content_type == "derived" + else basetype_content_type.upper()) if datatype.initialValue is not None: infos["initial"] = datatype.initialValue.getvalue() @@ -2061,7 +2131,7 @@ element_type = PLCOpenParser.CreateElement("type", "variable") if isinstance(element_infos["Type"], TupleType): if element_infos["Type"][0] == "array": - array_type, base_type_name, dimensions = element_infos["Type"] + _array_type, base_type_name, dimensions = element_infos["Type"] array = PLCOpenParser.CreateElement("array", "dataType") baseType = PLCOpenParser.CreateElement("baseType", "array") array.setbaseType(baseType) @@ -2110,12 +2180,12 @@ datatype.initialValue = None self.BufferProject() -#------------------------------------------------------------------------------- -# Project opened Pous management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Project opened Pous management functions + # ------------------------------------------------------------------------------- # Return edited element - def GetEditedElement(self, tagname, debug = False): + def GetEditedElement(self, tagname, debug=False): project = self.GetProject(debug) if project is not None: words = tagname.split("::") @@ -2139,21 +2209,21 @@ # Return edited element name def GetEditedElementName(self, tagname): words = tagname.split("::") - if words[0] in ["P","C","D"]: + if words[0] in ["P", "C", "D"]: return words[1] else: return words[2] return None # Return edited element name and type - def GetEditedElementType(self, tagname, debug = False): + def GetEditedElementType(self, tagname, debug=False): words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: return words[1], self.GetPouType(words[1], debug) return None, None # Return language in which edited element is written - def GetEditedElementBodyType(self, tagname, debug = False): + def GetEditedElementBodyType(self, tagname, debug=False): words = tagname.split("::") if words[0] == "P": return self.GetPouBodyType(words[1], debug) @@ -2164,9 +2234,9 @@ return None # Return the edited element variables - def GetEditedElementInterfaceVars(self, tagname, tree=False, debug = False): + def GetEditedElementInterfaceVars(self, tagname, tree=False, debug=False): words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: project = self.GetProject(debug) if project is not None: pou = project.getpou(words[1]) @@ -2175,7 +2245,7 @@ return [] # Return the edited element return type - def GetEditedElementInterfaceReturnType(self, tagname, tree=False, debug = False): + def GetEditedElementInterfaceReturnType(self, tagname, tree=False, debug=False): words = tagname.split("::") if words[0] == "P": project = self.GetProject(debug) @@ -2195,14 +2265,14 @@ element.settext(text) # Return the edited element text - def GetEditedElementText(self, tagname, debug = False): + def GetEditedElementText(self, tagname, debug=False): element = self.GetEditedElement(tagname, debug) if element is not None: return element.gettext() return "" # Return the edited element transitions - def GetEditedElementTransitions(self, tagname, debug = False): + def GetEditedElementTransitions(self, tagname, debug=False): pou = self.GetEditedElement(tagname, debug) if pou is not None and pou.getbodyType() == "SFC": transitions = [] @@ -2212,7 +2282,7 @@ return [] # Return edited element transitions - def GetEditedElementActions(self, tagname, debug = False): + def GetEditedElementActions(self, tagname, debug=False): pou = self.GetEditedElement(tagname, debug) if pou is not None and pou.getbodyType() == "SFC": actions = [] @@ -2222,9 +2292,9 @@ return [] # Return the names of the pou elements - def GetEditedElementVariables(self, tagname, debug = False): + def GetEditedElementVariables(self, tagname, debug=False): words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: return self.GetProjectPouVariableNames(words[1], debug) elif words[0] in ["C", "R"]: names = self.GetConfigurationVariableNames(words[1], debug) @@ -2234,13 +2304,13 @@ return names return [] - def GetEditedElementCopy(self, tagname, debug = False): + def GetEditedElementCopy(self, tagname, debug=False): element = self.GetEditedElement(tagname, debug) if element is not None: return element.tostring() return "" - def GetEditedElementInstancesCopy(self, tagname, blocks_id = None, wires = None, debug = False): + def GetEditedElementInstancesCopy(self, tagname, blocks_id=None, wires=None, debug=False): element = self.GetEditedElement(tagname, debug) text = "" if element is not None: @@ -2261,20 +2331,21 @@ element.remove(copy_body) return text - def GenerateNewName(self, tagname, name, format, start_idx=0, exclude={}, debug=False): - names = exclude.copy() + def GenerateNewName(self, tagname, name, format, start_idx=0, exclude=None, debug=False): + names = {} if exclude is None else exclude.copy() if tagname is not None: names.update(dict([(varname.upper(), True) for varname in self.GetEditedElementVariables(tagname, debug)])) words = tagname.split("::") - if words[0] in ["P","T","A"]: + if words[0] in ["P", "T", "A"]: element = self.GetEditedElement(tagname, debug) if element is not None and element.getbodyType() not in ["ST", "IL"]: for instance in element.getinstances(): - if isinstance(instance, - (PLCOpenParser.GetElementClass("step", "sfcObjects"), - PLCOpenParser.GetElementClass("connector", "commonObjects"), - PLCOpenParser.GetElementClass("continuation", "commonObjects"))): + if isinstance( + instance, + (PLCOpenParser.GetElementClass("step", "sfcObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), + PLCOpenParser.GetElementClass("continuation", "commonObjects"))): names[instance.getname().upper()] = True else: project = self.GetProject(debug) @@ -2296,13 +2367,13 @@ i = start_idx while name is None or names.get(name.upper(), False): - name = (format%i) + name = (format % i) i += 1 return name def PasteEditedElementInstances(self, tagname, text, new_pos, middle=False, debug=False): element = self.GetEditedElement(tagname, debug) - element_name, element_type = self.GetEditedElementType(tagname, debug) + _element_name, element_type = self.GetEditedElementType(tagname, debug) if element is not None: bodytype = element.getbodyType() @@ -2319,7 +2390,7 @@ try: instances, error = LoadPouInstances(text, bodytype) - except: + except Exception: instances, error = [], "" if error is not None or len(instances) == 0: return _("Invalid plcopen element(s)!!!") @@ -2334,10 +2405,10 @@ blockname = instance.getinstanceName() if blocktype_infos["type"] != "function" and blockname is not None: if element_type == "function": - return _("FunctionBlock \"%s\" can't be pasted in a Function!!!")%blocktype + return _("FunctionBlock \"%s\" can't be pasted in a Function!!!") % blocktype blockname = self.GenerateNewName(tagname, blockname, - "%s%%d"%blocktype, + "%s%%d" % blocktype, debug=debug) exclude[blockname] = True instance.setinstanceName(blockname) @@ -2351,7 +2422,7 @@ exclude[stepname] = True instance.setname(stepname) localid = instance.getlocalId() - if not used_id.has_key(localid): + if localid not in used_id: new_id[localid] = True idx = 1 @@ -2360,8 +2431,8 @@ for instance in instances: localId = instance.getlocalId() bbox.union(instance.getBoundingBox()) - if used_id.has_key(localId): - while used_id.has_key(idx) or new_id.has_key(idx): + if localId in used_id: + while (idx in used_id) or (idx in new_id): idx += 1 new_id[idx] = True instance.setlocalId(idx) @@ -2398,7 +2469,7 @@ return new_id, connections - def GetEditedElementInstancesInfos(self, tagname, debug = False): + def GetEditedElementInstancesInfos(self, tagname, debug=False): element_instances = OrderedDict() element = self.GetEditedElement(tagname, debug) if element is not None: @@ -2406,7 +2477,7 @@ pou_block_instances_xslt_tree = etree.XSLT( pou_block_instances_xslt, - extensions = { + extensions={ ("pou_block_instances_ns", name): getattr(factory, name) for name in ["AddBlockInstance", "SetSpecificValues", "AddInstanceConnection", "AddConnectionLink", @@ -2434,7 +2505,7 @@ result = wire.GetConnectedInfos(-1) else: result = wire.GetConnectedInfos(0) - if result != None: + if result is not None: refLocalId, formalParameter = result connections = connection.getconnections() if connections is None or len(connection.getconnections()) <= idx: @@ -2459,7 +2530,7 @@ var_type_obj.setcontent(derived_type) return var_type_obj - def AddEditedElementPouVar(self, tagname, var_type, name,**args): + def AddEditedElementPouVar(self, tagname, var_type, name, **args): if self.Project is not None: words = tagname.split("::") if words[0] in ['P', 'T', 'A']: @@ -2494,7 +2565,7 @@ if pou is not None: pou.removepouVar(type, name) - def AddEditedElementBlock(self, tagname, id, blocktype, blockname = None): + def AddEditedElementBlock(self, tagname, id, blocktype, blockname=None): element = self.GetEditedElement(tagname) if element is not None: block = PLCOpenParser.CreateElement("block", "fbdObjects") @@ -2919,7 +2990,7 @@ SELECTION_CONVERGENCE: "selectionConvergence", SIMULTANEOUS_DIVERGENCE: "simultaneousDivergence", SIMULTANEOUS_CONVERGENCE: "simultaneousConvergence"}.get( - divergence_type), "sfcObjects") + divergence_type), "sfcObjects") divergence.setlocalId(id) element.addinstance(divergence) @@ -3047,7 +3118,7 @@ self.RemoveEditedElementPouVar(tagname, instance.gettypeName(), instance.getinstanceName()) element.removeinstance(id) - def GetEditedResourceVariables(self, tagname, debug = False): + def GetEditedResourceVariables(self, tagname, debug=False): varlist = [] words = tagname.split("::") for var in self.GetConfigurationGlobalVars(words[1], debug): @@ -3070,17 +3141,17 @@ new_task.setname(task["Name"]) if task["Triggering"] == "Interrupt": new_task.setsingle(task["Single"]) -## result = duration_model.match(task["Interval"]).groups() -## if reduce(lambda x, y: x or y != None, result): -## values = [] -## for value in result[:-1]: -## if value != None: -## values.append(int(value)) -## else: -## values.append(0) -## if result[-1] is not None: -## values.append(int(float(result[-1]) * 1000)) -## new_task.setinterval(datetime.time(*values)) +# result = duration_model.match(task["Interval"]).groups() +# if reduce(lambda x, y: x or y != None, result): +# values = [] +# for value in result[:-1]: +# if value != None: +# values.append(int(value)) +# else: +# values.append(0) +# if result[-1] is not None: +# values.append(int(float(result[-1]) * 1000)) +# new_task.setinterval(datetime.time(*values)) if task["Triggering"] == "Cyclic": new_task.setinterval(task["Interval"]) new_task.setpriority(int(task["Priority"])) @@ -3097,7 +3168,7 @@ new_instance.setname(instance["Name"]) new_instance.settypeName(instance["Type"]) - def GetEditedResourceInfos(self, tagname, debug = False): + def GetEditedResourceInfos(self, tagname, debug=False): resource = self.GetEditedElement(tagname, debug) if resource is not None: tasks = resource.gettask() @@ -3114,19 +3185,19 @@ new_task["Single"] = "" interval = task.getinterval() if interval is not None: -## text = "" -## if interval.hour != 0: -## text += "%dh"%interval.hour -## if interval.minute != 0: -## text += "%dm"%interval.minute -## if interval.second != 0: -## text += "%ds"%interval.second -## if interval.microsecond != 0: -## if interval.microsecond % 1000 != 0: -## text += "%.3fms"%(float(interval.microsecond) / 1000) -## else: -## text += "%dms"%(interval.microsecond / 1000) -## new_task["Interval"] = text + # text = "" + # if interval.hour != 0: + # text += "%dh"%interval.hour + # if interval.minute != 0: + # text += "%dm"%interval.minute + # if interval.second != 0: + # text += "%ds"%interval.second + # if interval.microsecond != 0: + # if interval.microsecond % 1000 != 0: + # text += "%.3fms"%(float(interval.microsecond) / 1000) + # else: + # text += "%dms"%(interval.microsecond / 1000) + # new_task["Interval"] = text new_task["Interval"] = interval else: new_task["Interval"] = "" @@ -3166,7 +3237,7 @@ self.CurrentElementEditing = None return error - def SaveXMLFile(self, filepath = None): + def SaveXMLFile(self, filepath=None): if not filepath and self.FilePath == "": return False else: @@ -3183,9 +3254,9 @@ self.SetFilePath(filepath) return True -#------------------------------------------------------------------------------- -# Search in Current Project Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Search in Current Project Functions + # ------------------------------------------------------------------------------- def SearchInProject(self, criteria): return self.Project.Search(criteria) @@ -3203,14 +3274,12 @@ return search_results return [] -#------------------------------------------------------------------------------- -# Current Buffering Management Functions -#------------------------------------------------------------------------------- - - """ - Return a copy of the project - """ + # ------------------------------------------------------------------------------- + # Current Buffering Management Functions + # ------------------------------------------------------------------------------- + def Copy(self, model): + """Return a copy of the project""" return deepcopy(model) def CreateProjectBuffer(self, saved): diff -r c1298e7ffe3a -r 8391c11477f4 PLCGenerator.py --- a/PLCGenerator.py Fri Mar 24 12:07:47 2017 +0000 +++ b/PLCGenerator.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,21 +22,24 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +from types import * +import re from plcopen import PLCOpenParser from plcopen.structures import * -from types import * -import re + # Dictionary associating PLCOpen variable categories to the corresponding # IEC 61131-3 variable categories -varTypeNames = {"localVars" : "VAR", "tempVars" : "VAR_TEMP", "inputVars" : "VAR_INPUT", - "outputVars" : "VAR_OUTPUT", "inOutVars" : "VAR_IN_OUT", "externalVars" : "VAR_EXTERNAL", - "globalVars" : "VAR_GLOBAL", "accessVars" : "VAR_ACCESS"} +varTypeNames = {"localVars": "VAR", "tempVars": "VAR_TEMP", "inputVars": "VAR_INPUT", + "outputVars": "VAR_OUTPUT", "inOutVars": "VAR_IN_OUT", "externalVars": "VAR_EXTERNAL", + "globalVars": "VAR_GLOBAL", "accessVars": "VAR_ACCESS"} # Dictionary associating PLCOpen POU categories to the corresponding # IEC 61131-3 POU categories -pouTypeNames = {"function" : "FUNCTION", "functionBlock" : "FUNCTION_BLOCK", "program" : "PROGRAM"} +pouTypeNames = {"function": "FUNCTION", "functionBlock": "FUNCTION_BLOCK", "program": "PROGRAM"} errorVarTypes = { @@ -45,8 +48,9 @@ "VAR_INOUT": "var_inout", } -# Helper function for reindenting text + def ReIndentText(text, nb_spaces): + """ Helper function for reindenting text """ compute = "" lines = text.splitlines() if len(lines) > 0: @@ -58,15 +62,16 @@ while lines[line_num][spaces] == " ": spaces += 1 indent = "" - for i in xrange(spaces, nb_spaces): + for dummy in xrange(spaces, nb_spaces): indent += " " for line in lines: if line != "": - compute += "%s%s\n"%(indent, line) + compute += "%s%s\n" % (indent, line) else: compute += "\n" return compute + def SortInstances(a, b): ax, ay = int(a.getx()), int(a.gety()) bx, by = int(b.getx()), int(b.gety()) @@ -75,28 +80,29 @@ else: return cmp(ay, by) -# Helper for emulate join on element list + def JoinList(separator, mylist): - if len(mylist) > 0 : + """ Helper for emulate join on element list """ + if len(mylist) > 0: return reduce(lambda x, y: x + separator + y, mylist) - else : + else: return mylist -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Specific exception for PLC generating errors -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class PLCGenException(Exception): pass -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Generator of PLC program -#------------------------------------------------------------------------------- - - -class ProgramGenerator: +# ------------------------------------------------------------------------------- + + +class ProgramGenerator(object): # Create a new PCL program generator def __init__(self, controler, project, errors, warnings): @@ -114,9 +120,9 @@ def ComputeValue(self, value, var_type): base_type = self.Controler.GetBaseType(var_type) if base_type == "STRING" and not value.startswith("'") and not value.endswith("'"): - return "'%s'"%value + return "'%s'" % value elif base_type == "WSTRING" and not value.startswith('"') and not value.endswith('"'): - return "\"%s\""%value + return "\"%s\"" % value return value # Generate a data type from its name @@ -154,16 +160,16 @@ max_value = basetype_content.range.getupper() datatype_def += [(basetype_name, (tagname, "base")), (" (", ()), - ("%s"%min_value, (tagname, "lower")), + ("%s" % min_value, (tagname, "lower")), ("..", ()), - ("%s"%max_value, (tagname, "upper")), - (")",())] + ("%s" % max_value, (tagname, "upper")), + (")", ())] # Data type is an enumerated type elif basetype_content_type == "enum": values = [[(value.getname(), (tagname, "value", i))] for i, value in enumerate( basetype_content.xpath("ppx:values/ppx:value", - namespaces=PLCOpenParser.NSMAP))] + namespaces=PLCOpenParser.NSMAP))] datatype_def += [("(", ())] datatype_def += JoinList([(", ", ())], values) datatype_def += [(")", ())] @@ -178,13 +184,13 @@ # Array derived directly from an elementary type else: basetype_name = base_type_type.upper() - dimensions = [[("%s"%dimension.getlower(), (tagname, "range", i, "lower")), + dimensions = [[("%s" % dimension.getlower(), (tagname, "range", i, "lower")), ("..", ()), - ("%s"%dimension.getupper(), (tagname, "range", i, "upper"))] + ("%s" % dimension.getupper(), (tagname, "range", i, "upper"))] for i, dimension in enumerate(basetype_content.getdimension())] datatype_def += [("ARRAY [", ())] datatype_def += JoinList([(",", ())], dimensions) - datatype_def += [("] OF " , ()), + datatype_def += [("] OF ", ()), (basetype_name, (tagname, "base"))] # Data type is a structure elif basetype_content_type == "struct": @@ -245,18 +251,18 @@ pou = self.Project.getpou(pou_name) pou_type = pou.getpouType() # Verify that POU type exists - if pouTypeNames.has_key(pou_type): + if pou_type in pouTypeNames: # Create a POU program generator pou_program = PouProgramGenerator(self, pou.getname(), pouTypeNames[pou_type], self.Errors, self.Warnings) program = pou_program.GenerateProgram(pou) self.Program += program else: - raise PLCGenException, _("Undefined pou type \"%s\"")%pou_type + raise PLCGenException(_("Undefined pou type \"%s\"") % pou_type) # Generate a POU defined and used in text def GeneratePouProgramInText(self, text): for pou_name in self.PouComputed.keys(): - model = re.compile("(?:^|[^0-9^A-Z])%s(?:$|[^0-9^A-Z])"%pou_name.upper()) + model = re.compile("(?:^|[^0-9^A-Z])%s(?:$|[^0-9^A-Z])" % pou_name.upper()) if model.search(text) is not None: self.GeneratePouProgram(pou_name) @@ -389,16 +395,15 @@ resrce += [(" TASK ", ()), (task.getname(), (tagname, "task", task_number, "name")), ("(", ())] - args = [] single = task.getsingle() # Single argument if exists if single is not None: if len(single) == 0: - msg = _("Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'.").\ - format(a1 = task.getname(), a2 = config_name, a3 = resource.getname()) - raise PLCGenException, msg - - if single[0]=='[' and single[-1]==']' : + raise PLCGenException( + _("Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'."). + format(a1=task.getname(), a2=config_name, a3=resource.getname())) + + if single[0] == '[' and single[-1] == ']': SNGLKW = "MULTI" else: SNGLKW = "SINGLE" @@ -411,19 +416,19 @@ resrce += [("INTERVAL := ", ()), (interval, (tagname, "task", task_number, "interval")), (",", ())] -## resrce += [("INTERVAL := t#", ())] -## if interval.hour != 0: -## resrce += [("%dh"%interval.hour, (tagname, "task", task_number, "interval", "hour"))] -## if interval.minute != 0: -## resrce += [("%dm"%interval.minute, (tagname, "task", task_number, "interval", "minute"))] -## if interval.second != 0: -## resrce += [("%ds"%interval.second, (tagname, "task", task_number, "interval", "second"))] -## if interval.microsecond != 0: -## resrce += [("%dms"%(interval.microsecond / 1000), (tagname, "task", task_number, "interval", "millisecond"))] -## resrce += [(",", ())] +# resrce += [("INTERVAL := t#", ())] +# if interval.hour != 0: +# resrce += [("%dh"%interval.hour, (tagname, "task", task_number, "interval", "hour"))] +# if interval.minute != 0: +# resrce += [("%dm"%interval.minute, (tagname, "task", task_number, "interval", "minute"))] +# if interval.second != 0: +# resrce += [("%ds"%interval.second, (tagname, "task", task_number, "interval", "second"))] +# if interval.microsecond != 0: +# resrce += [("%dms"%(interval.microsecond / 1000), (tagname, "task", task_number, "interval", "millisecond"))] +# resrce += [(",", ())] # Priority argument resrce += [("PRIORITY := ", ()), - ("%d"%task.getpriority(), (tagname, "task", task_number, "priority")), + ("%d" % task.getpriority(), (tagname, "task", task_number, "priority")), (");\n", ())] task_number += 1 instance_number = 0 @@ -441,10 +446,10 @@ # Generate any program assign to no task for instance in resource.getpouInstance(): resrce += [(" PROGRAM ", ()), - (instance.getname(), (tagname, "instance", instance_number, "name")), - (" : ", ()), - (instance.gettypeName(), (tagname, "instance", instance_number, "type")), - (";\n", ())] + (instance.getname(), (tagname, "instance", instance_number, "name")), + (" : ", ()), + (instance.gettypeName(), (tagname, "instance", instance_number, "type")), + (";\n", ())] instance_number += 1 resrce += [(" END_RESOURCE\n", ())] return resrce @@ -477,9 +482,9 @@ return self.Program -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Generator of POU programs -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- [ConnectorClass, ContinuationClass, ActionBlockClass] = [ PLCOpenParser.GetElementClass(instance_name, "commonObjects") @@ -493,14 +498,19 @@ [StepClass, TransitionClass, JumpStepClass, SelectionConvergenceClass, SelectionDivergenceClass, SimultaneousConvergenceClass, SimultaneousDivergenceClass] = [ - PLCOpenParser.GetElementClass(instance_name, "sfcObjects") - for instance_name in ["step", "transition", "jumpStep", - "selectionConvergence", "selectionDivergence", - "simultaneousConvergence", "simultaneousDivergence"]] + PLCOpenParser.GetElementClass(instance_name, "sfcObjects") + for instance_name in ["step", + "transition", + "jumpStep", + "selectionConvergence", + "selectionDivergence", + "simultaneousConvergence", + "simultaneousDivergence"]] TransitionObjClass = PLCOpenParser.GetElementClass("transition", "transitions") ActionObjClass = PLCOpenParser.GetElementClass("action", "actions") -class PouProgramGenerator: + +class PouProgramGenerator(object): # Create a new POU program generator def __init__(self, parent, name, type, errors, warnings): @@ -517,7 +527,7 @@ self.ComputedConnectors = {} self.ConnectionTypes = {} self.RelatedConnections = [] - self.SFCNetworks = {"Steps":{}, "Transitions":{}, "Actions":{}} + self.SFCNetworks = {"Steps": {}, "Transitions": {}, "Actions": {}} self.SFCComputedBlocks = [] self.ActionNumber = 0 self.Program = [] @@ -541,8 +551,8 @@ # Test if a variable has already been defined def IsAlreadyDefined(self, name): - for list_type, option, located, vars in self.Interface: - for var_type, var_name, var_address, var_initial in vars: + for _list_type, _option, _located, vars in self.Interface: + for _var_type, var_name, _var_address, _var_initial in vars: if name == var_name: return True return False @@ -553,8 +563,8 @@ current_type = None if len(parts) > 0: name = parts.pop(0) - for list_type, option, located, vars in self.Interface: - for var_type, var_name, var_address, var_initial in vars: + for _list_type, _option, _located, vars in self.Interface: + for var_type, var_name, _var_address, _var_initial in vars: if name == var_name: current_type = var_type break @@ -563,7 +573,7 @@ if blocktype is not None: name = parts.pop(0) current_type = None - for var_name, var_type, var_modifier in blocktype["inputs"] + blocktype["outputs"]: + for var_name, var_type, _var_modifier in blocktype["inputs"] + blocktype["outputs"]: if var_name == name: current_type = var_type break @@ -590,7 +600,7 @@ parameter = link.getformalParameter() instance = body.getcontentInstance(link.getrefLocalId()) if isinstance(instance, (InVariableClass, InOutVariableClass, - ContinuationClass, ContactClass, CoilClass)): + ContinuationClass, ContactClass, CoilClass)): return instance.connectionPointOut elif isinstance(instance, BlockClass): outputvariables = instance.outputVariables.getvariable() @@ -629,11 +639,6 @@ def ComputeInterface(self, pou): interface = pou.getinterface() if interface is not None: - body = pou.getbody() - if isinstance(body, ListType): - body = body[0] - body_content = body.getcontent() - body_type = body_content.getLocalTag() if self.Type == "FUNCTION": returntype_content = interface.getreturnType()[0] returntype_content_type = returntype_content.getLocalTag() @@ -691,14 +696,15 @@ self.Interface.append((varTypeNames[varlist_type], option, True, located)) LITERAL_TYPES = { - "T": "TIME", - "D": "DATE", + "T": "TIME", + "D": "DATE", "TOD": "TIME_OF_DAY", - "DT": "DATE_AND_TIME", - "2": None, - "8": None, - "16": None, + "DT": "DATE_AND_TIME", + "2": None, + "8": None, + "16": None, } + def ComputeConnectionTypes(self, pou): body = pou.getbody() if isinstance(body, ListType): @@ -712,8 +718,7 @@ InOutVariableClass)): expression = instance.getexpression() var_type = self.GetVariableType(expression) - if (isinstance(pou, TransitionObjClass) - and expression == pou.getname()): + if isinstance(pou, TransitionObjClass) and expression == pou.getname(): var_type = "BOOL" elif (not isinstance(pou, (TransitionObjClass, ActionObjClass)) and pou.getpouType() == "function" and expression == pou.getname()): @@ -740,7 +745,7 @@ if isinstance(instance, (OutVariableClass, InOutVariableClass)): self.ConnectionTypes[instance.connectionPointIn] = var_type connected = self.GetConnectedConnector(instance.connectionPointIn, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = var_type elif isinstance(instance, (ContactClass, CoilClass)): @@ -749,7 +754,7 @@ self.ConnectionTypes[instance.connectionPointIn] = "BOOL" for link in instance.connectionPointIn.getconnections(): connected = self.GetLinkedConnector(link, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = "BOOL" elif isinstance(instance, LeftPowerRailClass): @@ -761,7 +766,7 @@ self.ConnectionTypes[connection] = "BOOL" for link in connection.getconnections(): connected = self.GetLinkedConnector(link, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = "BOOL" elif isinstance(instance, TransitionClass): @@ -770,10 +775,12 @@ self.ConnectionTypes[content["value"]] = "BOOL" connections = content["value"].getconnections() if not connections: - raise PLCGenException, _("SFC transition in POU \"%s\" must be connected.") % self.Name - for link in connections: + raise PLCGenException( + _("SFC transition in POU \"%s\" must be connected.") % self.Name) + + for link in connections: connected = self.GetLinkedConnector(link, body) - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for related in self.ExtractRelatedConnections(connected): self.ConnectionTypes[related] = "BOOL" elif isinstance(instance, ContinuationClass): @@ -783,8 +790,9 @@ for element in body.getcontentInstances(): if isinstance(element, ConnectorClass) and element.getname() == name: if connector is not None: - msg = _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) connector = element if connector is not None: undefined = [instance.connectionPointOut, connector.connectionPointIn] @@ -793,7 +801,7 @@ undefined.append(connected) related = [] for connection in undefined: - if self.ConnectionTypes.has_key(connection): + if connection in self.ConnectionTypes: var_type = self.ConnectionTypes[connection] else: related.extend(self.ExtractRelatedConnections(connection)) @@ -803,8 +811,10 @@ for connection in related: self.ConnectionTypes[connection] = var_type else: - msg = _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) + elif isinstance(instance, BlockClass): block_infos = self.GetBlockType(instance.gettypeName(), "undefined") if block_infos is not None: @@ -826,7 +836,8 @@ if block_infos is not None: self.ComputeBlockInputTypes(instance, block_infos, body) else: - raise PLCGenException, _("No informations found for \"%s\" block")%(instance.gettypeName()) + raise PLCGenException( + _("No informations found for \"%s\" block") % (instance.gettypeName())) if body_type == "SFC": previous_tagname = self.TagName for action in pou.getactionList(): @@ -845,13 +856,13 @@ for connection in self.ExtractRelatedConnections(variable.connectionPointOut): self.ConnectionTypes[connection] = "BOOL" else: - for oname, otype, oqualifier in block_infos["outputs"]: + for oname, otype, _oqualifier in block_infos["outputs"]: if output_name == oname: if otype.startswith("ANY"): - if not undefined.has_key(otype): + if otype not in undefined: undefined[otype] = [] undefined[otype].append(variable.connectionPointOut) - elif not self.ConnectionTypes.has_key(variable.connectionPointOut): + elif variable.connectionPointOut not in self.ConnectionTypes: for connection in self.ExtractRelatedConnections(variable.connectionPointOut): self.ConnectionTypes[connection] = otype for variable in instance.inputVariables.getvariable(): @@ -860,18 +871,18 @@ for connection in self.ExtractRelatedConnections(variable.connectionPointIn): self.ConnectionTypes[connection] = "BOOL" else: - for iname, itype, iqualifier in block_infos["inputs"]: + for iname, itype, _iqualifier in block_infos["inputs"]: if input_name == iname: connected = self.GetConnectedConnector(variable.connectionPointIn, body) if itype.startswith("ANY"): - if not undefined.has_key(itype): + if itype not in undefined: undefined[itype] = [] undefined[itype].append(variable.connectionPointIn) if connected is not None: undefined[itype].append(connected) else: self.ConnectionTypes[variable.connectionPointIn] = itype - if connected is not None and not self.ConnectionTypes.has_key(connected): + if connected is not None and connected not in self.ConnectionTypes: for connection in self.ExtractRelatedConnections(connected): self.ConnectionTypes[connection] = itype for var_type, connections in undefined.items(): @@ -894,11 +905,11 @@ body = body[0] body_content = body.getcontent() body_type = body_content.getLocalTag() - if body_type in ["IL","ST"]: + if body_type in ["IL", "ST"]: text = body_content.getanyText() self.ParentGenerator.GeneratePouProgramInText(text.upper()) self.Program = [(ReIndentText(text, len(self.CurrentIndent)), - (self.TagName, "body", len(self.CurrentIndent)))] + (self.TagName, "body", len(self.CurrentIndent)))] elif body_type == "SFC": self.IndentRight() for instance in body.getcontentInstances(): @@ -912,7 +923,7 @@ self.GenerateSFCJump(instance, pou) if len(self.InitialSteps) > 0 and len(self.SFCComputedBlocks) > 0: action_name = "COMPUTE_FUNCTION_BLOCKS" - action_infos = {"qualifier" : "S", "content" : action_name} + action_infos = {"qualifier": "S", "content": action_name} self.SFCNetworks["Steps"][self.InitialSteps[0]]["actions"].append(action_infos) self.SFCNetworks["Actions"][action_name] = (self.SFCComputedBlocks, ()) self.Program = [] @@ -920,7 +931,7 @@ for initialstep in self.InitialSteps: self.ComputeSFCStep(initialstep) else: - otherInstances = {"outVariables&coils" : [], "blocks" : [], "connectors" : []} + otherInstances = {"outVariables&coils": [], "blocks": [], "connectors": []} orderedInstances = [] for instance in body.getcontentInstances(): if isinstance(instance, (OutVariableClass, InOutVariableClass, BlockClass)): @@ -958,11 +969,13 @@ if block_infos is None: block_infos = self.GetBlockType(block_type) if block_infos is None: - raise PLCGenException, _("Undefined block type \"{a1}\" in \"{a2}\" POU").format(a1 = block_type, a2 = self.Name) + raise PLCGenException( + _("Undefined block type \"{a1}\" in \"{a2}\" POU"). + format(a1=block_type, a2=self.Name)) try: self.GenerateBlock(instance, block_infos, body, None) except ValueError, e: - raise PLCGenException, e.message + raise PLCGenException(e.message) elif isinstance(instance, ConnectorClass): connector = instance.getname() if self.ComputedConnectors.get(connector, None): @@ -986,7 +999,7 @@ uncomputed_index = range(len(paths)) factorized_paths = [] for num, path in enumerate(paths): - if type(path) == ListType: + if isinstance(path, ListType): if len(path) > 1: str_path = str(path[-1:]) same_paths.setdefault(str_path, []) @@ -1009,7 +1022,23 @@ return factorized_paths def GenerateBlock(self, block, block_infos, body, link, order=False, to_inout=False): - body_type = body.getcontent().getLocalTag() + + def _GetBlockName(name, type): + """function returns name of function or function block instance""" + if name: + # function blocks + blockname = "{a1}({a2})".format(a1=name, a2=type) + else: + # functions + blockname = type + return blockname + + def _RaiseUnconnectedInOutError(name, type, parameter, place): + blockname = _GetBlockName(name, type) + raise ValueError( + _("InOut variable {a1} in block {a2} in POU {a3} must be connected."). + format(a1=parameter, a2=blockname, a3=place)) + name = block.getinstanceName() type = block.gettypeName() executionOrderId = block.getexecutionOrderId() @@ -1031,7 +1060,7 @@ [(input_name, None) for input_name in input_names]) for variable in input_variables: parameter = variable.getformalParameter() - if input_connected.has_key(parameter): + if parameter in input_connected: input_connected[parameter] = variable if input_connected["EN"] is None: input_connected.pop("EN") @@ -1053,10 +1082,12 @@ if connections is not None: if parameter != "EN": one_input_connected = True - if inout_variables.has_key(parameter): + if parameter in inout_variables: expression = self.ComputeExpression(body, connections, executionOrderId > 0, True) if expression is not None: - inout_variables[parameter] = value + inout_variables[parameter] = expression + else: + _RaiseUnconnectedInOutError(name, type, parameter, self.Name) else: expression = self.ComputeExpression(body, connections, executionOrderId > 0) if expression is not None: @@ -1073,11 +1104,11 @@ if one_input_connected: for i, variable in enumerate(output_variables): parameter = variable.getformalParameter() - if not inout_variables.has_key(parameter) and parameter in output_names + ["", "ENO"]: + if parameter not in inout_variables and parameter in output_names + ["", "ENO"]: if variable.getformalParameter() == "": - variable_name = "%s%d"%(type, block.getlocalId()) + variable_name = "%s%d" % (type, block.getlocalId()) else: - variable_name = "%s%d_%s"%(type, block.getlocalId(), parameter) + variable_name = "%s%d_%s" % (type, block.getlocalId(), parameter) if self.Interface[-1][0] != "VAR" or self.Interface[-1][1] is not None or self.Interface[-1][2]: self.Interface.append(("VAR", None, False, [])) if variable.connectionPointOut in self.ConnectionTypes: @@ -1086,7 +1117,7 @@ self.Interface[-1][3].append(("ANY", variable_name, None, None)) if len(output_variables) > 1 and parameter not in ["", "OUT"]: vars.append([(parameter, (self.TagName, "block", block.getlocalId(), "output", i)), - (" => %s"%variable_name, ())]) + (" => %s" % variable_name, ())]) else: output_info = (self.TagName, "block", block.getlocalId(), "output", i) output_name = variable_name @@ -1098,7 +1129,7 @@ self.Program += JoinList([(", ", ())], vars) self.Program += [(");\n", ())] else: - msg = _("\"{a1}\" function cancelled in \"{a2}\" POU: No input connected").format(a1 = type, a2 = self.TagName.split("::")[-1]) + msg = _("\"{a1}\" function cancelled in \"{a2}\" POU: No input connected").format(a1=type, a2=self.TagName.split("::")[-1]) self.Warnings.append(msg) elif block_infos["type"] == "functionBlock": if not self.ComputedBlocks.get(block, False) and not order: @@ -1116,10 +1147,12 @@ input_info = (self.TagName, "block", block.getlocalId(), "input", input_idx) connections = variable.connectionPointIn.getconnections() if connections is not None: - expression = self.ComputeExpression(body, connections, executionOrderId > 0, inout_variables.has_key(parameter)) + expression = self.ComputeExpression(body, connections, executionOrderId > 0, parameter in inout_variables) if expression is not None: vars.append([(parameter, input_info), (" := ", ())] + self.ExtractModifier(variable, expression, input_info)) + elif parameter in inout_variables: + _RaiseUnconnectedInOutError(name, type, parameter, self.Name) self.Program += [(self.CurrentIndent, ()), (name, (self.TagName, "block", block.getlocalId(), "name")), ("(", ())] @@ -1145,9 +1178,9 @@ else: for i, variable in enumerate(output_variables): blockPointx, blockPointy = variable.connectionPointOut.getrelPositionXY() - if (connectionPoint is None or - block.getx() + blockPointx == connectionPoint.getx() and - block.gety() + blockPointy == connectionPoint.gety()): + if connectionPoint is None or \ + block.getx() + blockPointx == connectionPoint.getx() and \ + block.gety() + blockPointy == connectionPoint.gety(): output_variable = variable output_parameter = variable.getformalParameter() output_idx = i @@ -1155,21 +1188,27 @@ if output_variable is not None: if block_infos["type"] == "function": output_info = (self.TagName, "block", block.getlocalId(), "output", output_idx) - if inout_variables.has_key(output_parameter): - output_value = inout_variables[output_parameter] + if output_parameter in inout_variables: + for variable in input_variables: + if variable.getformalParameter() == output_parameter: + connections = variable.connectionPointIn.getconnections() + if connections is not None: + expression = self.ComputeExpression( + body, connections, executionOrderId > 0, True) + output_value = expression + break else: if output_parameter == "": - output_name = "%s%d"%(type, block.getlocalId()) + output_name = "%s%d" % (type, block.getlocalId()) else: - output_name = "%s%d_%s"%(type, block.getlocalId(), output_parameter) + output_name = "%s%d_%s" % (type, block.getlocalId(), output_parameter) output_value = [(output_name, output_info)] return self.ExtractModifier(output_variable, output_value, output_info) - if block_infos["type"] == "functionBlock": output_info = (self.TagName, "block", block.getlocalId(), "output", output_idx) - output_name = self.ExtractModifier(output_variable, [("%s.%s"%(name, output_parameter), output_info)], output_info) + output_name = self.ExtractModifier(output_variable, [("%s.%s" % (name, output_parameter), output_info)], output_info) if to_inout: - variable_name = "%s_%s"%(name, output_parameter) + variable_name = "%s_%s" % (name, output_parameter) if not self.IsAlreadyDefined(variable_name): if self.Interface[-1][0] != "VAR" or self.Interface[-1][1] is not None or self.Interface[-1][2]: self.Interface.append(("VAR", None, False, [])) @@ -1179,7 +1218,7 @@ else: self.Interface[-1][3].append(("ANY", variable_name, None, None)) self.Program += [(self.CurrentIndent, ()), - ("%s := "%variable_name, ())] + ("%s := " % variable_name, ())] self.Program += output_name self.Program += [(";\n", ())] return [(variable_name, ())] @@ -1187,15 +1226,12 @@ if link is not None: if output_parameter is None: output_parameter = "" - if name: - blockname = "{a1}({a2})".format(a1 = name, a2 = type) - else: - blockname = type - msg = _("No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken").\ - format(a1 = output_parameter, a2 = blockname, a3 = self.Name) - raise ValueError, msg - - def GeneratePaths(self, connections, body, order = False, to_inout = False): + blockname = _GetBlockName(name, type) + raise ValueError( + _("No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken"). + format(a1=output_parameter, a2=blockname, a3=self.Name)) + + def GeneratePaths(self, connections, body, order=False, to_inout=False): paths = [] for connection in connections: localId = connection.getrefLocalId() @@ -1211,24 +1247,26 @@ if block_infos is None: block_infos = self.GetBlockType(block_type) if block_infos is None: - msg = _("Undefined block type \"{a1}\" in \"{a2}\" POU").format(a1 = block_type, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("Undefined block type \"{a1}\" in \"{a2}\" POU"). + format(a1=block_type, a2=self.Name)) try: paths.append(str(self.GenerateBlock(next, block_infos, body, connection, order, to_inout))) except ValueError, e: - raise PLCGenException, e.message + raise PLCGenException(e.message) elif isinstance(next, ContinuationClass): name = next.getname() computed_value = self.ComputedConnectors.get(name, None) - if computed_value != None: + if computed_value is not None: paths.append(str(computed_value)) else: connector = None for instance in body.getcontentInstances(): if isinstance(instance, ConnectorClass) and instance.getname() == name: if connector is not None: - msg = _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) connector = instance if connector is not None: connections = connector.connectionPointIn.getconnections() @@ -1238,8 +1276,9 @@ self.ComputedConnectors[name] = expression paths.append(str(expression)) else: - msg = _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU").format(a1 = name, a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU"). + format(a1=name, a2=self.Name)) elif isinstance(next, ContactClass): contact_info = (self.TagName, "contact", next.getlocalId()) variable = str(self.ExtractModifier(next, [(next.getvariable(), contact_info + ("reference",))], contact_info)) @@ -1250,7 +1289,7 @@ paths.append([variable, tuple(factorized_paths)]) else: paths.append([variable] + factorized_paths) - elif type(result[0]) == ListType: + elif isinstance(result[0], ListType): paths.append([variable] + result[0]) elif result[0] is not None: paths.append([variable, result[0]]) @@ -1260,8 +1299,8 @@ paths.append(str(self.GeneratePaths(next.connectionPointIn.getconnections(), body, order))) return paths - def ComputePaths(self, paths, first = False): - if type(paths) == TupleType: + def ComputePaths(self, paths, first=False): + if isinstance(paths, TupleType): if None in paths: return [("TRUE", ())] else: @@ -1270,7 +1309,7 @@ return JoinList([(" OR ", ())], vars) else: return [("(", ())] + JoinList([(" OR ", ())], vars) + [(")", ())] - elif type(paths) == ListType: + elif isinstance(paths, ListType): vars = [self.ComputePaths(path) for path in paths] return JoinList([(" AND ", ())], vars) elif paths is None: @@ -1278,7 +1317,7 @@ else: return eval(paths) - def ComputeExpression(self, body, connections, order = False, to_inout = False): + def ComputeExpression(self, body, connections, order=False, to_inout=False): paths = self.GeneratePaths(connections, body, order, to_inout) if len(paths) == 0: return None @@ -1315,15 +1354,15 @@ if self.Interface[-1][0] != "VAR" or self.Interface[-1][1] is not None or self.Interface[-1][2]: self.Interface.append(("VAR", None, False, [])) i = 1 - name = "%s%d"%(edge, i) + name = "%s%d" % (edge, i) while self.IsAlreadyDefined(name): i += 1 - name = "%s%d"%(edge, i) + name = "%s%d" % (edge, i) self.Interface[-1][3].append((edge, name, None, None)) self.Program += [(self.CurrentIndent, ()), (name, var_info), ("(CLK := ", ())] self.Program += expression self.Program += [(");\n", ())] - return [("%s.Q"%name, var_info)] + return [("%s.Q" % name, var_info)] def ExtractDivergenceInput(self, divergence, pou): connectionPointIn = divergence.getconnectionPointIn() @@ -1354,10 +1393,10 @@ if step_name not in self.SFCNetworks["Steps"].keys(): if step.getinitialStep(): self.InitialSteps.append(step_name) - step_infos = {"id" : step.getlocalId(), - "initial" : step.getinitialStep(), - "transitions" : [], - "actions" : []} + step_infos = {"id": step.getlocalId(), + "initial": step.getinitialStep(), + "transitions": [], + "actions": []} self.SFCNetworks["Steps"][step_name] = step_infos if step.connectionPointIn is not None: instances = [] @@ -1389,8 +1428,10 @@ jump_target = jump.gettargetName() if not pou.hasstep(jump_target): pname = pou.getname() - msg = _("SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"").format( a1 = pname, a2 = jump_target) - raise PLCGenException, msg + raise PLCGenException( + _("SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\""). + format(a1=pname, a2=jump_target)) + if jump.connectionPointIn is not None: instances = [] connections = jump.connectionPointIn.getconnections() @@ -1430,10 +1471,10 @@ if step_name in self.SFCNetworks["Steps"].keys(): actions = actionBlock.getactions() for i, action in enumerate(actions): - action_infos = {"id" : actionBlock.getlocalId(), - "qualifier" : action["qualifier"], - "content" : action["value"], - "num" : i} + action_infos = {"id": actionBlock.getlocalId(), + "qualifier": action["qualifier"], + "content": action["value"], + "num": i} if "duration" in action: action_infos["duration"] = action["duration"] if "indicator" in action: @@ -1441,9 +1482,12 @@ if action["type"] == "reference": self.GenerateSFCAction(action["value"], pou) else: - action_name = "%s_INLINE%d"%(step_name.upper(), self.GetActionNumber()) - self.SFCNetworks["Actions"][action_name] = ([(self.CurrentIndent, ()), - (action["value"], (self.TagName, "action_block", action_infos["id"], "action", i, "inline")), + action_name = "%s_INLINE%d" % (step_name.upper(), self.GetActionNumber()) + self.SFCNetworks["Actions"][action_name] = ([ + (self.CurrentIndent, ()), + (action["value"], ( + self.TagName, "action_block", action_infos["id"], + "action", i, "inline")), ("\n", ())], ()) action_infos["content"] = action_name self.SFCNetworks["Steps"][step_name]["actions"].append(action_infos) @@ -1480,15 +1524,15 @@ steps.extend(self.ExtractConvergenceInputs(step, pou)) elif isinstance(instance, SimultaneousConvergenceClass): steps.extend(self.ExtractConvergenceInputs(instance, pou)) - transition_infos = {"id" : transition.getlocalId(), + transition_infos = {"id": transition.getlocalId(), "priority": transition.getpriority(), - "from": [], - "to" : [], - "content": []} + "from": [], + "to": [], + "content": []} self.SFCNetworks["Transitions"][transition] = transition_infos transitionValues = transition.getconditionContent() if transitionValues["type"] == "inline": - transition_infos["content"] = [("\n%s:= "%self.CurrentIndent, ()), + transition_infos["content"] = [("\n%s:= " % self.CurrentIndent, ()), (transitionValues["value"], (self.TagName, "transition", transition.getlocalId(), "inline")), (";\n", ())] elif transitionValues["type"] == "reference": @@ -1505,17 +1549,19 @@ (ReIndentText(transitionBody.getcontent().getanyText(), len(self.CurrentIndent)), (self.TagName, "body", len(self.CurrentIndent)))] else: for instance in transitionBody.getcontentInstances(): - if isinstance(instance, OutVariableClass) and instance.getexpression() == transitionValues["value"]\ - or isinstance(instance, CoilClass) and instance.getvariable() == transitionValues["value"]: + if isinstance(instance, OutVariableClass) and instance.getexpression() == transitionValues["value"] or \ + isinstance(instance, CoilClass) and instance.getvariable() == transitionValues["value"]: connections = instance.connectionPointIn.getconnections() if connections is not None: expression = self.ComputeExpression(transitionBody, connections) if expression is not None: - transition_infos["content"] = [("\n%s:= "%self.CurrentIndent, ())] + expression + [(";\n", ())] + transition_infos["content"] = [("\n%s:= " % self.CurrentIndent, ())] + expression + [(";\n", ())] self.SFCComputedBlocks += self.Program self.Program = [] - if not transition_infos.has_key("content"): - raise PLCGenException, _("Transition \"%s\" body must contain an output variable or coil referring to its name") % transitionValues["value"] + if "content" not in transition_infos: + raise PLCGenException( + _("Transition \"%s\" body must contain an output variable or coil referring to its name") + % transitionValues["value"]) self.TagName = previous_tagname elif transitionValues["type"] == "connection": body = pou.getbody() @@ -1525,7 +1571,7 @@ if connections is not None: expression = self.ComputeExpression(body, connections) if expression is not None: - transition_infos["content"] = [("\n%s:= "%self.CurrentIndent, ())] + expression + [(";\n", ())] + transition_infos["content"] = [("\n%s:= " % self.CurrentIndent, ())] + expression + [(";\n", ())] self.SFCComputedBlocks += self.Program self.Program = [] for step in steps: @@ -1564,7 +1610,7 @@ (action_infos["indicator"], action_info + ("indicator",))] self.Program += [(");\n", ())] self.IndentLeft() - self.Program += [("%sEND_STEP\n\n"%self.CurrentIndent, ())] + self.Program += [("%sEND_STEP\n\n" % self.CurrentIndent, ())] for action in actions: self.ComputeSFCAction(action) for transition in step_infos["transitions"]: @@ -1573,19 +1619,19 @@ def ComputeSFCAction(self, action_name): if action_name in self.SFCNetworks["Actions"].keys(): action_content, action_info = self.SFCNetworks["Actions"].pop(action_name) - self.Program += [("%sACTION "%self.CurrentIndent, ()), + self.Program += [("%sACTION " % self.CurrentIndent, ()), (action_name, action_info), (":\n", ())] self.Program += action_content - self.Program += [("%sEND_ACTION\n\n"%self.CurrentIndent, ())] + self.Program += [("%sEND_ACTION\n\n" % self.CurrentIndent, ())] def ComputeSFCTransition(self, transition): if transition in self.SFCNetworks["Transitions"].keys(): transition_infos = self.SFCNetworks["Transitions"].pop(transition) - self.Program += [("%sTRANSITION"%self.CurrentIndent, ())] - if transition_infos["priority"] != None: + self.Program += [("%sTRANSITION" % self.CurrentIndent, ())] + if transition_infos["priority"] is not None: self.Program += [(" (PRIORITY := ", ()), - ("%d"%transition_infos["priority"], (self.TagName, "transition", transition_infos["id"], "priority")), + ("%d" % transition_infos["priority"], (self.TagName, "transition", transition_infos["id"], "priority")), (")", ())] self.Program += [(" FROM ", ())] if len(transition_infos["from"]) > 1: @@ -1595,9 +1641,9 @@ elif len(transition_infos["from"]) == 1: self.Program += transition_infos["from"][0] else: - msg = _("Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU").\ - format(a1 = transition_infos["content"], a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU"). + format(a1=transition_infos["content"], a2=self.Name)) self.Program += [(" TO ", ())] if len(transition_infos["to"]) > 1: self.Program += [("(", ())] @@ -1606,12 +1652,12 @@ elif len(transition_infos["to"]) == 1: self.Program += transition_infos["to"][0] else: - msg = _("Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU").\ - format(a1 = transition_infos["content"], a2 = self.Name) - raise PLCGenException, msg + raise PLCGenException( + _("Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU"). + format(a1=transition_infos["content"], a2=self.Name)) self.Program += transition_infos["content"] - self.Program += [("%sEND_TRANSITION\n\n"%self.CurrentIndent, ())] - for [(step_name, step_infos)] in transition_infos["to"]: + self.Program += [("%sEND_TRANSITION\n\n" % self.CurrentIndent, ())] + for [(step_name, _step_infos)] in transition_infos["to"]: self.ComputeSFCStep(step_name) def GenerateProgram(self, pou): @@ -1619,35 +1665,35 @@ self.ComputeConnectionTypes(pou) self.ComputeProgram(pou) - program = [("%s "%self.Type, ()), + program = [("%s " % self.Type, ()), (self.Name, (self.TagName, "name"))] if self.ReturnType is not None: program += [(" : ", ()), (self.ReturnType, (self.TagName, "return"))] program += [("\n", ())] if len(self.Interface) == 0: - raise PLCGenException, _("No variable defined in \"%s\" POU")%self.Name - if len(self.Program) == 0 : - raise PLCGenException, _("No body defined in \"%s\" POU")%self.Name + raise PLCGenException(_("No variable defined in \"%s\" POU") % self.Name) + if len(self.Program) == 0: + raise PLCGenException(_("No body defined in \"%s\" POU") % self.Name) var_number = 0 - for list_type, option, located, variables in self.Interface: + for list_type, option, _located, variables in self.Interface: variable_type = errorVarTypes.get(list_type, "var_local") - program += [(" %s"%list_type, ())] + program += [(" %s" % list_type, ())] if option is not None: - program += [(" %s"%option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))] + program += [(" %s" % option, (self.TagName, variable_type, (var_number, var_number + len(variables)), option.lower()))] program += [("\n", ())] for var_type, var_name, var_address, var_initial in variables: program += [(" ", ())] if var_name: program += [(var_name, (self.TagName, variable_type, var_number, "name")), (" ", ())] - if var_address != None: + if var_address is not None: program += [("AT ", ()), (var_address, (self.TagName, variable_type, var_number, "location")), (" ", ())] program += [(": ", ()), (var_type, (self.TagName, variable_type, var_number, "type"))] - if var_initial != None: + if var_initial is not None: program += [(" := ", ()), (self.ParentGenerator.ComputeValue(var_initial, var_type), (self.TagName, variable_type, var_number, "initial value"))] program += [(";\n", ())] @@ -1655,11 +1701,11 @@ program += [(" END_VAR\n", ())] program += [("\n", ())] program += self.Program - program += [("END_%s\n\n"%self.Type, ())] + program += [("END_%s\n\n" % self.Type, ())] return program + def GenerateCurrentProgram(controler, project, errors, warnings): generator = ProgramGenerator(controler, project, errors, warnings) generator.GenerateProgram() return generator.GetGeneratedProgram() - diff -r c1298e7ffe3a -r 8391c11477f4 PLCOpenEditor.py --- a/PLCOpenEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/PLCOpenEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,76 +23,56 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +from __future__ import print_function +import os +import sys +import getopt + import wx -import os, sys, platform, time, traceback, getopt + import version - -beremiz_dir = os.path.dirname(os.path.realpath(__file__)) - -__version__ = "$Revision: 1.130 $" - -if __name__ == '__main__': - # Usage message displayed when help request or when error detected in - # command line - def usage(): - print "\nUsage of PLCOpenEditor.py :" - print "\n %s [Filepath]\n"%sys.argv[0] - - # Parse options given to PLCOpenEditor in command line - try: - opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) - except getopt.GetoptError: - # print help information and exit: - usage() - sys.exit(2) - - # Extract if help has been requested - for o, a in opts: - if o in ("-h", "--help"): - usage() - sys.exit() - - # Extract the optional filename to open - fileOpen = None - if len(args) > 1: - usage() - sys.exit() - elif len(args) == 1: - fileOpen = args[0] - - # Create wxApp (Need to create App before internationalization because of - # Windows) - if wx.VERSION >= (3, 0, 0): - app = wx.App() - else: - app = wx.PySimpleApp() - - - from util.misc import InstallLocalRessources - InstallLocalRessources(beremiz_dir) - -from docutil import * +import util.paths as paths +import util.ExceptionHandler +from util.misc import InstallLocalRessources +from docutil.docpdf import open_pdf from IDEFrame import IDEFrame, AppendMenu -from IDEFrame import TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, POUINSTANCEVARIABLESPANEL, LIBRARYTREE, PAGETITLES -from IDEFrame import EncodeFileSystemPath, DecodeFileSystemPath +from IDEFrame import \ + TITLE, \ + EDITORTOOLBAR, \ + FILEMENU, \ + EDITMENU, \ + DISPLAYMENU, \ + PROJECTTREE, \ + POUINSTANCEVARIABLESPANEL, \ + LIBRARYTREE, \ + PAGETITLES, \ + DecodeFileSystemPath from editors.Viewer import Viewer from PLCControler import PLCControler from dialogs import ProjectDialog from dialogs.AboutDialog import ShowAboutDialog -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # PLCOpenEditor Main Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Define PLCOpenEditor FileMenu extra items id -[ID_PLCOPENEDITORFILEMENUGENERATE, +[ + ID_PLCOPENEDITORFILEMENUGENERATE, ] = [wx.NewId() for _init_coll_FileMenu_Items in range(1)] + +beremiz_dir = paths.AbsDir(__file__) + + class PLCOpenEditor(IDEFrame): # Compatibility function for wx versions < 2.6 if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -99,33 +80,33 @@ def _init_coll_FileMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_NEW, - kind=wx.ITEM_NORMAL, text=_(u'New') +'\tCTRL+N') + kind=wx.ITEM_NORMAL, text=_(u'New') + '\tCTRL+N') AppendMenu(parent, help='', id=wx.ID_OPEN, - kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') + kind=wx.ITEM_NORMAL, text=_(u'Open') + '\tCTRL+O') AppendMenu(parent, help='', id=wx.ID_CLOSE, - kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') + kind=wx.ITEM_NORMAL, text=_(u'Close Tab') + '\tCTRL+W') AppendMenu(parent, help='', id=wx.ID_CLOSE_ALL, - kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') + kind=wx.ITEM_NORMAL, text=_(u'Close Project') + '\tCTRL+SHIFT+W') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_SAVE, - kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') + kind=wx.ITEM_NORMAL, text=_(u'Save') + '\tCTRL+S') AppendMenu(parent, help='', id=wx.ID_SAVEAS, - kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S') + kind=wx.ITEM_NORMAL, text=_(u'Save As...') + '\tCTRL+SHIFT+S') AppendMenu(parent, help='', id=ID_PLCOPENEDITORFILEMENUGENERATE, - kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G') + kind=wx.ITEM_NORMAL, text=_(u'Generate Program') + '\tCTRL+G') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_PAGE_SETUP, - kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') + kind=wx.ITEM_NORMAL, text=_(u'Page Setup') + '\tCTRL+ALT+P') AppendMenu(parent, help='', id=wx.ID_PREVIEW, - kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') + kind=wx.ITEM_NORMAL, text=_(u'Preview') + '\tCTRL+SHIFT+P') AppendMenu(parent, help='', id=wx.ID_PRINT, - kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') + kind=wx.ITEM_NORMAL, text=_(u'Print') + '\tCTRL+P') parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_PROPERTIES, - kind=wx.ITEM_NORMAL, text=_(u'&Properties')) + kind=wx.ITEM_NORMAL, text=_(u'&Properties')) parent.AppendSeparator() AppendMenu(parent, help='', id=wx.ID_EXIT, - kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') + kind=wx.ITEM_NORMAL, text=_(u'Quit') + '\tCTRL+Q') self.Bind(wx.EVT_MENU, self.OnNewProjectMenu, id=wx.ID_NEW) self.Bind(wx.EVT_MENU, self.OnOpenProjectMenu, id=wx.ID_OPEN) @@ -134,7 +115,7 @@ self.Bind(wx.EVT_MENU, self.OnSaveProjectMenu, id=wx.ID_SAVE) self.Bind(wx.EVT_MENU, self.OnSaveProjectAsMenu, id=wx.ID_SAVEAS) self.Bind(wx.EVT_MENU, self.OnGenerateProgramMenu, - id=ID_PLCOPENEDITORFILEMENUGENERATE) + id=ID_PLCOPENEDITORFILEMENUGENERATE) self.Bind(wx.EVT_MENU, self.OnPageSetupMenu, id=wx.ID_PAGE_SETUP) self.Bind(wx.EVT_MENU, self.OnPreviewMenu, id=wx.ID_PREVIEW) self.Bind(wx.EVT_MENU, self.OnPrintMenu, id=wx.ID_PRINT) @@ -149,23 +130,34 @@ def _init_coll_HelpMenu_Items(self, parent): AppendMenu(parent, help='', id=wx.ID_HELP, - kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') - #AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, + kind=wx.ITEM_NORMAL, text=_(u'PLCOpenEditor') + '\tF1') + # AppendMenu(parent, help='', id=wx.ID_HELP_CONTENTS, # kind=wx.ITEM_NORMAL, text=u'PLCOpen\tF2') - #AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, + # AppendMenu(parent, help='', id=wx.ID_HELP_CONTEXT, # kind=wx.ITEM_NORMAL, text=u'IEC 61131-3\tF3') + + def handler(event): + return wx.MessageBox( + version.GetCommunityHelpMsg(), + _(u'Community support'), + wx.OK | wx.ICON_INFORMATION) + + id = wx.NewId() + parent.Append(help='', id=id, kind=wx.ITEM_NORMAL, text=_(u'Community support')) + self.Bind(wx.EVT_MENU, handler, id=id) + AppendMenu(parent, help='', id=wx.ID_ABOUT, - kind=wx.ITEM_NORMAL, text=_(u'About')) + kind=wx.ITEM_NORMAL, text=_(u'About')) self.Bind(wx.EVT_MENU, self.OnPLCOpenEditorMenu, id=wx.ID_HELP) - #self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS) + # self.Bind(wx.EVT_MENU, self.OnPLCOpenMenu, id=wx.ID_HELP_CONTENTS) self.Bind(wx.EVT_MENU, self.OnAboutMenu, id=wx.ID_ABOUT) - ## Constructor of the PLCOpenEditor class. - # @param parent The parent window. - # @param controler The controler been used by PLCOpenEditor (default: None). - # @param fileOpen The filepath to open if no controler defined (default: None). - # @param debug The filepath to open if no controler defined (default: False). - def __init__(self, parent, fileOpen = None): + def __init__(self, parent, fileOpen=None): + """ Constructor of the PLCOpenEditor class. + + :param parent: The parent window. + :param fileOpen: The filepath to open if no controler defined (default: None). + """ self.icon = wx.Icon(os.path.join(beremiz_dir, "images", "poe.ico"), wx.BITMAP_TYPE_ICO) IDEFrame.__init__(self, parent) @@ -193,7 +185,7 @@ if result is not None: (num, line) = result - self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1 = num, a2 = line)) + self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1=num, a2=line)) def OnCloseFrame(self, event): if self.Controler is None or self.CheckSaveBeforeClosing(_("Close Application")): @@ -208,13 +200,13 @@ def RefreshTitle(self): name = _("PLCOpenEditor") if self.Controler is not None: - self.SetTitle("%s - %s"%(name, self.Controler.GetFilename())) + self.SetTitle("%s - %s" % (name, self.Controler.GetFilename())) else: self.SetTitle(name) -#------------------------------------------------------------------------------- -# File Menu Functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # File Menu Functions + # ------------------------------------------------------------------------------- def RefreshFileMenu(self): MenuToolBar = self.Panes["MenuToolBar"] @@ -306,7 +298,7 @@ if result is not None: (num, line) = result - self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1 = num, a2 = line)) + self.ShowErrorMessage(_("PLC syntax error at line {a1}:\n{a2}").format(a1=num, a2=line)) def OnCloseProjectMenu(self, event): if not self.CheckSaveBeforeClosing(): @@ -321,24 +313,24 @@ self.SaveProjectAs() def OnGenerateProgramMenu(self, event): - dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE|wx.CHANGE_DIR) + dialog = wx.FileDialog(self, _("Choose a file"), os.getcwd(), self.Controler.GetProgramFilePath(), _("ST files (*.st)|*.st|All files|*.*"), wx.SAVE | wx.CHANGE_DIR) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() message_text = "" header, icon = _("Done"), wx.ICON_INFORMATION if os.path.isdir(os.path.dirname(filepath)): - program, errors, warnings = self.Controler.GenerateProgram(filepath) + _program, errors, warnings = self.Controler.GenerateProgram(filepath) message_text += "".join([_("warning: %s\n") % warning for warning in warnings]) if len(errors) > 0: message_text += "".join([_("error: %s\n") % error for error in errors]) - message_text += _("Can't generate program to file %s!")%filepath + message_text += _("Can't generate program to file %s!") % filepath header, icon = _("Error"), wx.ICON_ERROR else: message_text += _("Program was successfully generated!") else: - message_text += _("\"%s\" is not a valid folder!")%os.path.dirname(filepath) + message_text += _("\"%s\" is not a valid folder!") % os.path.dirname(filepath) header, icon = _("Error"), wx.ICON_ERROR - message = wx.MessageDialog(self, message_text, header, wx.OK|icon) + message = wx.MessageDialog(self, message_text, header, wx.OK | icon) message.ShowModal() message.Destroy() dialog.Destroy() @@ -369,130 +361,65 @@ if filepath != "": directory, filename = os.path.split(filepath) else: - directory, filename = os.getcwd(), "%(projectName)s.xml"%self.Controler.GetProjectProperties() - dialog = wx.FileDialog(self, _("Choose a file"), directory, filename, _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE|wx.OVERWRITE_PROMPT) + directory, filename = os.getcwd(), "%(projectName)s.xml" % self.Controler.GetProjectProperties() + dialog = wx.FileDialog(self, _("Choose a file"), directory, filename, _("PLCOpen files (*.xml)|*.xml|All files|*.*"), wx.SAVE | wx.OVERWRITE_PROMPT) if dialog.ShowModal() == wx.ID_OK: filepath = dialog.GetPath() if os.path.isdir(os.path.dirname(filepath)): result = self.Controler.SaveXMLFile(filepath) if not result: - self.ShowErrorMessage(_("Can't save project to file %s!")%filepath) + self.ShowErrorMessage(_("Can't save project to file %s!") % filepath) else: - self.ShowErrorMessage(_("\"%s\" is not a valid folder!")%os.path.dirname(filepath)) + self.ShowErrorMessage(_("\"%s\" is not a valid folder!") % os.path.dirname(filepath)) self._Refresh(TITLE, FILEMENU, PAGETITLES) dialog.Destroy() -#------------------------------------------------------------------------------- -# Exception Handler -#------------------------------------------------------------------------------- - -Max_Traceback_List_Size = 20 - -def Display_Exception_Dialog(e_type,e_value,e_tb): - trcbck_lst = [] - for i,line in enumerate(traceback.extract_tb(e_tb)): - trcbck = " " + str(i+1) + _(". ") - if line[0].find(os.getcwd()) == -1: - trcbck += _("file : ") + str(line[0]) + _(", ") - else: - trcbck += _("file : ") + str(line[0][len(os.getcwd()):]) + _(", ") - trcbck += _("line : ") + str(line[1]) + _(", ") + _("function : ") + str(line[2]) - trcbck_lst.append(trcbck) - - # Allow clicking.... - cap = wx.Window_GetCapture() - if cap: - cap.ReleaseMouse() - - dlg = wx.SingleChoiceDialog(None, - _(""" -An unhandled exception (bug) occured. Bug report saved at : -(%s) - -Please be kind enough to send this file to: -beremiz-devel@lists.sourceforge.net - -You should now restart program. - -Traceback: -""") % bug_report_path + - repr(e_type) + " : " + repr(e_value), - _("Error"), - trcbck_lst) - try: - res = (dlg.ShowModal() == wx.ID_OK) - finally: - dlg.Destroy() - - return res - -def Display_Error_Dialog(e_value): - message = wx.MessageDialog(None, str(e_value), _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - -def get_last_traceback(tb): - while tb.tb_next: - tb = tb.tb_next - return tb - - -def format_namespace(d, indent=' '): - return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) - - -ignored_exceptions = [] # a problem with a line in a module is only reported once per session - -def AddExceptHook(path, app_version='[No version]'):#, ignored_exceptions=[]): - - def handle_exception(e_type, e_value, e_traceback): - traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func - last_tb = get_last_traceback(e_traceback) - ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) - if str(e_value).startswith("!!!"): - Display_Error_Dialog(e_value) - elif ex not in ignored_exceptions: - result = Display_Exception_Dialog(e_type,e_value,e_traceback) - if result: - ignored_exceptions.append(ex) - info = { - 'app-title' : wx.GetApp().GetAppName(), # app_title - 'app-version' : app_version, - 'wx-version' : wx.VERSION_STRING, - 'wx-platform' : wx.Platform, - 'python-version' : platform.python_version(), #sys.version.split()[0], - 'platform' : platform.platform(), - 'e-type' : e_type, - 'e-value' : e_value, - 'date' : time.ctime(), - 'cwd' : os.getcwd(), - } - if e_traceback: - info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) - last_tb = get_last_traceback(e_traceback) - exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred - info['locals'] = format_namespace(exception_locals) - if 'self' in exception_locals: - info['self'] = format_namespace(exception_locals['self'].__dict__) - - output = open(path+os.sep+"bug_report_"+info['date'].replace(':','-').replace(' ','_')+".txt",'w') - lst = info.keys() - lst.sort() - for a in lst: - output.write(a+":\n"+str(info[a])+"\n\n") - - #sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) - sys.excepthook = handle_exception + +class PLCOpenEditorApp(wx.App): + # def SetOpenFile( + + def PrintUsage(self): + print("\nUsage of PLCOpenEditor.py :") + print("\n %s [Filepath]\n" % sys.argv[0]) + + def ParseCommandLine(self): + # Parse options given to PLCOpenEditor in command line + try: + opts, args = getopt.getopt(sys.argv[1:], "h", ["help"]) + except getopt.GetoptError: + # print help information and exit: + self.PrintUsage() + sys.exit(2) + + # Extract if help has been requested + for o, _a in opts: + if o in ("-h", "--help"): + self.PrintUsage() + sys.exit() + + # Extract the optional filename to open + self.fileOpen = None + if len(args) > 1: + self.PrintUsage() + sys.exit() + elif len(args) == 1: + self.fileOpen = args[0] + + def OnInit(self): + self.SetAppName('plcopeneditor') + self.ParseCommandLine() + InstallLocalRessources(beremiz_dir) + if wx.VERSION < (3, 0, 0): + wx.InitAllImageHandlers() + util.ExceptionHandler.AddExceptHook(version.app_version) + self.frame = PLCOpenEditor(None, fileOpen=self.fileOpen) + return True + + def Show(self): + self.frame.Show() + if __name__ == '__main__': - if wx.VERSION < (3, 0, 0): - wx.InitAllImageHandlers() - - # Install a exception handle for bug reports - AddExceptHook(os.getcwd(), version.app_version) - - frame = PLCOpenEditor(None, fileOpen=fileOpen) - - frame.Show() + app = PLCOpenEditorApp() + app.Show() app.MainLoop() - diff -r c1298e7ffe3a -r 8391c11477f4 POULibrary.py --- a/POULibrary.py Fri Mar 24 12:07:47 2017 +0000 +++ b/POULibrary.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,9 +22,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from weakref import ref -class POULibrary: + +class POULibrary(object): def __init__(self, CTR, LibName, TypeStack): from PLCControler import PLCControler self.CTR = ref(CTR) @@ -33,21 +36,21 @@ self.LibraryControler.OpenXMLFile(self.GetLibraryPath()) self.LibraryControler.ClearConfNodeTypes() self.LibraryControler.AddConfNodeTypesList(TypeStack) - self.program = None; + self.program = None def GetSTCode(self): if not self.program: self.program = self.LibraryControler.GenerateProgram()[0]+"\n" - return self.program + return self.program def GetName(self): return self.LibName def GetCTR(self): return self.CTR() - + def GetTypes(self): - return {"name" : self.GetName(), "types": self.LibraryControler.Project} + return {"name": self.GetName(), "types": self.LibraryControler.Project} def GetLibraryPath(self): raise Exception("Not implemented") diff -r c1298e7ffe3a -r 8391c11477f4 ProjectController.py --- a/ProjectController.py Fri Mar 24 12:07:47 2017 +0000 +++ b/ProjectController.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -25,22 +26,27 @@ """ Beremiz Project Controller """ -import os,sys,traceback + + +from __future__ import absolute_import +import os +import traceback import time -import features +from time import localtime import shutil -import wx -import re, tempfile -from math import ceil +import re +import tempfile from types import ListType from threading import Timer, Lock, Thread -from time import localtime from datetime import datetime from weakref import WeakKeyDictionary from itertools import izip -import targets +import wx + +import features import connectors +import util.paths as paths from util.misc import CheckPathPerm, GetClassImporter from util.MiniTextControler import MiniTextControler from util.ProcessLogger import ProcessLogger @@ -52,28 +58,30 @@ from dialogs import DiscoveryDialog from PLCControler import PLCControler from plcopen.structures import IEC_KEYWORDS -from targets.typemapping import DebugTypesSize, LogLevelsCount, LogLevels -from targets.typemapping import UnpackDebugBuffer +import targets +from runtime.typemapping import DebugTypesSize, UnpackDebugBuffer from ConfigTreeNode import ConfigTreeNode, XSDSchemaErrorMessage -base_folder = os.path.split(os.path.dirname(os.path.realpath(__file__)))[0] +base_folder = paths.AbsParentDir(__file__) MATIEC_ERROR_MODEL = re.compile(".*\.st:(\d+)-(\d+)\.\.(\d+)-(\d+): (?:error)|(?:warning) : (.*)$") ITEM_CONFNODE = 25 + def ExtractChildrenTypesFromCatalog(catalog): children_types = [] - for n,d,h,c in catalog: + for n, d, _h, c in catalog: if isinstance(c, ListType): children_types.extend(ExtractChildrenTypesFromCatalog(c)) else: children_types.append((n, GetClassImporter(c), d)) return children_types + def ExtractMenuItemsFromCatalog(catalog): menu_items = [] - for n,d,h,c in catalog: + for n, d, h, c in catalog: if isinstance(c, ListType): children = ExtractMenuItemsFromCatalog(c) else: @@ -81,38 +89,69 @@ menu_items.append((n, d, h, children)) return menu_items + def GetAddMenuItems(): return ExtractMenuItemsFromCatalog(features.catalog) -class Iec2CSettings(): + +class Iec2CSettings(object): def __init__(self): - self.iec2c = os.path.join(base_folder, "matiec", "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "")) + self.iec2c = None self.iec2c_buildopts = None - self.ieclib_path = os.path.join(base_folder, "matiec", "lib") - self.ieclib_c_path = None - - def findLibCPath(self): - path="" - paths=[ - os.path.join(base_folder, "matiec", "lib", "C"), - os.path.join(base_folder, "matiec", "lib") ] + self.ieclib_path = self.findLibPath() + self.ieclib_c_path = self.findLibCPath() + + def findObject(self, paths, test): + path = None for p in paths: - filename=os.path.join(p, "iec_types.h") - if (os.path.isfile(filename)): + if test(p): path = p break return path + def findCmd(self): + cmd = "iec2c"+(".exe" if wx.Platform == '__WXMSW__' else "") + paths = [ + os.path.join(base_folder, "matiec") + ] + path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, cmd))) + + # otherwise use iec2c from PATH + if path is not None: + cmd = os.path.join(path, cmd) + + return cmd + + def findLibPath(self): + paths = [ + os.path.join(base_folder, "matiec", "lib"), + "/usr/lib/matiec" + ] + path = self.findObject(paths, lambda p: os.path.isfile(os.path.join(p, "ieclib.txt"))) + return path + + def findLibCPath(self): + path = None + if self.ieclib_path is not None: + paths = [ + os.path.join(self.ieclib_path, "C"), + self.ieclib_path] + path = self.findObject( + paths, + lambda p: os.path.isfile(os.path.join(p, "iec_types.h"))) + return path + def findSupportedOptions(self): - buildcmd = "\"%s\" -h"%(self.iec2c) - options =["-f", "-l", "-p"] + buildcmd = "\"%s\" -h" % (self.getCmd()) + options = ["-f", "-l", "-p"] buildopt = "" try: # Invoke compiler. Output files are listed to stdout, errors to stderr - status, result, err_result = ProcessLogger(self.logger, buildcmd, - no_stdout=True, no_stderr=True).spin() - except Exception,e: + _status, result, _err_result = ProcessLogger(None, buildcmd, + no_stdout=True, + no_stderr=True).spin() + except Exception: return buildopt for opt in options: @@ -121,6 +160,8 @@ return buildopt def getCmd(self): + if self.iec2c is None: + self.iec2c = self.findCmd() return self.iec2c def getOptions(self): @@ -136,23 +177,8 @@ self.ieclib_c_path = self.findLibCPath() return self.ieclib_c_path -iec2c_cfg = Iec2CSettings() - -class ProjectController(ConfigTreeNode, PLCControler): - """ - This class define Root object of the confnode tree. - It is responsible of : - - Managing project directory - - Building project - - Handling PLCOpenEditor controler and view - - Loading user confnodes and instanciante them as children - - ... - - """ - - # For root object, available Children Types are modules of the confnode packages. - CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) - + +def GetProjectControllerXSD(): XSD = """ @@ -167,12 +193,12 @@ """+((""" - """+"\n".join(['' - for libname,lib in features.libraries])+""" + for libname, _lib in features.libraries])+""" - """) if len(features.libraries)>0 else '') + """ + """) if len(features.libraries) > 0 else '') + """ @@ -180,10 +206,32 @@ """ + return XSD + + +class ProjectController(ConfigTreeNode, PLCControler): + """ + This class define Root object of the confnode tree. + It is responsible of : + - Managing project directory + - Building project + - Handling PLCOpenEditor controler and view + - Loading user confnodes and instanciante them as children + - ... + + """ + # For root object, available Children Types are modules of the confnode packages. + CTNChildrenTypes = ExtractChildrenTypesFromCatalog(features.catalog) + XSD = GetProjectControllerXSD() EditorType = ProjectNodeEditor + iec2c_cfg = None def __init__(self, frame, logger): PLCControler.__init__(self) + ConfigTreeNode.__init__(self) + + if ProjectController.iec2c_cfg is None: + ProjectController.iec2c_cfg = Iec2CSettings() self.MandatoryParams = None self._builder = None @@ -197,7 +245,7 @@ self.IECdebug_datas = {} self.IECdebug_lock = Lock() - self.DebugTimer=None + self.DebugTimer = None self.ResetIECProgramsAndVariables() # In both new or load scenario, no need to save @@ -224,8 +272,8 @@ def LoadLibraries(self): self.Libraries = [] - TypeStack=[] - for libname,clsname in features.libraries: + TypeStack = [] + for libname, clsname in features.libraries: if self.BeremizRoot.Libraries is None or getattr(self.BeremizRoot.Libraries, "Enable_"+libname+"_Library"): Lib = GetClassImporter(clsname)()(self, libname, TypeStack) TypeStack.append(Lib.GetTypes()) @@ -244,7 +292,8 @@ # Timer to pull PLC status self.StatusTimer = wx.Timer(self.AppFrame, -1) self.AppFrame.Bind(wx.EVT_TIMER, - self.PullPLCStatusProc, self.StatusTimer) + self.PullPLCStatusProc, + self.StatusTimer) if self._connector is not None: frame.LogViewer.SetLogSource(self._connector) @@ -253,7 +302,8 @@ # Timer to dispatch debug values to consumers self.DispatchDebugValuesTimer = wx.Timer(self.AppFrame, -1) self.AppFrame.Bind(wx.EVT_TIMER, - self.DispatchDebugValuesProc, self.DispatchDebugValuesTimer) + self.DispatchDebugValuesProc, + self.DispatchDebugValuesTimer) self.RefreshConfNodesBlockLists() @@ -262,14 +312,14 @@ self.AppFrame.Unbind(wx.EVT_TIMER, self.StatusTimer) self.StatusTimer = None self.AppFrame = None - + self.KillDebugThread() self.logger = logger def CTNName(self): return "Project" def CTNTestModified(self): - return self.ChangesToSave or not self.ProjectIsSaved() + return self.ChangesToSave or not self.ProjectIsSaved() def CTNFullName(self): return "" @@ -278,10 +328,10 @@ return self def GetIECLibPath(self): - return iec2c_cfg.getLibCPath() + return self.iec2c_cfg.getLibCPath() def GetIEC2cPath(self): - return iec2c_cfg.getCmd() + return self.iec2c_cfg.getCmd() def GetCurrentLocation(self): return () @@ -317,7 +367,7 @@ target.setcontent(self.Parser.CreateElement(target_name, "TargetType")) return target - def GetParamsAttributes(self, path = None): + def GetParamsAttributes(self, path=None): params = ConfigTreeNode.GetParamsAttributes(self, path) if params[0]["name"] == "BeremizRoot": for child in params[0]["children"]: @@ -338,10 +388,11 @@ if CheckPathPerm(self.ProjectPath): return True if self.AppFrame is not None: - dialog = wx.MessageDialog(self.AppFrame, - _('You must have permission to work on the project\nWork on a project copy ?'), - _('Error'), - wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) + dialog = wx.MessageDialog( + self.AppFrame, + _('You must have permission to work on the project\nWork on a project copy ?'), + _('Error'), + wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION) answer = dialog.ShowModal() dialog.Destroy() if answer == wx.ID_YES: @@ -364,6 +415,18 @@ self.ProjectAddConfiguration(config_name) self.ProjectAddConfigurationResource(config_name, res_name) + def SetProjectDefaultConfiguration(self): + # Sets default task and instance for new project + config = self.Project.getconfiguration(self.GetProjectMainConfigurationName()) + resource = config.getresource()[0].getname() + config = config.getname() + resource_tagname = self.ComputeConfigurationResourceName(config, resource) + def_task = [ + {'Priority': '0', 'Single': '', 'Interval': 'T#20ms', 'Name': 'task0', 'Triggering': 'Cyclic'}] + def_instance = [ + {'Task': def_task[0].get('Name'), 'Type': self.GetProjectPouNames()[0], 'Name': 'instance0'}] + self.SetEditedResourceInfos(resource_tagname, def_task, def_instance) + def NewProject(self, ProjectPath, BuildPath=None): """ Create a new project in an empty folder @@ -411,7 +474,7 @@ if error is not None: if self.Project is not None: (fname_err, lnum, src) = (("PLC",) + error) - self.logger.write_warning(XSDSchemaErrorMessage.format(a1 = fname_err, a2 = lnum, a3 = src)) + self.logger.write_warning(XSDSchemaErrorMessage.format(a1=fname_err, a2=lnum, a3=src)) else: return error, False if len(self.GetProjectConfigNames()) == 0: @@ -424,11 +487,11 @@ self._setBuildPath(BuildPath) # If dir have already be made, and file exist if os.path.isdir(self.CTNPath()) and os.path.isfile(self.ConfNodeXmlFilePath()): - #Load the confnode.xml file into parameters members + # Load the confnode.xml file into parameters members result = self.LoadXMLParams() if result: return result, False - #Load and init all the children + # Load and init all the children self.LoadChildren() self.RefreshConfNodesBlockLists() self.UpdateButtons() @@ -498,7 +561,7 @@ path = os.getenv("USERPROFILE") else: path = os.getenv("HOME") - dirdialog = wx.DirDialog(self.AppFrame , _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON) + dirdialog = wx.DirDialog(self.AppFrame, _("Choose a directory to save project"), path, wx.DD_NEW_DIR_BUTTON) answer = dirdialog.ShowModal() dirdialog.Destroy() if answer == wx.ID_OK: @@ -513,24 +576,24 @@ def GetLibrariesTypes(self): self.LoadLibraries() - return [ lib.GetTypes() for lib in self.Libraries ] + return [lib.GetTypes() for lib in self.Libraries] def GetLibrariesSTCode(self): - return "\n".join([ lib.GetSTCode() for lib in self.Libraries ]) + return "\n".join([lib.GetSTCode() for lib in self.Libraries]) def GetLibrariesCCode(self, buildpath): - if len(self.Libraries)==0: - return [],[],() + if len(self.Libraries) == 0: + return [], [], () self.GetIECProgramsAndVariables() - LibIECCflags = '"-I%s" -Wno-unused-function'%os.path.abspath(self.GetIECLibPath()) - LocatedCCodeAndFlags=[] - Extras=[] + LibIECCflags = '"-I%s" -Wno-unused-function' % os.path.abspath(self.GetIECLibPath()) + LocatedCCodeAndFlags = [] + Extras = [] for lib in self.Libraries: - res=lib.Generate_C(buildpath,self._VariablesList,LibIECCflags) + res = lib.Generate_C(buildpath, self._VariablesList, LibIECCflags) LocatedCCodeAndFlags.append(res[:2]) - if len(res)>2: + if len(res) > 2: Extras.extend(res[2:]) - return map(list,zip(*LocatedCCodeAndFlags))+[tuple(Extras)] + return map(list, zip(*LocatedCCodeAndFlags))+[tuple(Extras)] # Update PLCOpenEditor ConfNode Block types from loaded confnodes def RefreshConfNodesBlockLists(self): @@ -567,7 +630,7 @@ return children def ConfNodePath(self): - return os.path.split(__file__)[0] + return paths.AbsDir(__file__) def CTNPath(self, CTNName=None): return self.ProjectPath @@ -619,10 +682,10 @@ def GetLocations(self): locations = [] - filepath = os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h") + filepath = os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h") if os.path.isfile(filepath): # IEC2C compiler generate a list of located variables : LOCATED_VARIABLES.h - location_file = open(os.path.join(self._getBuildPath(),"LOCATED_VARIABLES.h")) + location_file = open(os.path.join(self._getBuildPath(), "LOCATED_VARIABLES.h")) # each line of LOCATED_VARIABLES.h declares a located variable lines = [line.strip() for line in location_file.readlines()] # This regular expression parses the lines genereated by IEC2C @@ -634,7 +697,7 @@ # Get the resulting dict resdict = result.groupdict() # rewrite string for variadic location as a tuple of integers - resdict['LOC'] = tuple(map(int,resdict['LOC'].split(','))) + resdict['LOC'] = tuple(map(int, resdict['LOC'].split(','))) # set located size to 'X' if not given if not resdict['SIZE']: resdict['SIZE'] = 'X' @@ -661,14 +724,14 @@ self.logger.write(_("Generating SoftPLC IEC-61131 ST/IL/SFC code...\n")) # ask PLCOpenEditor controller to write ST/IL/SFC code file - program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) + _program, errors, warnings = self.GenerateProgram(self._getIECgeneratedcodepath()) if len(warnings) > 0: self.logger.write_warning(_("Warnings in ST/IL/SFC code generator :\n")) for warning in warnings: - self.logger.write_warning("%s\n"%warning) + self.logger.write_warning("%s\n" % warning) if len(errors) > 0: # Failed ! - self.logger.write_error(_("Error in ST/IL/SFC code generator :\n%s\n")%errors[0]) + self.logger.write_error(_("Error in ST/IL/SFC code generator :\n%s\n") % errors[0]) return False plc_file = open(self._getIECcodepath(), "w") # Add ST Library from confnodes @@ -679,7 +742,7 @@ plc_file.close() plc_file = open(self._getIECcodepath(), "r") self.ProgramOffset = 0 - for line in plc_file.xreadlines(): + for dummy in plc_file.xreadlines(): self.ProgramOffset += 1 plc_file.close() plc_file = open(self._getIECcodepath(), "a") @@ -687,23 +750,26 @@ plc_file.close() return True - - def _Compile_ST_to_SoftPLC(self): + iec2c_libpath = self.iec2c_cfg.getLibPath() + if iec2c_libpath is None: + self.logger.write_error(_("matiec installation is not found\n")) + return False + self.logger.write(_("Compiling IEC Program into C code...\n")) buildpath = self._getBuildPath() - buildcmd = "\"%s\" %s -I \"%s\" -T \"%s\" \"%s\""%( - iec2c_cfg.getCmd(), - iec2c_cfg.getOptions(), - iec2c_cfg.getLibPath(), - buildpath, - self._getIECcodepath()) + buildcmd = "\"%s\" %s -I \"%s\" -T \"%s\" \"%s\"" % ( + self.iec2c_cfg.getCmd(), + self.iec2c_cfg.getOptions(), + iec2c_libpath, + buildpath, + self._getIECcodepath()) try: # Invoke compiler. Output files are listed to stdout, errors to stderr status, result, err_result = ProcessLogger(self.logger, buildcmd, - no_stdout=True, no_stderr=True).spin() - except Exception,e: + no_stdout=True, no_stderr=True).spin() + except Exception, e: self.logger.write_error(buildcmd + "\n") self.logger.write_error(repr(e) + "\n") return False @@ -718,7 +784,7 @@ m_result = MATIEC_ERROR_MODEL.match(err_line) if m_result is not None: - first_line, first_column, last_line, last_column, error = m_result.groups() + first_line, _first_column, last_line, _last_column, _error = m_result.groups() first_line, last_line = int(first_line), int(last_line) last_section = None @@ -732,31 +798,33 @@ if first_line <= i <= last_line: if last_section is not None: self.logger.write_warning("In section: " + last_section) - last_section = None # only write section once + last_section = None # only write section once self.logger.write_warning("%04d: %s" % (i, line)) f.close() - self.logger.write_error(_("Error : IEC to C compiler returned %d\n")%status) + self.logger.write_error(_("Error : IEC to C compiler returned %d\n") % status) return False # Now extract C files of stdout - C_files = [ fname for fname in result.splitlines() if fname[-2:]==".c" or fname[-2:]==".C" ] + C_files = [fname for fname in result.splitlines() if fname[-2:] == ".c" or fname[-2:] == ".C"] # remove those that are not to be compiled because included by others C_files.remove("POUS.c") if not C_files: self.logger.write_error(_("Error : At least one configuration and one resource must be declared in PLC !\n")) return False # transform those base names to full names with path - C_files = map(lambda filename:os.path.join(buildpath, filename), C_files) + C_files = map(lambda filename: os.path.join(buildpath, filename), C_files) # prepend beremiz include to configuration header - H_files = [ fname for fname in result.splitlines() if fname[-2:]==".h" or fname[-2:]==".H" ] + H_files = [fname for fname in result.splitlines() if fname[-2:] == ".h" or fname[-2:] == ".H"] H_files.remove("LOCATED_VARIABLES.h") - H_files = map(lambda filename:os.path.join(buildpath, filename), H_files) + H_files = map(lambda filename: os.path.join(buildpath, filename), H_files) for H_file in H_files: - with file(H_file, 'r') as original: data = original.read() - with file(H_file, 'w') as modified: modified.write('#include "beremiz.h"\n' + data) + with file(H_file, 'r') as original: + data = original.read() + with file(H_file, 'w') as modified: + modified.write('#include "beremiz.h"\n' + data) self.logger.write(_("Extracting Located Variables...\n")) # Keep track of generated located variables for later use by self._Generate_C @@ -764,7 +832,7 @@ # Keep track of generated C files for later use by self.CTNGenerate_C self.PLCGeneratedCFiles = C_files # compute CFLAGS for plc - self.plcCFLAGS = '"-I%s" -Wno-unused-function'%iec2c_cfg.getLibCPath() + self.plcCFLAGS = '"-I%s" -Wno-unused-function' % self.iec2c_cfg.getLibCPath() return True def GetBuilder(self): @@ -776,19 +844,19 @@ targetclass = targets.GetBuilder(targetname) # if target already - if self._builder is None or not isinstance(self._builder,targetclass): + if self._builder is None or not isinstance(self._builder, targetclass): # Get classname instance self._builder = targetclass(self) return self._builder def ResetBuildMD5(self): - builder=self.GetBuilder() + builder = self.GetBuilder() if builder is not None: builder.ResetBinaryCodeMD5() self.EnableMethod("_Transfer", False) def GetLastBuildMD5(self): - builder=self.GetBuilder() + builder = self.GetBuilder() if builder is not None: return builder.GetBinaryCodeMD5() else: @@ -809,9 +877,9 @@ """ return ([(C_file_name, self.plcCFLAGS) - for C_file_name in self.PLCGeneratedCFiles ], - "", # no ldflags - False) # do not expose retreive/publish calls + for C_file_name in self.PLCGeneratedCFiles], + "", # no ldflags + False) # do not expose retreive/publish calls def ResetIECProgramsAndVariables(self): """ @@ -834,7 +902,7 @@ """ if self._ProgramList is None or self._VariablesList is None: try: - csvfile = os.path.join(self._getBuildPath(),"VARIABLES.csv") + csvfile = os.path.join(self._getBuildPath(), "VARIABLES.csv") # describes CSV columns ProgramsListAttributeName = ["num", "C_path", "type"] VariablesListAttributeName = ["num", "vartype", "IEC_path", "C_path", "type"] @@ -845,7 +913,7 @@ # Separate sections ListGroup = [] - for line in open(csvfile,'r').xreadlines(): + for line in open(csvfile, 'r').xreadlines(): strippedline = line.strip() if strippedline.startswith("//"): # Start new section @@ -857,9 +925,9 @@ # first section contains programs for line in ListGroup[0]: # Split and Maps each field to dictionnary entries - attrs = dict(zip(ProgramsListAttributeName,line.strip().split(';'))) - # Truncate "C_path" to remove conf an ressources names - attrs["C_path"] = '__'.join(attrs["C_path"].split(".",2)[1:]) + attrs = dict(zip(ProgramsListAttributeName, line.strip().split(';'))) + # Truncate "C_path" to remove conf an resources names + attrs["C_path"] = '__'.join(attrs["C_path"].split(".", 2)[1:]) # Push this dictionnary into result. self._ProgramList.append(attrs) @@ -868,9 +936,9 @@ Idx = 0 for line in ListGroup[1]: # Split and Maps each field to dictionnary entries - attrs = dict(zip(VariablesListAttributeName,line.strip().split(';'))) - # Truncate "C_path" to remove conf an ressources names - parts = attrs["C_path"].split(".",2) + attrs = dict(zip(VariablesListAttributeName, line.strip().split(';'))) + # Truncate "C_path" to remove conf an resources names + parts = attrs["C_path"].split(".", 2) if len(parts) > 2: config_FB = config_FBs.get(tuple(parts[:2])) if config_FB: @@ -886,19 +954,19 @@ # Push this dictionnary into result. self._DbgVariablesList.append(attrs) # Fill in IEC<->C translation dicts - IEC_path=attrs["IEC_path"] - self._IECPathToIdx[IEC_path]=(Idx, attrs["type"]) + IEC_path = attrs["IEC_path"] + self._IECPathToIdx[IEC_path] = (Idx, attrs["type"]) # Ignores numbers given in CSV file # Idx=int(attrs["num"]) # Count variables only, ignore FBs - Idx+=1 + Idx += 1 self._VariablesList.append(attrs) # third section contains ticktime if len(ListGroup) > 2: self._Ticktime = int(ListGroup[2][0]) - except Exception,e: + except Exception: self.logger.write_error(_("Cannot open/parse VARIABLES.csv!\n")) self.logger.write_error(traceback.format_exc()) self.ResetIECProgramsAndVariables() @@ -915,31 +983,35 @@ # prepare debug code variable_decl_array = [] bofs = 0 - for v in self._DbgVariablesList : + for v in self._DbgVariablesList: sz = DebugTypesSize.get(v["type"], 0) variable_decl_array += [ - "{&(%(C_path)s), "%v+ - {"EXT":"%(type)s_P_ENUM", - "IN":"%(type)s_P_ENUM", - "MEM":"%(type)s_O_ENUM", - "OUT":"%(type)s_O_ENUM", - "VAR":"%(type)s_ENUM"}[v["vartype"]]%v + - "}"] + "{&(%(C_path)s), " % v + + { + "EXT": "%(type)s_P_ENUM", + "IN": "%(type)s_P_ENUM", + "MEM": "%(type)s_O_ENUM", + "OUT": "%(type)s_O_ENUM", + "VAR": "%(type)s_ENUM" + }[v["vartype"]] % v + + "}"] bofs += sz debug_code = targets.GetCode("plc_debug.c") % { - "buffer_size":bofs, - "programs_declarations": - "\n".join(["extern %(type)s %(C_path)s;"%p for p in self._ProgramList]), - "extern_variables_declarations":"\n".join([ - {"EXT":"extern __IEC_%(type)s_p %(C_path)s;", - "IN":"extern __IEC_%(type)s_p %(C_path)s;", - "MEM":"extern __IEC_%(type)s_p %(C_path)s;", - "OUT":"extern __IEC_%(type)s_p %(C_path)s;", - "VAR":"extern __IEC_%(type)s_t %(C_path)s;", - "FB":"extern %(type)s %(C_path)s;"}[v["vartype"]]%v - for v in self._VariablesList if v["C_path"].find('.')<0]), - "variable_decl_array": ",\n".join(variable_decl_array) - } + "buffer_size": bofs, + "programs_declarations": "\n".join(["extern %(type)s %(C_path)s;" % + p for p in self._ProgramList]), + "extern_variables_declarations": "\n".join([ + { + "EXT": "extern __IEC_%(type)s_p %(C_path)s;", + "IN": "extern __IEC_%(type)s_p %(C_path)s;", + "MEM": "extern __IEC_%(type)s_p %(C_path)s;", + "OUT": "extern __IEC_%(type)s_p %(C_path)s;", + "VAR": "extern __IEC_%(type)s_t %(C_path)s;", + "FB": "extern %(type)s %(C_path)s;" + }[v["vartype"]] % v + for v in self._VariablesList if v["C_path"].find('.') < 0]), + "variable_decl_array": ",\n".join(variable_decl_array) + } return debug_code @@ -950,43 +1022,43 @@ """ # filter location that are related to code that will be called # in retreive, publish, init, cleanup - locstrs = map(lambda x:"_".join(map(str,x)), - [loc for loc,Cfiles,DoCalls in self.LocationCFilesAndCFLAGS if loc and DoCalls]) + locstrs = map(lambda x: "_".join(map(str, x)), + [loc for loc, _Cfiles, DoCalls in + self.LocationCFilesAndCFLAGS if loc and DoCalls]) # Generate main, based on template if not self.BeremizRoot.getDisable_Extensions(): plc_main_code = targets.GetCode("plc_main_head.c") % { - "calls_prototypes":"\n".join([( - "int __init_%(s)s(int argc,char **argv);\n"+ - "void __cleanup_%(s)s(void);\n"+ - "void __retrieve_%(s)s(void);\n"+ - "void __publish_%(s)s(void);")%{'s':locstr} for locstr in locstrs]), - "retrieve_calls":"\n ".join([ - "__retrieve_%s();"%locstr for locstr in locstrs]), - "publish_calls":"\n ".join([ #Call publish in reverse order - "__publish_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), - "init_calls":"\n ".join([ - "init_level=%d; "%(i+1)+ - "if((res = __init_%s(argc,argv))){"%locstr + - #"printf(\"%s\"); "%locstr + #for debug - "return res;}" for i,locstr in enumerate(locstrs)]), - "cleanup_calls":"\n ".join([ - "if(init_level >= %d) "%i+ - "__cleanup_%s();"%locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) + "calls_prototypes": "\n".join([( + "int __init_%(s)s(int argc,char **argv);\n" + + "void __cleanup_%(s)s(void);\n" + + "void __retrieve_%(s)s(void);\n" + + "void __publish_%(s)s(void);") % {'s': locstr} for locstr in locstrs]), + "retrieve_calls": "\n ".join([ + "__retrieve_%s();" % locstr for locstr in locstrs]), + "publish_calls": "\n ".join([ # Call publish in reverse order + "__publish_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]), + "init_calls": "\n ".join([ + "init_level=%d; " % (i+1) + + "if((res = __init_%s(argc,argv))){" % locstr + + # "printf(\"%s\"); "%locstr + #for debug + "return res;}" for i, locstr in enumerate(locstrs)]), + "cleanup_calls": "\n ".join([ + "if(init_level >= %d) " % i + + "__cleanup_%s();" % locstrs[i-1] for i in xrange(len(locstrs), 0, -1)]) } else: plc_main_code = targets.GetCode("plc_main_head.c") % { - "calls_prototypes":"\n", - "retrieve_calls":"\n", - "publish_calls":"\n", - "init_calls":"\n", - "cleanup_calls":"\n" - } + "calls_prototypes": "\n", + "retrieve_calls": "\n", + "publish_calls": "\n", + "init_calls": "\n", + "cleanup_calls": "\n" + } plc_main_code += targets.GetTargetCode(self.GetTarget().getcontent().getLocalTag()) plc_main_code += targets.GetCode("plc_main_tail.c") return plc_main_code - def _Build(self): """ Method called by user to (re)build SoftPLC and confnode tree @@ -1032,10 +1104,10 @@ # Build try: - if not builder.build() : + if not builder.build(): self.logger.write_error(_("C Build failed.\n")) return False - except Exception, exc: + except Exception: self.logger.write_error(_("C Build crashed !\n")) self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() @@ -1054,7 +1126,7 @@ CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C( buildpath, self.PLCGeneratedLocatedVars) - except Exception, exc: + except Exception: self.logger.write_error(_("Runtime IO extensions C code generation failed !\n")) self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() @@ -1063,13 +1135,13 @@ # Generate C code and compilation params from liraries try: LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(buildpath) - except Exception, exc: + except Exception: self.logger.write_error(_("Runtime library extensions C code generation failed !\n")) self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() return False - self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS + self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + CTNLocationCFilesAndCFLAGS self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS ExtraFiles = CTNExtraFiles + LibExtraFiles @@ -1081,32 +1153,35 @@ # Recreate directory os.mkdir(extrafilespath) # Then write the files - for fname,fobject in ExtraFiles: - fpath = os.path.join(extrafilespath,fname) + for fname, fobject in ExtraFiles: + fpath = os.path.join(extrafilespath, fname) open(fpath, "wb").write(fobject.read()) # Now we can forget ExtraFiles (will close files object) del ExtraFiles # Header file for extensions - open(os.path.join(buildpath,"beremiz.h"), "w").write(targets.GetHeader()) + open(os.path.join(buildpath, "beremiz.h"), "w").write(targets.GetHeader()) # Template based part of C code generation # files are stacked at the beginning, as files of confnode tree root - for generator, filename, name in [ - # debugger code - (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), - # init/cleanup/retrieve/publish, run and align code - (self.Generate_plc_main,"plc_main.c","Common runtime")]: + c_source = [ + # debugger code + (self.Generate_plc_debugger, "plc_debugger.c", "Debugger"), + # init/cleanup/retrieve/publish, run and align code + (self.Generate_plc_main, "plc_main.c", "Common runtime") + ] + + for generator, filename, name in c_source: try: # Do generate code = generator() if code is None: - raise - code_path = os.path.join(buildpath,filename) + raise Exception + code_path = os.path.join(buildpath, filename) open(code_path, "w").write(code) # Insert this file as first file to be compiled at root confnode - self.LocationCFilesAndCFLAGS[0][1].insert(0,(code_path, self.plcCFLAGS)) - except Exception, exc: + self.LocationCFilesAndCFLAGS[0][1].insert(0, (code_path, self.plcCFLAGS)) + except Exception: self.logger.write_error(name+_(" generation failed !\n")) self.logger.write_error(traceback.format_exc()) self.ResetBuildMD5() @@ -1117,30 +1192,34 @@ def ShowError(self, logger, from_location, to_location): chunk_infos = self.GetChunkInfos(from_location, to_location) for infos, (start_row, start_col) in chunk_infos: - row = 1 if from_location[0] < start_row else (from_location[0] - start_row) + row = 1 if from_location[0] < start_row else (from_location[0] - start_row) col = 1 if (start_row != from_location[0]) else (from_location[1] - start_col) start = (row, col) - row = 1 if to_location[0] < start_row else (to_location[0] - start_row) + row = 1 if to_location[0] < start_row else (to_location[0] - start_row) col = 1 if (start_row != to_location[0]) else (to_location[1] - start_col) end = (row, col) - + if self.AppFrame is not None: self.AppFrame.ShowError(infos, start, end) _IECCodeView = None + def _showIECcode(self): self._OpenView("IEC code") _IECRawCodeView = None + def _editIECrawcode(self): self._OpenView("IEC raw code") _ProjectFilesView = None + def _OpenProjectFiles(self): self._OpenView("Project Files") _FileEditors = {} + def _OpenFileEditor(self, filepath): self._OpenView(filepath) @@ -1154,10 +1233,10 @@ self._IECCodeView.SetKeywords(IEC_KEYWORDS) try: text = file(plc_file).read() - except: + except Exception: text = '(* No IEC code have been generated at that time ! *)' - self._IECCodeView.SetText(text = text) - self._IECCodeView.Editor.SetReadOnly(True) + self._IECCodeView.SetText(text=text) + self._IECCodeView.Editor.SetReadOnly(True) self._IECCodeView.SetIcon(GetBitmap("ST")) setattr(self._IECCodeView, "_OnClose", self.OnCloseEditor) @@ -1199,7 +1278,7 @@ elif name is not None and name.find("::") != -1: filepath, editor_name = name.split("::") - if not self._FileEditors.has_key(filepath): + if filepath not in self._FileEditors: if os.path.isfile(filepath): file_extension = os.path.splitext(filepath)[1] @@ -1212,9 +1291,12 @@ editor_name = editors.keys()[0] elif len(editors) > 0: names = editors.keys() - dialog = wx.SingleChoiceDialog(self.AppFrame, - _("Select an editor:"), _("Editor selection"), - names, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog( + self.AppFrame, + _("Select an editor:"), + _("Editor selection"), + names, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: editor_name = names[dialog.GetSelection()] dialog.Destroy() @@ -1228,7 +1310,7 @@ if isinstance(self._FileEditors[filepath], DebugViewer): self._FileEditors[filepath].SetDataProducer(self) - if self._FileEditors.has_key(filepath): + if filepath in self._FileEditors: editor = self._FileEditors[filepath] self.AppFrame.EditProjectElement(editor, editor.GetTagName()) @@ -1264,11 +1346,10 @@ self.ShowMethod("_showIECcode", os.path.isfile(self._getIECcodepath())) if self.AppFrame is not None and not self.UpdateMethodsFromPLCStatus(): self.AppFrame.RefreshStatusToolBar() - + def UpdateButtons(self): wx.CallAfter(self._UpdateButtons) - def UpdatePLCLog(self, log_count): if log_count: if self.AppFrame is not None: @@ -1285,21 +1366,21 @@ if status is None: self._SetConnector(None, False) status = "Disconnected" - if(self.previous_plcstate != status): + if self.previous_plcstate != status: for args in { - "Started" : [("_Run", False), - ("_Stop", True)], - "Stopped" : [("_Run", True), - ("_Stop", False)], - "Empty" : [("_Run", False), - ("_Stop", False)], - "Broken" : [], - "Disconnected" :[("_Run", False), - ("_Stop", False), - ("_Transfer", False), - ("_Connect", True), - ("_Disconnect", False)], - }.get(status,[]): + "Started": [("_Run", False), + ("_Stop", True)], + "Stopped": [("_Run", True), + ("_Stop", False)], + "Empty": [("_Run", False), + ("_Stop", False)], + "Broken": [], + "Disconnected": [("_Run", False), + ("_Stop", False), + ("_Transfer", False), + ("_Connect", True), + ("_Disconnect", False)], + }.get(status, []): self.ShowMethod(*args) self.previous_plcstate = status if self.AppFrame is not None: @@ -1323,8 +1404,8 @@ "Disconnected": _("Disconnected") } return msgs.get(status, status) - - def ShowPLCProgress(self, status = "", progress = 0): + + def ShowPLCProgress(self, status="", progress=0): self.AppFrame.ProgressStatusBar.Show() self.AppFrame.ConnectionStatusBar.SetStatusText(self.GetTextStatus(status), 1) self.AppFrame.ProgressStatusBar.SetValue(progress) @@ -1335,40 +1416,40 @@ self.previous_plcstate = "" self.AppFrame.ProgressStatusBar.Hide() self.UpdateMethodsFromPLCStatus() - + def PullPLCStatusProc(self, event): self.UpdateMethodsFromPLCStatus() def SnapshotAndResetDebugValuesBuffers(self): buffers, self.DebugValuesBuffers = (self.DebugValuesBuffers, - [list() for n in xrange(len(self.TracedIECPath))]) + [list() for dummy in xrange(len(self.TracedIECPath))]) ticks, self.DebugTicks = self.DebugTicks, [] return ticks, buffers def RegisterDebugVarToConnector(self): - self.DebugTimer=None + self.DebugTimer = None Idxs = [] self.TracedIECPath = [] self.TracedIECTypes = [] if self._connector is not None: self.IECdebug_lock.acquire() IECPathsToPop = [] - for IECPath,data_tuple in self.IECdebug_datas.iteritems(): - WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple + for IECPath, data_tuple in self.IECdebug_datas.iteritems(): + WeakCallableDict, _data_log, _status, fvalue, _buffer_list = data_tuple if len(WeakCallableDict) == 0: # Callable Dict is empty. # This variable is not needed anymore! IECPathsToPop.append(IECPath) elif IECPath != "__tick__": # Convert - Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) + Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) if Idx is not None: if IEC_Type in DebugTypesSize: Idxs.append((Idx, IEC_Type, fvalue, IECPath)) else: - self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n")%IEC_Type) + self.logger.write_warning(_("Debug: Unsupported type to debug '%s'\n") % IEC_Type) else: - self.logger.write_warning(_("Debug: Unknown variable '%s'\n")%IECPath) + self.logger.write_warning(_("Debug: Unknown variable '%s'\n") % IECPath) for IECPathToPop in IECPathsToPop: self.IECdebug_datas.pop(IECPathToPop) @@ -1397,12 +1478,12 @@ if self.IsPLCStarted(): # Timer to prevent rapid-fire when registering many variables # use wx.CallAfter use keep using same thread. TODO : use wx.Timer instead - self.DebugTimer=Timer(0.5,wx.CallAfter,args = [self.RegisterDebugVarToConnector]) + self.DebugTimer = Timer(0.5, wx.CallAfter, args=[self.RegisterDebugVarToConnector]) # Rearm anti-rapid-fire timer self.DebugTimer.start() def GetDebugIECVariableType(self, IECPath): - Idx, IEC_Type = self._IECPathToIdx.get(IECPath,(None,None)) + _Idx, IEC_Type = self._IECPathToIdx.get(IECPath, (None, None)) return IEC_Type def SubscribeDebugIECVariable(self, IECPath, callableobj, buffer_list=False): @@ -1411,24 +1492,24 @@ to a WeakKeyDictionary linking weakly referenced callables """ - if IECPath != "__tick__" and not self._IECPathToIdx.has_key(IECPath): + if IECPath != "__tick__" and IECPath not in self._IECPathToIdx: return None self.IECdebug_lock.acquire() # If no entry exist, create a new one with a fresh WeakKeyDictionary IECdebug_data = self.IECdebug_datas.get(IECPath, None) if IECdebug_data is None: - IECdebug_data = [ - WeakKeyDictionary(), # Callables - [], # Data storage [(tick, data),...] - "Registered", # Variable status - None, - buffer_list] # Forced value + IECdebug_data = [ + WeakKeyDictionary(), # Callables + [], # Data storage [(tick, data),...] + "Registered", # Variable status + None, + buffer_list] # Forced value self.IECdebug_datas[IECPath] = IECdebug_data else: IECdebug_data[4] |= buffer_list - IECdebug_data[0][callableobj]=buffer_list + IECdebug_data[0][callableobj] = buffer_list self.IECdebug_lock.release() @@ -1440,12 +1521,12 @@ self.IECdebug_lock.acquire() IECdebug_data = self.IECdebug_datas.get(IECPath, None) if IECdebug_data is not None: - IECdebug_data[0].pop(callableobj,None) + IECdebug_data[0].pop(callableobj, None) if len(IECdebug_data[0]) == 0: self.IECdebug_datas.pop(IECPath) else: IECdebug_data[4] = reduce( - lambda x, y: x|y, + lambda x, y: x | y, IECdebug_data[0].itervalues(), False) self.IECdebug_lock.release() @@ -1460,7 +1541,7 @@ self.ReArmDebugRegisterTimer() def ForceDebugIECVariable(self, IECPath, fvalue): - if not self.IECdebug_datas.has_key(IECPath): + if IECPath not in self.IECdebug_datas: return self.IECdebug_lock.acquire() @@ -1475,7 +1556,7 @@ self.ReArmDebugRegisterTimer() def ReleaseDebugIECVariable(self, IECPath): - if not self.IECdebug_datas.has_key(IECPath): + if IECPath not in self.IECdebug_datas: return self.IECdebug_lock.acquire() @@ -1492,9 +1573,9 @@ def CallWeakcallables(self, IECPath, function_name, *cargs): data_tuple = self.IECdebug_datas.get(IECPath, None) if data_tuple is not None: - WeakCallableDict, data_log, status, fvalue, buffer_list = data_tuple - #data_log.append((debug_tick, value)) - for weakcallable,buffer_list in WeakCallableDict.iteritems(): + WeakCallableDict, _data_log, _status, _fvalue, buffer_list = data_tuple + # data_log.append((debug_tick, value)) + for weakcallable, buffer_list in WeakCallableDict.iteritems(): function = getattr(weakcallable, function_name, None) if function is not None: if buffer_list: @@ -1519,20 +1600,18 @@ while (not self.debug_break) and (self._connector is not None): plc_status, Traces = self._connector.GetTraceVariables() debug_getvar_retry += 1 - #print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()] - if plc_status == "Started" : + # print [dict.keys() for IECPath, (dict, log, status, fvalue) in self.IECdebug_datas.items()] + if plc_status == "Started": if len(Traces) > 0: - Failed = False self.IECdebug_lock.acquire() - for debug_tick, debug_buff in Traces : + for debug_tick, debug_buff in Traces: debug_vars = UnpackDebugBuffer(debug_buff, self.TracedIECTypes) - if (debug_vars is not None and - len(debug_vars) == len(self.TracedIECPath)): + if debug_vars is not None and len(debug_vars) == len(self.TracedIECPath): for IECPath, values_buffer, value in izip( self.TracedIECPath, self.DebugValuesBuffers, debug_vars): - IECdebug_data = self.IECdebug_datas.get(IECPath, None) #FIXME get + IECdebug_data = self.IECdebug_datas.get(IECPath, None) # FIXME get if IECdebug_data is not None and value is not None: forced = IECdebug_data[2:4] == ["Forced", value] if not IECdebug_data[4] and len(values_buffer) > 0: @@ -1618,7 +1697,7 @@ self.logger.write_error(_("Couldn't stop PLC !\n")) # debugthread should die on his own - #self.KillDebugThread() + # self.KillDebugThread() wx.CallAfter(self.UpdateMethodsFromPLCStatus) @@ -1645,10 +1724,7 @@ return # Get connector uri - uri = self.\ - BeremizRoot.\ - getURI_location().\ - strip() + uri = self.BeremizRoot.getURI_location().strip() # if uri is empty launch discovery dialog if uri == "": @@ -1658,7 +1734,7 @@ answer = dialog.ShowModal() uri = dialog.GetURI() dialog.Destroy() - except: + except Exception: self.logger.write_error(_("Local service discovery failed!\n")) self.logger.write_error(traceback.format_exc()) uri = None @@ -1668,9 +1744,7 @@ self.logger.write_error(_("Connection canceled!\n")) return else: - self.\ - BeremizRoot.\ - setURI_location(uri) + self.BeremizRoot.setURI_location(uri) self.ChangesToSave = True if self._View is not None: self._View.RefreshView() @@ -1683,14 +1757,14 @@ # Get connector from uri try: self._SetConnector(connectors.ConnectorFactory(uri, self)) - except Exception, msg: - self.logger.write_error(_("Exception while connecting %s!\n")%uri) + except Exception: + self.logger.write_error(_("Exception while connecting %s!\n") % uri) self.logger.write_error(traceback.format_exc()) # Did connection success ? if self._connector is None: # Oups. - self.logger.write_error(_("Connection failed to %s!\n")%uri) + self.logger.write_error(_("Connection failed to %s!\n") % uri) else: self.ShowMethod("_Connect", False) self.ShowMethod("_Disconnect", True) @@ -1700,14 +1774,7 @@ # Init with actual PLC status and print it self.UpdateMethodsFromPLCStatus() - if self.previous_plcstate is not None: - status = _(self.previous_plcstate) - else: - status = "" - - #self.logger.write(_("PLC is %s\n")%status) - - if self.previous_plcstate in ["Started","Stopped"]: + if self.previous_plcstate in ["Started", "Stopped"]: if self.DebugAvailable() and self.GetIECProgramsAndVariables(): self.logger.write(_("Debugger ready\n")) self._connect_debug() @@ -1722,22 +1789,21 @@ # Check remote target PLC correspondance to that md5 if MD5 is not None: if not self._connector.MatchMD5(MD5): -# self.logger.write_warning( -# _("Latest build does not match with target, please transfer.\n")) + # self.logger.write_warning( + # _("Latest build does not match with target, please transfer.\n")) self.EnableMethod("_Transfer", True) else: -# self.logger.write( -# _("Latest build matches target, no transfer needed.\n")) + # self.logger.write( + # _("Latest build matches target, no transfer needed.\n")) self.EnableMethod("_Transfer", True) # warns controller that program match self.ProgramTransferred() - #self.EnableMethod("_Transfer", False) + # self.EnableMethod("_Transfer", False) else: -# self.logger.write_warning( -# _("Cannot compare latest build to target. Please build.\n")) + # self.logger.write_warning( + # _("Cannot compare latest build to target. Please build.\n")) self.EnableMethod("_Transfer", False) - def _Disconnect(self): self._SetConnector(None) @@ -1746,7 +1812,7 @@ MD5 = self.GetLastBuildMD5() # Check if md5 file is empty : ask user to build PLC - if MD5 is None : + if MD5 is None: self.logger.write_error(_("Failed : Must build before transfer.\n")) return False @@ -1761,15 +1827,15 @@ self._getProjectFilesPath()]: extrafiles.extend( - [(name, open(os.path.join(extrafilespath, name), - 'rb').read()) \ - for name in os.listdir(extrafilespath)]) + [(name, open(os.path.join(extrafilespath, name), + 'rb').read()) + for name in os.listdir(extrafilespath)]) # Send PLC on target builder = self.GetBuilder() if builder is not None: data = builder.GetBinaryCode() - if data is not None : + if data is not None: if self._connector.NewPLC(MD5, data, extrafiles) and self.GetIECProgramsAndVariables(): self.UnsubscribeAllDebugIECVariable() self.ProgramTransferred() @@ -1777,82 +1843,102 @@ self.AppFrame.CloseObsoleteDebugTabs() self.AppFrame.RefreshPouInstanceVariablesPanel() self.logger.write(_("Transfer completed successfully.\n")) + self.AppFrame.LogViewer.ResetLogCounters() else: self.logger.write_error(_("Transfer failed\n")) - self.HidePLCProgress() + self.HidePLCProgress() else: self.logger.write_error(_("No PLC to transfer (did build succeed ?)\n")) wx.CallAfter(self.UpdateMethodsFromPLCStatus) StatusMethods = [ - {"bitmap" : "Build", - "name" : _("Build"), - "tooltip" : _("Build project into build folder"), - "method" : "_Build"}, - {"bitmap" : "Clean", - "name" : _("Clean"), - "enabled" : False, - "tooltip" : _("Clean project build folder"), - "method" : "_Clean"}, - {"bitmap" : "Run", - "name" : _("Run"), - "shown" : False, - "tooltip" : _("Start PLC"), - "method" : "_Run"}, - {"bitmap" : "Stop", - "name" : _("Stop"), - "shown" : False, - "tooltip" : _("Stop Running PLC"), - "method" : "_Stop"}, - {"bitmap" : "Connect", - "name" : _("Connect"), - "tooltip" : _("Connect to the target PLC"), - "method" : "_Connect"}, - {"bitmap" : "Transfer", - "name" : _("Transfer"), - "shown" : False, - "tooltip" : _("Transfer PLC"), - "method" : "_Transfer"}, - {"bitmap" : "Disconnect", - "name" : _("Disconnect"), - "shown" : False, - "tooltip" : _("Disconnect from PLC"), - "method" : "_Disconnect"}, - {"bitmap" : "ShowIECcode", - "name" : _("Show code"), - "shown" : False, - "tooltip" : _("Show IEC code generated by PLCGenerator"), - "method" : "_showIECcode"}, + { + "bitmap": "Build", + "name": _("Build"), + "tooltip": _("Build project into build folder"), + "method": "_Build" + }, + { + "bitmap": "Clean", + "name": _("Clean"), + "tooltip": _("Clean project build folder"), + "method": "_Clean", + "enabled": False, + }, + { + "bitmap": "Run", + "name": _("Run"), + "tooltip": _("Start PLC"), + "method": "_Run", + "shown": False, + }, + { + "bitmap": "Stop", + "name": _("Stop"), + "tooltip": _("Stop Running PLC"), + "method": "_Stop", + "shown": False, + }, + { + "bitmap": "Connect", + "name": _("Connect"), + "tooltip": _("Connect to the target PLC"), + "method": "_Connect" + }, + { + "bitmap": "Transfer", + "name": _("Transfer"), + "tooltip": _("Transfer PLC"), + "method": "_Transfer", + "shown": False, + }, + { + "bitmap": "Disconnect", + "name": _("Disconnect"), + "tooltip": _("Disconnect from PLC"), + "method": "_Disconnect", + "shown": False, + }, + { + "bitmap": "ShowIECcode", + "name": _("Show code"), + "tooltip": _("Show IEC code generated by PLCGenerator"), + "method": "_showIECcode", + "shown": False, + }, ] ConfNodeMethods = [ - {"bitmap" : "editIECrawcode", - "name" : _("Raw IEC code"), - "tooltip" : _("Edit raw IEC code added to code generated by PLCGenerator"), - "method" : "_editIECrawcode"}, - {"bitmap" : "ManageFolder", - "name" : _("Project Files"), - "tooltip" : _("Open a file explorer to manage project files"), - "method" : "_OpenProjectFiles"}, + { + "bitmap": "editIECrawcode", + "name": _("Raw IEC code"), + "tooltip": _("Edit raw IEC code added to code generated by PLCGenerator"), + "method": "_editIECrawcode" + }, + { + "bitmap": "ManageFolder", + "name": _("Project Files"), + "tooltip": _("Open a file explorer to manage project files"), + "method": "_OpenProjectFiles" + }, ] - def EnableMethod(self, method, value): for d in self.StatusMethods: - if d["method"]==method: - d["enabled"]=value + if d["method"] == method: + d["enabled"] = value return True return False def ShowMethod(self, method, value): for d in self.StatusMethods: - if d["method"]==method: - d["shown"]=value + if d["method"] == method: + d["shown"] = value return True return False def CallMethod(self, method): for d in self.StatusMethods: - if d["method"]==method and d.get("enabled", True) and d.get("shown", True): + if d["method"] == method and d.get("enabled", True) and d.get("shown", True): getattr(self, method)() diff -r c1298e7ffe3a -r 8391c11477f4 README.md --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README.md Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,119 @@ +[![docs](https://readthedocs.org/projects/beremiz/badge/?version=latest)](https://beremiz.readthedocs.io) + +# Beremiz # + +Beremiz is an integrated development environment for machine automation. It is Free Software, conforming to IEC-61131 among other standards. + +It relies on open standards to be independent of the targeted device, and let you turn any processor into a PLC. Beremiz includes tools to create HMI, and to connect your PLC programs to existing supervisions, databases, or fieldbuses. + +With Beremiz, you conform to standards, avoid vendor lock, and contribute to the better future of Automation. + +See official [Beremiz website](http://www.beremiz.org/) for more information. + +### Build on Linux ### + +* Prerequisites +``` +#!sh + +* # Ubuntu/Debian : +sudo apt-get install build-essential bison flex autoconf +sudo apt-get install python-wxgtk3.0 pyro mercurial +sudo apt-get install python-nevow python-matplotlib python-lxml python-zeroconf +``` +* Prepare +``` +#!sh +mkdir ~/Beremiz +cd ~/Beremiz +``` + +* Get Source Code +``` +#!sh +cd ~/Beremiz + +hg clone https://bitbucket.org/skvorl/beremiz +hg clone https://bitbucket.org/mjsousa/matiec +``` + +* Build MatIEC compiler +``` +#!sh +cd ~/Beremiz/matiec +autoreconf -i +./configure +make +``` + +* Build CanFestival (optional) +Only needed for CANopen support. Please read CanFestival manual to choose CAN interface other than 'virtual'. + +``` +#!sh +cd ~/Beremiz +hg clone http://dev.automforge.net/CanFestival-3 + +cd ~/Beremiz/CanFestival-3 +./configure --can=virtual +make +``` + +* Launch Beremiz IDE + +``` +#!sh +cd ~/Beremiz/beremiz +python Beremiz.py +``` + +### Run standalone Beremiz service ### + +* Start standalone Beremiz service +``` +#!sh +cd ~/Beremiz +mkdir beremiz_workdir + +cd ~/beremiz +python Beremiz_service.py -p 61194 -i localhost -x 0 -a 1 ~/Beremiz/beremiz_workdir +``` + +* Launch Beremiz IDE +``` +#!sh +cd ~/Beremiz/beremiz +python Beremiz.py +``` +* Open/Create PLC project in Beremiz IDE. +* +Enter target location URI in project's settings (project->Config->BeremizRoot/URI_location) pointed to your running Beremiz service (For example, PYRO://127.0.0.1:61194). +Save project and connect to running Beremiz service. + +### Documentation ### + + * See [Beremiz youtube channel](https://www.youtube.com/channel/UCcE4KYI0p1f6CmSwtzyg-ZA) to get quick information how to use Beremiz IDE. + + * [Official user manual](http://beremiz.readthedocs.io/) is built from sources in doc directory. + Documentation does not cover all aspects of Beremiz use yet. + Contribution are very welcome! + + * [User manual](http://www.sm1820.ru/files/beremiz/beremiz_manual.pdf) from INEUM (Russian). + Be aware that it contains some information about functions available only in INEUM's fork of Beremiz. + + * [User manual](http://www.beremiz.org/LpcManager_UserManual.pdf) from Smarteh (English). + Be aware that it contains some information about functions available only in Smarteh's fork of Beremiz. + + * Outdated short [user manual](https://www.scribd.com/document/76101511/Manual-Beremiz#scribd) from LOLI Tech (English). + + * See official [Beremiz website](http://www.beremiz.org/) for more information. + +### Support and development ### + +Main community support channel is [mailing list](https://sourceforge.net/p/beremiz/mailman/beremiz-devel/) (beremiz-devel@lists.sourceforge.net). + +The list is moderated and requires subscription for posting to it. + +To subscribe to the mailing list go [here](https://sourceforge.net/p/beremiz/mailman/beremiz-devel/). + +Searchable archive using search engine of your choice is available [here](http://beremiz-devel.2374573.n4.nabble.com/). \ No newline at end of file diff -r c1298e7ffe3a -r 8391c11477f4 bitbucket-pipelines.yml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bitbucket-pipelines.yml Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,20 @@ +image: skvorl/beremiz-requirements + +pipelines: + custom: # Pipelines that are triggered manually + checks: # The name that is displayed in the list in the Bitbucket Cloud GUI + - step: + script: # Modify the commands below to build your repository. + - ln -s /CanFestival-3 $BITBUCKET_CLONE_DIR/../CanFestival-3 + - /usr/bin/python --version + - ./tests/tools/check_source.sh + - ./tests/tools/run_python_tests.sh + + + default: + - step: + script: # Modify the commands below to build your repository. + - ln -s /CanFestival-3 $BITBUCKET_CLONE_DIR/../CanFestival-3 + - /usr/bin/python --version + - ./tests/tools/check_source.sh + - ./tests/tools/run_python_tests.sh diff -r c1298e7ffe3a -r 8391c11477f4 c_ext/CFileEditor.py --- a/c_ext/CFileEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/c_ext/CFileEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,27 +22,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx.stc as stc from controls.CustomStyledTextCtrl import faces from editors.CodeFileEditor import CodeFileEditor, CodeEditor + class CppEditor(CodeEditor): - KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class", - "const", "const_cast", "continue", "default", "delete", "do", "double", - "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", - "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", - "namespace", "new", "operator", "private", "protected", "public", "register", - "reinterpret_cast", "return", "short", "signed", "sizeof", "static", - "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", - "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", - "void", "volatile", "wchar_t", "while"] + KEYWORDS = ["asm", "auto", "bool", "break", "case", "catch", "char", "class", + "const", "const_cast", "continue", "default", "delete", "do", "double", + "dynamic_cast", "else", "enum", "explicit", "export", "extern", "false", + "float", "for", "friend", "goto", "if", "inline", "int", "long", "mutable", + "namespace", "new", "operator", "private", "protected", "public", "register", + "reinterpret_cast", "return", "short", "signed", "sizeof", "static", + "static_cast", "struct", "switch", "template", "this", "throw", "true", "try", + "typedef", "typeid", "typename", "union", "unsigned", "using", "virtual", + "void", "volatile", "wchar_t", "while"] COMMENT_HEADER = "/" - + def SetCodeLexer(self): self.SetLexer(stc.STC_LEX_CPP) - + self.StyleSetSpec(stc.STC_C_COMMENT, 'fore:#408060,size:%(size)d' % faces) self.StyleSetSpec(stc.STC_C_COMMENTLINE, 'fore:#408060,size:%(size)d' % faces) self.StyleSetSpec(stc.STC_C_COMMENTDOC, 'fore:#408060,size:%(size)d' % faces) @@ -53,15 +56,12 @@ self.StyleSetSpec(stc.STC_C_OPERATOR, 'bold,size:%(size)d' % faces) self.StyleSetSpec(stc.STC_C_STRINGEOL, 'back:#FFD5FF,size:%(size)d' % faces) -#------------------------------------------------------------------------------- -# CFileEditor Main Frame Class -#------------------------------------------------------------------------------- class CFileEditor(CodeFileEditor): - + """ + CFileEditor Main Frame Class + """ + CONFNODEEDITOR_TABS = [ (_("C code"), "_create_CodePanel")] CODE_EDITOR = CppEditor - - - diff -r c1298e7ffe3a -r 8391c11477f4 c_ext/__init__.py --- a/c_ext/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/c_ext/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,4 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from c_ext import * +from __future__ import absolute_import +from c_ext.c_ext import * diff -r c1298e7ffe3a -r 8391c11477f4 c_ext/c_ext.py --- a/c_ext/c_ext.py Fri Mar 24 12:07:47 2017 +0000 +++ b/c_ext/c_ext.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,11 +22,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import os -from CFileEditor import CFileEditor +from c_ext.CFileEditor import CFileEditor from CodeFileTreeNode import CodeFile + class CFile(CodeFile): XSD = """ @@ -47,13 +49,13 @@ "retrieveFunction", "publishFunction"] EditorType = CFileEditor - + def GetIconName(self): return "Cfile" def CodeFileName(self): return os.path.join(self.CTNPath(), "cfile.xml") - + def CTNGenerate_C(self, buildpath, locations): """ Generate C code @@ -70,17 +72,17 @@ current_location = self.GetCurrentLocation() # define a unique name for the generated C file location_str = "_".join(map(str, current_location)) - + text = "/* Code generated by Beremiz c_ext confnode */\n\n" text += "#include \n\n" - + # Adding includes text += "/* User includes */\n" text += self.CodeFile.includes.getanyText().strip() text += "\n" - + text += '#include "iec_types_all.h"\n\n' - + # Adding variables config = self.GetCTRoot().GetProjectConfigNames()[0] text += "/* User variables reference */\n" @@ -93,36 +95,35 @@ text += "extern %(type)s %(global)s;\n" % var_infos text += "#define %(name)s %(global)s.value\n" % var_infos text += "\n" - + # Adding user global variables and routines text += "/* User internal user variables and routines */\n" text += self.CodeFile.globals.getanyText().strip() text += "\n" - + # Adding Beremiz confnode functions text += "/* Beremiz confnode functions */\n" - text += "int __init_%s(int argc,char **argv)\n{\n"%location_str + text += "int __init_%s(int argc,char **argv)\n{\n" % location_str text += self.CodeFile.initFunction.getanyText().strip() text += " return 0;\n}\n\n" - - text += "void __cleanup_%s(void)\n{\n"%location_str + + text += "void __cleanup_%s(void)\n{\n" % location_str text += self.CodeFile.cleanUpFunction.getanyText().strip() text += "\n}\n\n" - - text += "void __retrieve_%s(void)\n{\n"%location_str + + text += "void __retrieve_%s(void)\n{\n" % location_str text += self.CodeFile.retrieveFunction.getanyText().strip() text += "\n}\n\n" - - text += "void __publish_%s(void)\n{\n"%location_str + + text += "void __publish_%s(void)\n{\n" % location_str text += self.CodeFile.publishFunction.getanyText().strip() text += "\n}\n\n" - - Gen_Cfile_path = os.path.join(buildpath, "CFile_%s.c"%location_str) - cfile = open(Gen_Cfile_path,'w') + + Gen_Cfile_path = os.path.join(buildpath, "CFile_%s.c" % location_str) + cfile = open(Gen_Cfile_path, 'w') cfile.write(text) cfile.close() - - matiec_CFLAGS = '"-I%s"'%os.path.abspath(self.GetCTRoot().GetIECLibPath()) - - return [(Gen_Cfile_path, str(self.CExtension.getCFLAGS() + matiec_CFLAGS))],str(self.CExtension.getLDFLAGS()),True + matiec_CFLAGS = '"-I%s"' % os.path.abspath(self.GetCTRoot().GetIECLibPath()) + + return [(Gen_Cfile_path, str(self.CExtension.getCFLAGS() + matiec_CFLAGS))], str(self.CExtension.getLDFLAGS()), True diff -r c1298e7ffe3a -r 8391c11477f4 canfestival/NetworkEditor.py --- a/canfestival/NetworkEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/canfestival/NetworkEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,61 +22,68 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx -from subindextable import EditingPanel from networkeditortemplate import NetworkEditorTemplate from editors.ConfTreeNodeEditor import ConfTreeNodeEditor -[ID_NETWORKEDITOR, +[ + ID_NETWORKEDITOR, ] = [wx.NewId() for _init_ctrls in range(1)] -[ID_NETWORKEDITORCONFNODEMENUADDSLAVE, ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, - ID_NETWORKEDITORCONFNODEMENUMASTER, +[ + ID_NETWORKEDITORCONFNODEMENUADDSLAVE, + ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, + ID_NETWORKEDITORCONFNODEMENUMASTER, ] = [wx.NewId() for _init_coll_ConfNodeMenu_Items in range(3)] -[ID_NETWORKEDITORMASTERMENUNODEINFOS, ID_NETWORKEDITORMASTERMENUDS301PROFILE, - ID_NETWORKEDITORMASTERMENUDS302PROFILE, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, - ID_NETWORKEDITORMASTERMENUADD, +[ + ID_NETWORKEDITORMASTERMENUNODEINFOS, ID_NETWORKEDITORMASTERMENUDS301PROFILE, + ID_NETWORKEDITORMASTERMENUDS302PROFILE, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, + ID_NETWORKEDITORMASTERMENUADD, ] = [wx.NewId() for _init_coll_MasterMenu_Items in range(5)] -[ID_NETWORKEDITORADDMENUSDOSERVER, ID_NETWORKEDITORADDMENUSDOCLIENT, - ID_NETWORKEDITORADDMENUPDOTRANSMIT, ID_NETWORKEDITORADDMENUPDORECEIVE, - ID_NETWORKEDITORADDMENUMAPVARIABLE, ID_NETWORKEDITORADDMENUUSERTYPE, +[ + ID_NETWORKEDITORADDMENUSDOSERVER, ID_NETWORKEDITORADDMENUSDOCLIENT, + ID_NETWORKEDITORADDMENUPDOTRANSMIT, ID_NETWORKEDITORADDMENUPDORECEIVE, + ID_NETWORKEDITORADDMENUMAPVARIABLE, ID_NETWORKEDITORADDMENUUSERTYPE, ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)] + class NetworkEditor(ConfTreeNodeEditor, NetworkEditorTemplate): - + ID = ID_NETWORKEDITOR CONFNODEEDITOR_TABS = [ (_("CANOpen network"), "_create_NetworkEditor")] - + def _create_NetworkEditor(self, prnt): - self.NetworkEditor = wx.Panel(id=-1, parent=prnt, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) - + self.NetworkEditor = wx.Panel( + id=-1, parent=prnt, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + NetworkEditorTemplate._init_ctrls(self, self.NetworkEditor) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - - main_sizer.AddWindow(self.NetworkNodes, 0, border=5, flag=wx.GROW|wx.ALL) - + + main_sizer.AddWindow(self.NetworkNodes, 0, border=5, flag=wx.GROW | wx.ALL) + self.NetworkEditor.SetSizer(main_sizer) - + return self.NetworkEditor - + def __init__(self, parent, controler, window): ConfTreeNodeEditor.__init__(self, parent, controler, window) NetworkEditorTemplate.__init__(self, controler, window, False) - + self.RefreshNetworkNodes() self.RefreshBufferState() - + def __del__(self): self.Controler.OnCloseEditor(self) - + def GetConfNodeMenuItems(self): add_menu = [(wx.ITEM_NORMAL, (_('SDO Server'), ID_NETWORKEDITORADDMENUSDOSERVER, '', self.OnAddSDOServerMenu)), (wx.ITEM_NORMAL, (_('SDO Client'), ID_NETWORKEDITORADDMENUSDOCLIENT, '', self.OnAddSDOClientMenu)), @@ -84,44 +91,44 @@ (wx.ITEM_NORMAL, (_('PDO Receive'), ID_NETWORKEDITORADDMENUPDORECEIVE, '', self.OnAddPDOReceiveMenu)), (wx.ITEM_NORMAL, (_('Map Variable'), ID_NETWORKEDITORADDMENUMAPVARIABLE, '', self.OnAddMapVariableMenu)), (wx.ITEM_NORMAL, (_('User Type'), ID_NETWORKEDITORADDMENUUSERTYPE, '', self.OnAddUserTypeMenu))] - + profile = self.Manager.GetCurrentProfileName() if profile not in ("None", "DS-301"): other_profile_text = _("%s Profile") % profile add_menu.append((wx.ITEM_SEPARATOR, None)) - for text, indexes in self.Manager.GetCurrentSpecificMenu(): + for text, _indexes in self.Manager.GetCurrentSpecificMenu(): add_menu.append((wx.ITEM_NORMAL, (text, wx.NewId(), '', self.GetProfileCallBack(text)))) else: other_profile_text = _('Other Profile') - + master_menu = [(wx.ITEM_NORMAL, (_('DS-301 Profile'), ID_NETWORKEDITORMASTERMENUDS301PROFILE, '', self.OnCommunicationMenu)), (wx.ITEM_NORMAL, (_('DS-302 Profile'), ID_NETWORKEDITORMASTERMENUDS302PROFILE, '', self.OnOtherCommunicationMenu)), (wx.ITEM_NORMAL, (other_profile_text, ID_NETWORKEDITORMASTERMENUDSOTHERPROFILE, '', self.OnEditProfileMenu)), (wx.ITEM_SEPARATOR, None), (add_menu, (_('Add'), ID_NETWORKEDITORMASTERMENUADD))] - + return [(wx.ITEM_NORMAL, (_('Add slave'), ID_NETWORKEDITORCONFNODEMENUADDSLAVE, '', self.OnAddSlaveMenu)), (wx.ITEM_NORMAL, (_('Remove slave'), ID_NETWORKEDITORCONFNODEMENUREMOVESLAVE, '', self.OnRemoveSlaveMenu)), (wx.ITEM_SEPARATOR, None), (master_menu, (_('Master'), ID_NETWORKEDITORCONFNODEMENUMASTER))] - + def RefreshMainMenu(self): pass - + def RefreshConfNodeMenu(self, confnode_menu): confnode_menu.Enable(ID_NETWORKEDITORCONFNODEMENUMASTER, self.NetworkNodes.GetSelection() == 0) - + def RefreshView(self): ConfTreeNodeEditor.RefreshView(self) self.RefreshCurrentIndexList() - + def RefreshBufferState(self): NetworkEditorTemplate.RefreshBufferState(self) self.ParentWindow.RefreshTitle() self.ParentWindow.RefreshFileMenu() self.ParentWindow.RefreshEditMenu() self.ParentWindow.RefreshPageTitles() - + def OnNodeSelectedChanged(self, event): NetworkEditorTemplate.OnNodeSelectedChanged(self, event) wx.CallAfter(self.ParentWindow.RefreshEditMenu) diff -r c1298e7ffe3a -r 8391c11477f4 canfestival/SlaveEditor.py --- a/canfestival/SlaveEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/canfestival/SlaveEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,39 +22,43 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx from subindextable import EditingPanel from nodeeditortemplate import NodeEditorTemplate from editors.ConfTreeNodeEditor import ConfTreeNodeEditor -[ID_SLAVEEDITORCONFNODEMENUNODEINFOS, ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, - ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, - ID_SLAVEEDITORCONFNODEMENUADD, +[ + ID_SLAVEEDITORCONFNODEMENUNODEINFOS, ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, + ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, + ID_SLAVEEDITORCONFNODEMENUADD, ] = [wx.NewId() for _init_coll_ConfNodeMenu_Items in range(5)] -[ID_SLAVEEDITORADDMENUSDOSERVER, ID_SLAVEEDITORADDMENUSDOCLIENT, - ID_SLAVEEDITORADDMENUPDOTRANSMIT, ID_SLAVEEDITORADDMENUPDORECEIVE, - ID_SLAVEEDITORADDMENUMAPVARIABLE, ID_SLAVEEDITORADDMENUUSERTYPE, +[ + ID_SLAVEEDITORADDMENUSDOSERVER, ID_SLAVEEDITORADDMENUSDOCLIENT, + ID_SLAVEEDITORADDMENUPDOTRANSMIT, ID_SLAVEEDITORADDMENUPDORECEIVE, + ID_SLAVEEDITORADDMENUMAPVARIABLE, ID_SLAVEEDITORADDMENUUSERTYPE, ] = [wx.NewId() for _init_coll_AddMenu_Items in range(6)] + class SlaveEditor(ConfTreeNodeEditor, NodeEditorTemplate): - + CONFNODEEDITOR_TABS = [ (_("CANOpen slave"), "_create_SlaveNodeEditor")] - + def _create_SlaveNodeEditor(self, prnt): self.SlaveNodeEditor = EditingPanel(prnt, self, self.Controler, self.Editable) return self.SlaveNodeEditor - + def __init__(self, parent, controler, window, editable=True): self.Editable = editable ConfTreeNodeEditor.__init__(self, parent, controler, window) NodeEditorTemplate.__init__(self, controler, window, False) - + def __del__(self): self.Controler.OnCloseEditor(self) - + def GetConfNodeMenuItems(self): if self.Editable: add_menu = [(wx.ITEM_NORMAL, (_('SDO Server'), ID_SLAVEEDITORADDMENUSDOSERVER, '', self.OnAddSDOServerMenu)), @@ -63,23 +67,23 @@ (wx.ITEM_NORMAL, (_('PDO Receive'), ID_SLAVEEDITORADDMENUPDORECEIVE, '', self.OnAddPDOReceiveMenu)), (wx.ITEM_NORMAL, (_('Map Variable'), ID_SLAVEEDITORADDMENUMAPVARIABLE, '', self.OnAddMapVariableMenu)), (wx.ITEM_NORMAL, (_('User Type'), ID_SLAVEEDITORADDMENUUSERTYPE, '', self.OnAddUserTypeMenu))] - + profile = self.Controler.GetCurrentProfileName() if profile not in ("None", "DS-301"): other_profile_text = _("%s Profile") % profile add_menu.append((wx.ITEM_SEPARATOR, None)) - for text, indexes in self.Manager.GetCurrentSpecificMenu(): + for text, _indexes in self.Manager.GetCurrentSpecificMenu(): add_menu.append((wx.ITEM_NORMAL, (text, wx.NewId(), '', self.GetProfileCallBack(text)))) else: other_profile_text = _('Other Profile') - + return [(wx.ITEM_NORMAL, (_('DS-301 Profile'), ID_SLAVEEDITORCONFNODEMENUDS301PROFILE, '', self.OnCommunicationMenu)), (wx.ITEM_NORMAL, (_('DS-302 Profile'), ID_SLAVEEDITORCONFNODEMENUDS302PROFILE, '', self.OnOtherCommunicationMenu)), (wx.ITEM_NORMAL, (other_profile_text, ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, '', self.OnEditProfileMenu)), (wx.ITEM_SEPARATOR, None), (add_menu, (_('Add'), ID_SLAVEEDITORCONFNODEMENUADD))] return [] - + def RefreshConfNodeMenu(self, confnode_menu): if self.Editable: confnode_menu.Enable(ID_SLAVEEDITORCONFNODEMENUDSOTHERPROFILE, False) @@ -90,37 +94,37 @@ def RefreshCurrentIndexList(self): self.RefreshView() - + def RefreshBufferState(self): self.ParentWindow.RefreshTitle() self.ParentWindow.RefreshFileMenu() self.ParentWindow.RefreshEditMenu() self.ParentWindow.RefreshPageTitles() + class MasterViewer(SlaveEditor): SHOW_BASE_PARAMS = False SHOW_PARAMS = False - + def __init__(self, parent, controler, window, tagname): SlaveEditor.__init__(self, parent, controler, window, False) - + self.TagName = tagname - + def GetTagName(self): return self.TagName - + def GetCurrentNodeId(self): return None - + def GetInstancePath(self): return self.Controler.CTNFullName() + ".generated_master" - + def GetTitle(self): return self.GetInstancePath() - + def IsViewing(self, tagname): return self.GetInstancePath() == tagname def RefreshView(self): self.SlaveNodeEditor.RefreshIndexList() - diff -r c1298e7ffe3a -r 8391c11477f4 canfestival/__init__.py --- a/canfestival/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/canfestival/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,4 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from canfestival import * +from __future__ import absolute_import +from canfestival.canfestival import * diff -r c1298e7ffe3a -r 8391c11477f4 canfestival/canfestival.py --- a/canfestival/canfestival.py Fri Mar 24 12:07:47 2017 +0000 +++ b/canfestival/canfestival.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,36 +23,50 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys, shutil - -base_folder = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) -CanFestivalPath = os.path.join(base_folder, "CanFestival-3") -sys.path.append(os.path.join(CanFestivalPath, "objdictgen")) - +from __future__ import absolute_import +import os +import sys +import shutil import wx - -from nodelist import NodeList +from gnosis.xml.pickle import * +from gnosis.xml.pickle.util import setParanoia + +import util.paths as paths +from util.TranslationCatalogs import AddCatalog +from ConfigTreeNode import ConfigTreeNode +from PLCControler import \ + LOCATION_CONFNODE, \ + LOCATION_VAR_MEMORY + +try: + from nodelist import NodeList +except ImportError: + base_folder = paths.AbsParentDir(__file__, 2) + CanFestivalPath = os.path.join(base_folder, "CanFestival-3") + sys.path.append(os.path.join(CanFestivalPath, "objdictgen")) + + from nodelist import NodeList + from nodemanager import NodeManager -import config_utils, gen_cfile, eds_utils +import gen_cfile +import eds_utils import canfestival_config as local_canfestival_config -from ConfigTreeNode import ConfigTreeNode + from commondialogs import CreateNodeDialog from subindextable import IECTypeConversion, SizeConversion - -from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY -from SlaveEditor import SlaveEditor, MasterViewer -from NetworkEditor import NetworkEditor - -from gnosis.xml.pickle import * -from gnosis.xml.pickle.util import setParanoia +from canfestival import config_utils +from canfestival.SlaveEditor import SlaveEditor, MasterViewer +from canfestival.NetworkEditor import NetworkEditor + + +AddCatalog(os.path.join(CanFestivalPath, "objdictgen", "locale")) setParanoia(0) -from util.TranslationCatalogs import AddCatalog -AddCatalog(os.path.join(CanFestivalPath, "objdictgen", "locale")) - -#-------------------------------------------------- + +# -------------------------------------------------- # Location Tree Helper -#-------------------------------------------------- +# -------------------------------------------------- + def GetSlaveLocationTree(slave_node, current_location, name): entries = [] @@ -65,19 +80,19 @@ "size": size, "IEC_type": IECTypeConversion.get(typeinfos["name"]), "var_name": "%s_%4.4x_%2.2x" % ("_".join(name.split()), index, subindex), - "location": "%s%s"%(SizeConversion[size], ".".join(map(str, current_location + - (index, subindex)))), + "location": "%s%s" % (SizeConversion[size], ".".join(map(str, current_location + + (index, subindex)))), "description": "", "children": []}) - return {"name": name, - "type": LOCATION_CONFNODE, - "location": ".".join([str(i) for i in current_location]) + ".x", - "children": entries - } - -#-------------------------------------------------- + return {"name": name, + "type": LOCATION_CONFNODE, + "location": ".".join([str(i) for i in current_location]) + ".x", + "children": entries} + +# -------------------------------------------------- # SLAVE -#-------------------------------------------------- +# -------------------------------------------------- + class _SlaveCTN(NodeManager): XSD = """ @@ -100,7 +115,7 @@ """ - + EditorType = SlaveEditor IconPath = os.path.join(CanFestivalPath, "objdictgen", "networkedit.png") @@ -108,7 +123,7 @@ # TODO change netname when name change NodeManager.__init__(self) odfilepath = self.GetSlaveODPath() - if(os.path.isfile(odfilepath)): + if os.path.isfile(odfilepath): self.OpenFileInCurrent(odfilepath) else: self.FilePath = "" @@ -116,25 +131,25 @@ dialog.Type.Enable(False) dialog.GenSYNC.Enable(False) if dialog.ShowModal() == wx.ID_OK: - name, id, nodetype, description = dialog.GetValues() + name, id, _nodetype, description = dialog.GetValues() profile, filepath = dialog.GetProfile() NMT = dialog.GetNMTManagement() options = dialog.GetOptions() - self.CreateNewNode(name, # Name - will be changed at build time - id, # NodeID - will be changed at build time - "slave", # Type - description,# description - profile, # profile - filepath, # prfile filepath - NMT, # NMT - options) # options + self.CreateNewNode(name, # Name - will be changed at build time + id, # NodeID - will be changed at build time + "slave", # Type + description, # description + profile, # profile + filepath, # prfile filepath + NMT, # NMT + options) # options else: self.CreateNewNode("SlaveNode", # Name - will be changed at build time 0x00, # NodeID - will be changed at build time "slave", # Type - "", # description + "", # description "None", # profile - "", # prfile filepath + "", # prfile filepath "heartbeat", # NMT []) # options dialog.Destroy() @@ -154,48 +169,49 @@ if self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) return self._View - + def _ExportSlave(self): - dialog = wx.FileDialog(self.GetCTRoot().AppFrame, - _("Choose a file"), - os.path.expanduser("~"), - "%s.eds" % self.CTNName(), + dialog = wx.FileDialog(self.GetCTRoot().AppFrame, + _("Choose a file"), + os.path.expanduser("~"), + "%s.eds" % self.CTNName(), _("EDS files (*.eds)|*.eds|All files|*.*"), - wx.SAVE|wx.OVERWRITE_PROMPT) + wx.SAVE | wx.OVERWRITE_PROMPT) if dialog.ShowModal() == wx.ID_OK: result = eds_utils.GenerateEDSFile(dialog.GetPath(), self.GetCurrentNodeCopy()) if result: self.GetCTRoot().logger.write_error(_("Error: Export slave failed\n")) - dialog.Destroy() - + dialog.Destroy() + ConfNodeMethods = [ - {"bitmap" : "ExportSlave", - "name" : _("Export slave"), - "tooltip" : _("Export CanOpen slave to EDS file"), - "method" : "_ExportSlave"}, + { + "bitmap": "ExportSlave", + "name": _("Export slave"), + "tooltip": _("Export CanOpen slave to EDS file"), + "method": "_ExportSlave" + }, ] - + def CTNTestModified(self): return self.ChangesToSave or self.OneFileHasChanged() - + def OnCTNSave(self, from_project_path=None): return self.SaveCurrentInFile(self.GetSlaveODPath()) def SetParamsAttribute(self, path, value): result = ConfigTreeNode.SetParamsAttribute(self, path, value) - + # Filter IEC_Channel and Name, that have specific behavior if path == "BaseParams.IEC_Channel" and self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) - + return result - + def GetVariableLocationTree(self): - current_location = self.GetCurrentLocation() - return GetSlaveLocationTree(self.CurrentNode, - self.GetCurrentLocation(), + return GetSlaveLocationTree(self.CurrentNode, + self.GetCurrentLocation(), self.BaseParams.getName()) - + def CTNGenerate_C(self, buildpath, locations): """ Generate C code @@ -212,75 +228,78 @@ current_location = self.GetCurrentLocation() # define a unique name for the generated C file prefix = "_".join(map(str, current_location)) - Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix ) + Gen_OD_path = os.path.join(buildpath, "OD_%s.c" % prefix) # Create a new copy of the model slave = self.GetCurrentNodeCopy() - slave.SetNodeName("OD_%s"%prefix) + slave.SetNodeName("OD_%s" % prefix) # allow access to local OD from Slave PLC pointers = config_utils.LocalODPointers(locations, current_location, slave) res = gen_cfile.GenerateFile(Gen_OD_path, slave, pointers) - if res : - raise Exception, res - res = eds_utils.GenerateEDSFile(os.path.join(buildpath, "Slave_%s.eds"%prefix), slave) - if res : - raise Exception, res - return [(Gen_OD_path,local_canfestival_config.getCFLAGS(CanFestivalPath))],"",False + if res: + raise Exception(res) + res = eds_utils.GenerateEDSFile(os.path.join(buildpath, "Slave_%s.eds" % prefix), slave) + if res: + raise Exception(res) + return [(Gen_OD_path, local_canfestival_config.getCFLAGS(CanFestivalPath))], "", False def LoadPrevious(self): self.LoadCurrentPrevious() - + def LoadNext(self): self.LoadCurrentNext() - + def GetBufferState(self): return self.GetCurrentBufferState() -#-------------------------------------------------- +# -------------------------------------------------- # MASTER -#-------------------------------------------------- +# -------------------------------------------------- + class MiniNodeManager(NodeManager): - + def __init__(self, parent, filepath, fullname): NodeManager.__init__(self) - + self.OpenFileInCurrent(filepath) - + self.Parent = parent self.Fullname = fullname - + def GetIconName(self): return None - + def OnCloseEditor(self, view): self.Parent.OnCloseEditor(view) - + def CTNFullName(self): return self.Fullname - + def CTNTestModified(self): return False - + def GetBufferState(self): return self.GetCurrentBufferState() - + ConfNodeMethods = [] + class _NodeManager(NodeManager): def __init__(self, parent, *args, **kwargs): NodeManager.__init__(self, *args, **kwargs) self.Parent = parent - + def __del__(self): self.Parent = None - + def GetCurrentNodeName(self): return self.Parent.CTNName() - + def GetCurrentNodeID(self): return self.Parent.CanFestivalNode.getNodeId() - + + class _NodeListCTN(NodeList): XSD = """ @@ -293,20 +312,20 @@ - """ - + """ + EditorType = NetworkEditor IconPath = os.path.join(CanFestivalPath, "objdictgen", "networkedit.png") - + def __init__(self): manager = _NodeManager(self) NodeList.__init__(self, manager) self.LoadProject(self.CTNPath()) self.SetNetworkName(self.BaseParams.getName()) - + def GetCanDevice(self): return self.CanFestivalNode.getCAN_Device() - + def SetParamsAttribute(self, path, value): if path == "CanFestivalNode.NodeId": nodeid = self.CanFestivalNode.getNodeId() @@ -317,10 +336,10 @@ value += dir if value < 0: value = nodeid - + value, refresh = ConfigTreeNode.SetParamsAttribute(self, path, value) refresh_network = False - + # Filter IEC_Channel and Name, that have specific behavior if path == "BaseParams.IEC_Channel" and self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) @@ -329,31 +348,35 @@ refresh_network = True elif path == "CanFestivalNode.NodeId": refresh_network = True - + if refresh_network and self._View is not None: wx.CallAfter(self._View.RefreshBufferState) return value, refresh - + def GetVariableLocationTree(self): current_location = self.GetCurrentLocation() nodeindexes = self.SlaveNodes.keys() nodeindexes.sort() - return {"name": self.BaseParams.getName(), - "type": LOCATION_CONFNODE, - "location": self.GetFullIEC_Channel(), - "children": [GetSlaveLocationTree(self.Manager.GetCurrentNodeCopy(), - current_location, - _("Local entries"))] + - [GetSlaveLocationTree(self.SlaveNodes[nodeid]["Node"], - current_location + (nodeid,), - self.SlaveNodes[nodeid]["Name"]) - for nodeid in nodeindexes] + children = [] + children += [GetSlaveLocationTree(self.Manager.GetCurrentNodeCopy(), + current_location, + _("Local entries"))] + children += [GetSlaveLocationTree(self.SlaveNodes[nodeid]["Node"], + current_location + (nodeid,), + self.SlaveNodes[nodeid]["Name"]) for nodeid in nodeindexes] + + return { + "name": self.BaseParams.getName(), + "type": LOCATION_CONFNODE, + "location": self.GetFullIEC_Channel(), + "children": children } - + _GeneratedMasterView = None + def _ShowGeneratedMaster(self): self._OpenView("Generated master") - + def _OpenView(self, name=None, onlyopened=False): if name == "Generated master": app_frame = self.GetCTRoot().AppFrame @@ -363,37 +386,39 @@ if not os.path.exists(buildpath): self.GetCTRoot().logger.write_error(_("Error: No PLC built\n")) return - + masterpath = os.path.join(buildpath, "MasterGenerated.od") if not os.path.exists(masterpath): self.GetCTRoot().logger.write_error(_("Error: No Master generated\n")) return - + manager = MiniNodeManager(self, masterpath, self.CTNFullName()) self._GeneratedMasterView = MasterViewer(app_frame.TabsOpened, manager, app_frame, name) - + if self._GeneratedMasterView is not None: app_frame.EditProjectElement(self._GeneratedMasterView, self._GeneratedMasterView.GetInstancePath()) - + return self._GeneratedMasterView else: ConfigTreeNode._OpenView(self, name, onlyopened) if self._View is not None: self._View.SetBusId(self.GetCurrentLocation()) return self._View - + ConfNodeMethods = [ - {"bitmap" : "ShowMaster", - "name" : _("Show Master"), - "tooltip" : _("Show Master generated by config_utils"), - "method" : "_ShowGeneratedMaster"} + { + "bitmap": "ShowMaster", + "name": _("Show Master"), + "tooltip": _("Show Master generated by config_utils"), + "method": "_ShowGeneratedMaster" + } ] - + def OnCloseEditor(self, view): ConfigTreeNode.OnCloseEditor(self, view) if self._GeneratedMasterView == view: self._GeneratedMasterView = None - + def OnCTNClose(self): ConfigTreeNode.OnCTNClose(self) self._CloseView(self._GeneratedMasterView) @@ -401,11 +426,11 @@ def CTNTestModified(self): return self.ChangesToSave or self.HasChanged() - + def OnCTNSave(self, from_project_path=None): self.SetRoot(self.CTNPath()) if from_project_path is not None: - shutil.copytree(self.GetEDSFolder(from_project_path), + shutil.copytree(self.GetEDSFolder(from_project_path), self.GetEDSFolder()) return self.SaveProject() is None @@ -426,33 +451,34 @@ current_location = self.GetCurrentLocation() # define a unique name for the generated C file prefix = "_".join(map(str, current_location)) - Gen_OD_path = os.path.join(buildpath, "OD_%s.c"%prefix ) + Gen_OD_path = os.path.join(buildpath, "OD_%s.c" % prefix) # Create a new copy of the model with DCF loaded with PDO mappings for desired location try: - master, pointers = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(),"OD_%s"%prefix) + master, pointers = config_utils.GenerateConciseDCF(locations, current_location, self, self.CanFestivalNode.getSync_TPDOs(), "OD_%s" % prefix) except config_utils.PDOmappingException, e: - raise Exception, e.message + raise Exception(e.message) # Do generate C file. res = gen_cfile.GenerateFile(Gen_OD_path, master, pointers) - if res : - raise Exception, res - + if res: + raise Exception(res) + file = open(os.path.join(buildpath, "MasterGenerated.od"), "w") dump(master, file) file.close() - - return [(Gen_OD_path,local_canfestival_config.getCFLAGS(CanFestivalPath))],"",False - + + return [(Gen_OD_path, local_canfestival_config.getCFLAGS(CanFestivalPath))], "", False + def LoadPrevious(self): self.Manager.LoadCurrentPrevious() - + def LoadNext(self): self.Manager.LoadCurrentNext() - + def GetBufferState(self): return self.Manager.GetCurrentBufferState() - -class RootClass: + + +class RootClass(object): XSD = """ @@ -462,29 +488,30 @@ """ - - CTNChildrenTypes = [("CanOpenNode",_NodeListCTN, "CanOpen Master"), - ("CanOpenSlave",_SlaveCTN, "CanOpen Slave")] - def GetParamsAttributes(self, path = None): - infos = ConfigTreeNode.GetParamsAttributes(self, path = path) + + CTNChildrenTypes = [("CanOpenNode", _NodeListCTN, "CanOpen Master"), + ("CanOpenSlave", _SlaveCTN, "CanOpen Slave")] + + def GetParamsAttributes(self, path=None): + infos = ConfigTreeNode.GetParamsAttributes(self, path=path) for element in infos: if element["name"] == "CanFestivalInstance": for child in element["children"]: if child["name"] == "CAN_Driver": child["type"] = local_canfestival_config.DLL_LIST return infos - + def GetCanDriver(self): res = self.CanFestivalInstance.getCAN_Driver() - if not res : + if not res: return "" return res - + def CTNGenerate_C(self, buildpath, locations): can_driver = self.GetCanDriver() if can_driver is not None: can_drivers = local_canfestival_config.DLL_LIST - if can_driver not in can_drivers : + if can_driver not in can_drivers: can_driver = can_drivers[0] can_drv_ext = self.GetCTRoot().GetBuilder().extension can_drv_prefix = self.GetCTRoot().GetBuilder().dlopen_prefix @@ -492,106 +519,105 @@ else: can_driver_name = "" - - format_dict = {"locstr" : "_".join(map(str,self.GetCurrentLocation())), - "candriver" : can_driver_name, - "nodes_includes" : "", - "board_decls" : "", - "nodes_init" : "", - "nodes_open" : "", - "nodes_stop" : "", - "nodes_close" : "", - "nodes_send_sync" : "", - "nodes_proceed_sync" : "", - "slavebootups" : "", - "slavebootup_register" : "", - "post_sync" : "", - "post_sync_register" : "", - "pre_op" : "", - "pre_op_register" : "", - } + format_dict = { + "locstr": "_".join(map(str, self.GetCurrentLocation())), + "candriver": can_driver_name, + "nodes_includes": "", + "board_decls": "", + "nodes_init": "", + "nodes_open": "", + "nodes_stop": "", + "nodes_close": "", + "nodes_send_sync": "", + "nodes_proceed_sync": "", + "slavebootups": "", + "slavebootup_register": "", + "post_sync": "", + "post_sync_register": "", + "pre_op": "", + "pre_op_register": "", + } for child in self.IECSortedChildren(): - childlocstr = "_".join(map(str,child.GetCurrentLocation())) + childlocstr = "_".join(map(str, child.GetCurrentLocation())) nodename = "OD_%s" % childlocstr - + # Try to get Slave Node child_data = getattr(child, "CanFestivalSlaveNode", None) if child_data is None: # Not a slave -> master child_data = getattr(child, "CanFestivalNode") # Apply sync setting - format_dict["nodes_init"] += 'NODE_MASTER_INIT(%s, %s)\n '%( - nodename, - child_data.getNodeId()) + format_dict["nodes_init"] += 'NODE_MASTER_INIT(%s, %s)\n ' % ( + nodename, + child_data.getNodeId()) if child_data.getSync_TPDOs(): - format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n '%(nodename) - format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n '%(nodename) + format_dict["nodes_send_sync"] += 'NODE_SEND_SYNC(%s)\n ' % (nodename) + format_dict["nodes_proceed_sync"] += 'NODE_PROCEED_SYNC(%s)\n ' % (nodename) # initialize and declare node boot status variables for post_SlaveBootup lookup SlaveIDs = child.GetSlaveIDs() if len(SlaveIDs) == 0: # define post_SlaveBootup lookup functions format_dict["slavebootups"] += ( - "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){}\n"%(nodename)) + "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){}\n" % (nodename)) else: format_dict["slavebootups"] += ( - "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){\n"%(nodename)+ - " check_and_start_node(d, nodeId);\n"+ + "static void %s_post_SlaveBootup(CO_Data* d, UNS8 nodeId){\n" % (nodename) + + " check_and_start_node(d, nodeId);\n" + "}\n") # register previously declared func as post_SlaveBootup callback for that node format_dict["slavebootup_register"] += ( - "%s_Data.post_SlaveBootup = %s_post_SlaveBootup;\n"%(nodename,nodename)) + "%s_Data.post_SlaveBootup = %s_post_SlaveBootup;\n" % (nodename, nodename)) format_dict["pre_op"] += ( - "static void %s_preOperational(CO_Data* d){\n "%(nodename)+ - "".join([" masterSendNMTstateChange(d, %d, NMT_Reset_Comunication);\n"%NdId for NdId in SlaveIDs])+ + "static void %s_preOperational(CO_Data* d){\n " % (nodename) + + "".join([" masterSendNMTstateChange(d, %d, NMT_Reset_Comunication);\n" % NdId for NdId in SlaveIDs]) + "}\n") format_dict["pre_op_register"] += ( - "%s_Data.preOperational = %s_preOperational;\n"%(nodename,nodename)) + "%s_Data.preOperational = %s_preOperational;\n" % (nodename, nodename)) else: # Slave node align = child_data.getSync_Align() - align_ratio=child_data.getSync_Align_Ratio() + align_ratio = child_data.getSync_Align_Ratio() if align > 0: format_dict["post_sync"] += ( - "static int %s_CalCount = 0;\n"%(nodename)+ - "static void %s_post_sync(CO_Data* d){\n"%(nodename)+ - " if(%s_CalCount < %d){\n"%(nodename, align)+ - " %s_CalCount++;\n"%(nodename)+ - " align_tick(-1);\n"+ - " }else{\n"+ - " align_tick(%d);\n"%(align_ratio)+ - " }\n"+ + "static int %s_CalCount = 0;\n" % (nodename) + + "static void %s_post_sync(CO_Data* d){\n" % (nodename) + + " if(%s_CalCount < %d){\n" % (nodename, align) + + " %s_CalCount++;\n" % (nodename) + + " align_tick(-1);\n" + + " }else{\n" + + " align_tick(%d);\n" % (align_ratio) + + " }\n" + "}\n") format_dict["post_sync_register"] += ( - "%s_Data.post_sync = %s_post_sync;\n"%(nodename,nodename)) - format_dict["nodes_init"] += 'NODE_SLAVE_INIT(%s, %s)\n '%( - nodename, - child_data.getNodeId()) - + "%s_Data.post_sync = %s_post_sync;\n" % (nodename, nodename)) + format_dict["nodes_init"] += 'NODE_SLAVE_INIT(%s, %s)\n ' % ( + nodename, + child_data.getNodeId()) + # Include generated OD headers - format_dict["nodes_includes"] += '#include "%s.h"\n'%(nodename) + format_dict["nodes_includes"] += '#include "%s.h"\n' % (nodename) # Declare CAN channels according user filled config - format_dict["board_decls"] += 'BOARD_DECL(%s, "%s", "%s")\n'%( - nodename, - child.GetCanDevice(), - child_data.getCAN_Baudrate()) - format_dict["nodes_open"] += 'NODE_OPEN(%s)\n '%(nodename) - format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n '%(nodename) - format_dict["nodes_stop"] += 'NODE_STOP(%s)\n '%(nodename) - - filename = os.path.join(os.path.split(__file__)[0],"cf_runtime.c") + format_dict["board_decls"] += 'BOARD_DECL(%s, "%s", "%s")\n' % ( + nodename, + child.GetCanDevice(), + child_data.getCAN_Baudrate()) + format_dict["nodes_open"] += 'NODE_OPEN(%s)\n ' % (nodename) + format_dict["nodes_close"] += 'NODE_CLOSE(%s)\n ' % (nodename) + format_dict["nodes_stop"] += 'NODE_STOP(%s)\n ' % (nodename) + + filename = paths.AbsNeighbourFile(__file__, "cf_runtime.c") cf_main = open(filename).read() % format_dict - cf_main_path = os.path.join(buildpath, "CF_%(locstr)s.c"%format_dict) - f = open(cf_main_path,'w') + cf_main_path = os.path.join(buildpath, "CF_%(locstr)s.c" % format_dict) + f = open(cf_main_path, 'w') f.write(cf_main) f.close() - res = [(cf_main_path, local_canfestival_config.getCFLAGS(CanFestivalPath))],local_canfestival_config.getLDFLAGS(CanFestivalPath), True - + res = [(cf_main_path, local_canfestival_config.getCFLAGS(CanFestivalPath))], local_canfestival_config.getLDFLAGS(CanFestivalPath), True + if can_driver is not None: - can_driver_path = os.path.join(CanFestivalPath,"drivers",can_driver,can_driver_name) + can_driver_path = os.path.join(CanFestivalPath, "drivers", can_driver, can_driver_name) if os.path.exists(can_driver_path): - res += ((can_driver_name, file(can_driver_path,"rb")),) + res += ((can_driver_name, file(can_driver_path, "rb")),) return res - diff -r c1298e7ffe3a -r 8391c11477f4 canfestival/config_utils.py --- a/canfestival/config_utils.py Fri Mar 24 12:07:47 2017 +0000 +++ b/canfestival/config_utils.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,33 +22,55 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +from __future__ import print_function +import os +import sys +import getopt from types import * # Translation between IEC types and Can Open types -IECToCOType = {"BOOL":0x01, "SINT":0x02, "INT":0x03,"DINT":0x04,"LINT":0x10, - "USINT":0x05,"UINT":0x06,"UDINT":0x07,"ULINT":0x1B,"REAL":0x08, - "LREAL":0x11,"STRING":0x09,"BYTE":0x05,"WORD":0x06,"DWORD":0x07, - "LWORD":0x1B,"WSTRING":0x0B} - -# Constants for PDO types +IECToCOType = { + "BOOL": 0x01, + "SINT": 0x02, + "INT": 0x03, + "DINT": 0x04, + "LINT": 0x10, + "USINT": 0x05, + "UINT": 0x06, + "UDINT": 0x07, + "ULINT": 0x1B, + "REAL": 0x08, + "LREAL": 0x11, + "STRING": 0x09, + "BYTE": 0x05, + "WORD": 0x06, + "DWORD": 0x07, + "LWORD": 0x1B, + "WSTRING": 0x0B +} + +# Constants for PDO types RPDO = 1 TPDO = 2 -SlavePDOType = {"I" : TPDO, "Q" : RPDO} -InvertPDOType = {RPDO : TPDO, TPDO : RPDO} -PDOTypeBaseIndex = {RPDO : 0x1400, TPDO : 0x1800} -PDOTypeBaseCobId = {RPDO : 0x200, TPDO : 0x180} +SlavePDOType = {"I": TPDO, "Q": RPDO} +InvertPDOType = {RPDO: TPDO, TPDO: RPDO} +PDOTypeBaseIndex = {RPDO: 0x1400, TPDO: 0x1800} +PDOTypeBaseCobId = {RPDO: 0x200, TPDO: 0x180} VariableIncrement = 0x100 -VariableStartIndex = {TPDO : 0x2000, RPDO : 0x4000} -VariableDirText = {TPDO : "__I", RPDO : "__Q"} -VariableTypeOffset = dict(zip(["","X","B","W","D","L"], range(6))) +VariableStartIndex = {TPDO: 0x2000, RPDO: 0x4000} +VariableDirText = {TPDO: "__I", RPDO: "__Q"} +VariableTypeOffset = dict(zip(["", "X", "B", "W", "D", "L"], range(6))) TrashVariables = [(1, 0x01), (8, 0x05), (16, 0x06), (32, 0x07), (64, 0x1B)] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Specific exception for PDO mapping errors -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class PDOmappingException(Exception): pass @@ -61,22 +83,22 @@ @param size: number of bytes generated @return: a string containing the value converted """ - + data = ("%" + str(size * 2) + "." + str(size * 2) + "X") % value list_car = [data[i:i+2] for i in xrange(0, len(data), 2)] list_car.reverse() return "".join([chr(int(car, 16)) for car in list_car]) -def GetNodePDOIndexes(node, type, parameters = False): +def GetNodePDOIndexes(node, type, parameters=False): """ Find the PDO indexes of a node - @param node: node + @param node: node @param type: type of PDO searched (RPDO or TPDO or both) @param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False) @return: a list of indexes found """ - + indexes = [] if type & RPDO: indexes.extend([idx for idx in node.GetIndexes() if 0x1400 <= idx <= 0x15FF]) @@ -91,17 +113,17 @@ def SearchNodePDOMapping(loc_infos, node): """ Find the PDO indexes of a node - @param node: node + @param node: node @param type: type of PDO searched (RPDO or TPDO or both) @param parameters: indicate which indexes are expected (PDO paramaters : True or PDO mappings : False) @return: a list of indexes found """ - + model = (loc_infos["index"] << 16) + (loc_infos["subindex"] << 8) - + for PDOidx in GetNodePDOIndexes(node, loc_infos["pdotype"]): values = node.GetEntry(PDOidx) - if values != None: + if values is not None: for subindex, mapping in enumerate(values): if subindex != 0 and mapping & 0xFFFFFF00 == model: return PDOidx, subindex @@ -115,10 +137,10 @@ @param cobid: PDO generated COB ID @param transmittype : PDO transmit type @param pdomapping: list of PDO mappings - @return: a tuple of value and number of parameters to add to DCF - """ - - dcfdata=[] + @return: a tuple of value and number of parameters to add to DCF + """ + + dcfdata = [] # Create entry for RPDO or TPDO parameters and Disable PDO # ---- INDEX ----- --- SUBINDEX ---- ----- SIZE ------ ------ DATA ------ dcfdata += [LE_to_BE(idx, 2) + LE_to_BE(0x01, 1) + LE_to_BE(0x04, 4) + LE_to_BE(0x80000000 + cobid, 4)] @@ -128,7 +150,7 @@ # Disable Mapping dcfdata += [LE_to_BE(idx + 0x200, 2) + LE_to_BE(0x00, 1) + LE_to_BE(0x01, 4) + LE_to_BE(0x00, 1)] # Map Variables - for subindex, (name, loc_infos) in enumerate(pdomapping): + for subindex, (_name, loc_infos) in enumerate(pdomapping): value = (loc_infos["index"] << 16) + (loc_infos["subindex"] << 8) + loc_infos["size"] dcfdata += [LE_to_BE(idx + 0x200, 2) + LE_to_BE(subindex + 1, 1) + LE_to_BE(0x04, 4) + LE_to_BE(value, 4)] # Re-enable Mapping @@ -137,7 +159,8 @@ dcfdata += [LE_to_BE(idx, 2) + LE_to_BE(0x01, 1) + LE_to_BE(0x04, 4) + LE_to_BE(cobid, 4)] return "".join(dcfdata), len(dcfdata) -class ConciseDCFGenerator: + +class ConciseDCFGenerator(object): def __init__(self, nodelist, nodename): # Dictionary of location informations classed by name @@ -152,7 +175,7 @@ self.TrashVariables = {} # Dictionary of pointed variables self.PointedVariables = {} - + self.NodeList = nodelist self.Manager = self.NodeList.Manager self.MasterNode = self.Manager.GetCurrentNodeCopy() @@ -161,41 +184,40 @@ def GetPointedVariables(self): return self.PointedVariables - + def RemoveUsedNodeCobId(self, node): """ Remove all PDO COB ID used by the given node from the list of available COB ID @param node: node @return: a tuple of number of RPDO and TPDO for the node """ - + # Get list of all node TPDO and RPDO indexes nodeRpdoIndexes = GetNodePDOIndexes(node, RPDO, True) nodeTpdoIndexes = GetNodePDOIndexes(node, TPDO, True) - + # Mark all the COB ID of the node already mapped PDO as not available for PdoIdx in nodeRpdoIndexes + nodeTpdoIndexes: pdo_cobid = node.GetEntry(PdoIdx, 0x01) # Extract COB ID, if PDO isn't active - if pdo_cobid > 0x600 : + if pdo_cobid > 0x600: pdo_cobid -= 0x80000000 # Remove COB ID from the list of available COB ID if pdo_cobid in self.ListCobIDAvailable: self.ListCobIDAvailable.remove(pdo_cobid) - + return len(nodeRpdoIndexes), len(nodeTpdoIndexes) - def PrepareMasterNode(self): """ Add mandatory entries for DCF generation into MasterNode. """ - + # Adding DCF entry into Master node if not self.MasterNode.IsEntry(0x1F22): self.MasterNode.AddEntry(0x1F22, 1, "") self.Manager.AddSubentriesToCurrent(0x1F22, 127, self.MasterNode) - + # Adding trash mappable variables for unused mapped datas idxTrashVariables = 0x2000 + self.MasterNode.GetNodeID() # Add an entry for storing unexpected all variable @@ -206,40 +228,39 @@ self.Manager.SetCurrentEntry(idxTrashVariables, subidx + 1, typeidx, "type", None, self.MasterNode) # Store the mapping value for this entry self.TrashVariables[size] = (idxTrashVariables << 16) + ((subidx + 1) << 8) + size - + RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(self.MasterNode) - + # Store the indexes of the first RPDO and TPDO available for MasterNode - self.CurrentPDOParamsIdx = {RPDO : 0x1400 + RPDOnumber, TPDO : 0x1800 + TPDOnumber} + self.CurrentPDOParamsIdx = {RPDO: 0x1400 + RPDOnumber, TPDO: 0x1800 + TPDOnumber} # Prepare MasterNode with all nodelist slaves for idx, (nodeid, nodeinfos) in enumerate(self.NodeList.SlaveNodes.items()): node = nodeinfos["Node"] node.SetNodeID(nodeid) - + RPDOnumber, TPDOnumber = self.RemoveUsedNodeCobId(node) - + # Get Slave's default SDO server parameters - RSDO_cobid = node.GetEntry(0x1200,0x01) + RSDO_cobid = node.GetEntry(0x1200, 0x01) if not RSDO_cobid: RSDO_cobid = 0x600 + nodeid - TSDO_cobid = node.GetEntry(0x1200,0x02) + TSDO_cobid = node.GetEntry(0x1200, 0x02) if not TSDO_cobid: TSDO_cobid = 0x580 + nodeid - + # Configure Master's SDO parameters entries self.Manager.ManageEntriesOfCurrent([0x1280 + idx], [], self.MasterNode) self.MasterNode.SetEntry(0x1280 + idx, 0x01, RSDO_cobid) self.MasterNode.SetEntry(0x1280 + idx, 0x02, TSDO_cobid) - self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid) - - + self.MasterNode.SetEntry(0x1280 + idx, 0x03, nodeid) + def GetMasterNode(self): """ Return MasterNode. """ return self.MasterNode - + def AddParamsToDCF(self, nodeid, data, nbparams): """ Add entry to DCF, for the requested nodeID @@ -249,19 +270,19 @@ """ # Get current DCF for slave nodeDCF = self.MasterNode.GetEntry(0x1F22, nodeid) - + # Extract data and number of params in current DCF - if nodeDCF != None and nodeDCF != '': + if nodeDCF is not None and nodeDCF != '': tmpnbparams = [i for i in nodeDCF[:4]] tmpnbparams.reverse() - nbparams += int(''.join(["%2.2x"%ord(i) for i in tmpnbparams]), 16) + nbparams += int(''.join(["%2.2x" % ord(i) for i in tmpnbparams]), 16) data = nodeDCF[4:] + data - + # Build new DCF dcf = LE_to_BE(nbparams, 0x04) + data # Set new DCF for slave self.MasterNode.SetEntry(0x1F22, nodeid, dcf) - + def GetEmptyPDO(self, nodeid, pdotype, start_index=None): """ Search a not configured PDO for a slave @@ -275,12 +296,12 @@ index = PDOTypeBaseIndex[pdotype] else: index = start_index - + # Search for all PDO possible index until find a configurable PDO # starting from start_index while index < PDOTypeBaseIndex[pdotype] + 0x200: values = self.NodeList.GetSlaveNodeEntry(nodeid, index + 0x200) - if values != None and values[0] > 0: + if values is not None and values[0] > 0: # Check that all subindex upper than 0 equal 0 => configurable PDO if reduce(lambda x, y: x and y, map(lambda x: x == 0, values[1:]), True): cobid = self.NodeList.GetSlaveNodeEntry(nodeid, index, 1) @@ -296,7 +317,7 @@ return index, cobid, values[0] index += 1 return None - + def AddPDOMapping(self, nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs): """ Record a new mapping request for a slave, and add related slave config to the DCF @@ -305,16 +326,18 @@ @param pdomapping: list od variables to map with PDO """ # Add an entry to MasterMapping - self.MasterMapping[pdocobid] = {"type" : InvertPDOType[pdotype], - "mapping" : [None] + [(loc_infos["type"], name) for name, loc_infos in pdomapping]} - + self.MasterMapping[pdocobid] = { + "type": InvertPDOType[pdotype], + "mapping": [None] + [(loc_infos["type"], name) for name, loc_infos in pdomapping] + } + # Return the data to add to DCF if sync_TPDOs: return GeneratePDOMappingDCF(pdoindex, pdocobid, 0x01, pdomapping) else: return GeneratePDOMappingDCF(pdoindex, pdocobid, 0xFF, pdomapping) return 0, "" - + def GenerateDCF(self, locations, current_location, sync_TPDOs): """ Generate Concise DCF of MasterNode for the locations list given @@ -322,87 +345,101 @@ @param current_location: tuple of the located prefixes not to be considered @param sync_TPDOs: indicate if TPDO must be synchronous """ - - #------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- # Verify that locations correspond to real slave variables - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Get list of locations check if exists and mappables -> put them in IECLocations for location in locations: COlocationtype = IECToCOType[location["IEC_TYPE"]] name = location["NAME"] if name in self.IECLocations: if self.IECLocations[name]["type"] != COlocationtype: - raise PDOmappingException, _("Type conflict for location \"%s\"") % name + raise PDOmappingException(_("Type conflict for location \"%s\"") % name) else: # Get only the part of the location that concern this node loc = location["LOC"][len(current_location):] # loc correspond to (ID, INDEX, SUBINDEX [,BIT]) if len(loc) not in (2, 3, 4): - raise PDOmappingException, _("Bad location size : %s") % str(loc) + raise PDOmappingException(_("Bad location size : %s") % str(loc)) elif len(loc) == 2: continue - + direction = location["DIR"] - + sizelocation = location["SIZE"] - + # Extract and check nodeid nodeid, index, subindex = loc[:3] - + # Check Id is in slave node list if nodeid not in self.NodeList.SlaveNodes.keys(): - raise PDOmappingException, _("Non existing node ID : {a1} (variable {a2})").format(a1 = nodeid, a2 = name) - + raise PDOmappingException( + _("Non existing node ID : {a1} (variable {a2})"). + format(a1=nodeid, a2=name)) + # Get the model for this node (made from EDS) node = self.NodeList.SlaveNodes[nodeid]["Node"] - + # Extract and check index and subindex if not node.IsEntry(index, subindex): msg = _("No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})").\ - format(a1 = "%x" % index, a2 ="%x" % subindex, a3 = nodeid, a4 = name) - raise PDOmappingException, msg - + format(a1="%x" % index, a2="%x" % subindex, a3=nodeid, a4=name) + raise PDOmappingException(msg) + # Get the entry info subentry_infos = node.GetSubentryInfos(index, subindex) - + # If a PDO mappable if subentry_infos and subentry_infos["pdo"]: if sizelocation == "X" and len(loc) > 3: numbit = loc[3] elif sizelocation != "X" and len(loc) > 3: - msg = _("Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))").\ - format(a1 = name, a2 = nodeid, a3 = "%x" % index, a4 = "%x" % subindex) - raise PDOmappingException, msg + raise PDOmappingException( + _("Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))"). + format(a1=name, a2=nodeid, a3="%x" % index, a4="%x" % subindex)) else: numbit = None - + if location["IEC_TYPE"] != "BOOL" and subentry_infos["type"] != COlocationtype: - raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"").\ - format(a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name) - + raise PDOmappingException( + _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\""). + format(a1=location["IEC_TYPE"], + a2=COlocationtype, + a3=subentry_infos["type"], + a4=name)) + typeinfos = node.GetEntryInfos(COlocationtype) - self.IECLocations[name] = {"type":COlocationtype, "pdotype":SlavePDOType[direction], - "nodeid": nodeid, "index": index,"subindex": subindex, - "bit": numbit, "size": typeinfos["size"], "sizelocation": sizelocation} + self.IECLocations[name] = { + "type": COlocationtype, + "pdotype": SlavePDOType[direction], + "nodeid": nodeid, + "index": index, + "subindex": subindex, + "bit": numbit, + "size": typeinfos["size"], + "sizelocation": sizelocation + } else: - raise PDOmappingException, _("Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))").\ - format(a1 = name, a2 = nodeid, a3 = "%x" % index, a4 = "%x" % subindex) - - #------------------------------------------------------------------------------- + raise PDOmappingException( + _("Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))"). + format(a1=name, a2=nodeid, a3="%x" % index, a4="%x" % subindex)) + + # ------------------------------------------------------------------------------- # Search for locations already mapped - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + for name, locationinfos in self.IECLocations.items(): node = self.NodeList.SlaveNodes[locationinfos["nodeid"]]["Node"] - + # Search if slave has a PDO mapping this locations result = SearchNodePDOMapping(locationinfos, node) - if result != None: + if result is not None: index, subindex = result # Get COB ID of the PDO cobid = self.NodeList.GetSlaveNodeEntry(locationinfos["nodeid"], index - 0x200, 1) - + # Add PDO to MasterMapping if cobid not in self.MasterMapping.keys(): # Verify that PDO transmit type is conform to sync_TPDOs @@ -414,18 +451,18 @@ else: # Change TransmitType to ASYCHRONE data, nbparams = GeneratePDOMappingDCF(index - 0x200, cobid, 0xFF, []) - - # Add entry to slave dcf to change transmit type of + + # Add entry to slave dcf to change transmit type of self.AddParamsToDCF(locationinfos["nodeid"], data, nbparams) - + mapping = [None] values = node.GetEntry(index) # Store the size of each entry mapped in PDO for value in values[1:]: if value != 0: mapping.append(value % 0x100) - self.MasterMapping[cobid] = {"type" : InvertPDOType[locationinfos["pdotype"]], "mapping" : mapping} - + self.MasterMapping[cobid] = {"type": InvertPDOType[locationinfos["pdotype"]], "mapping": mapping} + # Indicate that this PDO entry must be saved if locationinfos["bit"] is not None: if not isinstance(self.MasterMapping[cobid]["mapping"][subindex], ListType): @@ -434,24 +471,24 @@ self.MasterMapping[cobid]["mapping"][subindex][locationinfos["bit"]] = (locationinfos["type"], name) else: self.MasterMapping[cobid]["mapping"][subindex] = (locationinfos["type"], name) - + else: # Add location to those that haven't been mapped yet if locationinfos["nodeid"] not in self.LocationsNotMapped.keys(): - self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO : [], RPDO : []} + self.LocationsNotMapped[locationinfos["nodeid"]] = {TPDO: [], RPDO: []} self.LocationsNotMapped[locationinfos["nodeid"]][locationinfos["pdotype"]].append((name, locationinfos)) - - #------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- # Build concise DCF for the others locations - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + for nodeid, locations in self.LocationsNotMapped.items(): node = self.NodeList.SlaveNodes[nodeid]["Node"] - + # Initialize number of params and data to add to node DCF nbparams = 0 dataparams = "" - + # Generate the best PDO mapping for each type of PDO for pdotype in (TPDO, RPDO): if len(locations[pdotype]) > 0: @@ -459,7 +496,8 @@ pdomapping = [] result = self.GetEmptyPDO(nodeid, pdotype) if result is None: - raise PDOmappingException, _("Unable to define PDO mapping for node %02x") % nodeid + raise PDOmappingException( + _("Unable to define PDO mapping for node %02x") % nodeid) pdoindex, pdocobid, pdonbparams = result for name, loc_infos in locations[pdotype]: pdosize += loc_infos["size"] @@ -473,7 +511,8 @@ pdomapping = [(name, loc_infos)] result = self.GetEmptyPDO(nodeid, pdotype, pdoindex + 1) if result is None: - raise PDOmappingException, _("Unable to define PDO mapping for node %02x") % nodeid + raise PDOmappingException( + _("Unable to define PDO mapping for node %02x") % nodeid) pdoindex, pdocobid, pdonbparams = result else: pdomapping.append((name, loc_infos)) @@ -483,78 +522,79 @@ data, nbaddedparams = self.AddPDOMapping(nodeid, pdotype, pdoindex, pdocobid, pdomapping, sync_TPDOs) dataparams += data nbparams += nbaddedparams - + # Add number of params and data to node DCF self.AddParamsToDCF(nodeid, dataparams, nbparams) - - #------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- # Master Node Configuration - #------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Generate Master's Configuration from informations stored in MasterMapping for cobid, pdo_infos in self.MasterMapping.items(): # Get next PDO index in MasterNode for this PDO type current_idx = self.CurrentPDOParamsIdx[pdo_infos["type"]] - + # Search if there is already a PDO in MasterNode with this cob id for idx in GetNodePDOIndexes(self.MasterNode, pdo_infos["type"], True): if self.MasterNode.GetEntry(idx, 1) == cobid: current_idx = idx - + # Add a PDO to MasterNode if not PDO have been found if current_idx == self.CurrentPDOParamsIdx[pdo_infos["type"]]: addinglist = [current_idx, current_idx + 0x200] self.Manager.ManageEntriesOfCurrent(addinglist, [], self.MasterNode) self.MasterNode.SetEntry(current_idx, 0x01, cobid) - + # Increment the number of PDO for this PDO type self.CurrentPDOParamsIdx[pdo_infos["type"]] += 1 - + # Change the transmit type of the PDO if sync_TPDOs: self.MasterNode.SetEntry(current_idx, 0x02, 0x01) else: self.MasterNode.SetEntry(current_idx, 0x02, 0xFF) - + mapping = [] for item in pdo_infos["mapping"]: if isinstance(item, ListType): mapping.extend(item) else: mapping.append(item) - + # Add some subentries to PDO mapping if there is not enough if len(mapping) > 1: self.Manager.AddSubentriesToCurrent(current_idx + 0x200, len(mapping) - 1, self.MasterNode) - + # Generate MasterNode's PDO mapping for subindex, variable in enumerate(mapping): if subindex == 0: continue new_index = False - + if isinstance(variable, (IntType, LongType)): # If variable is an integer then variable is unexpected self.MasterNode.SetEntry(current_idx + 0x200, subindex, self.TrashVariables[variable]) else: typeidx, varname = variable variable_infos = self.IECLocations[varname] - + # Calculate base index for storing variable - mapvariableidx = VariableStartIndex[variable_infos["pdotype"]] + \ - VariableTypeOffset[variable_infos["sizelocation"]] * VariableIncrement + \ - variable_infos["nodeid"] - + mapvariableidx = \ + VariableStartIndex[variable_infos["pdotype"]] + \ + VariableTypeOffset[variable_infos["sizelocation"]] * VariableIncrement + \ + variable_infos["nodeid"] + # Generate entry name - indexname = "%s%s%s_%d"%(VariableDirText[variable_infos["pdotype"]], - variable_infos["sizelocation"], - '_'.join(map(str,current_location)), - variable_infos["nodeid"]) - - # Search for an entry that has an empty subindex + indexname = "%s%s%s_%d" % (VariableDirText[variable_infos["pdotype"]], + variable_infos["sizelocation"], + '_'.join(map(str, current_location)), + variable_infos["nodeid"]) + + # Search for an entry that has an empty subindex while mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000: # Entry doesn't exist - if not self.MasterNode.IsEntry(mapvariableidx): + if not self.MasterNode.IsEntry(mapvariableidx): # Add entry to MasterNode self.Manager.AddMapVariableToCurrent(mapvariableidx, "beremiz"+indexname, 3, 1, self.MasterNode) new_index = True @@ -567,30 +607,31 @@ mapvariableidx += 8 * VariableIncrement else: break - + # Verify that a not full entry has been found if mapvariableidx < VariableStartIndex[variable_infos["pdotype"]] + 0x2000: # Generate subentry name - if variable_infos["bit"] != None: - subindexname = "%(index)d_%(subindex)d_%(bit)d"%variable_infos + if variable_infos["bit"] is not None: + subindexname = "%(index)d_%(subindex)d_%(bit)d" % variable_infos else: - subindexname = "%(index)d_%(subindex)d"%variable_infos + subindexname = "%(index)d_%(subindex)d" % variable_infos # If entry have just been created, no subentry have to be added if not new_index: self.Manager.AddSubentriesToCurrent(mapvariableidx, 1, self.MasterNode) nbsubentries += 1 # Add informations to the new subentry created - self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"name" : subindexname}) - self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values = {"type" : typeidx}) - + self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values={"name": subindexname}) + self.MasterNode.SetMappingEntry(mapvariableidx, nbsubentries, values={"type": typeidx}) + # Set value of the PDO mapping typeinfos = self.Manager.GetEntryInfos(typeidx) - if typeinfos != None: + if typeinfos is not None: value = (mapvariableidx << 16) + ((nbsubentries) << 8) + typeinfos["size"] self.MasterNode.SetEntry(current_idx + 0x200, subindex, value) - + # Add variable to pointed variables - self.PointedVariables[(mapvariableidx, nbsubentries)] = "%s_%s"%(indexname, subindexname) + self.PointedVariables[(mapvariableidx, nbsubentries)] = "%s_%s" % (indexname, subindexname) + def GenerateConciseDCF(locations, current_location, nodelist, sync_TPDOs, nodename): """ @@ -605,13 +646,14 @@ @param nodelist: CanFestival network editor model @return: a modified copy of the given CanFestival network editor model """ - + dcfgenerator = ConciseDCFGenerator(nodelist, nodename) dcfgenerator.GenerateDCF(locations, current_location, sync_TPDOs) - masternode,pointers = dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables() + masternode, pointers = dcfgenerator.GetMasterNode(), dcfgenerator.GetPointedVariables() # allow access to local OD from Master PLC pointers.update(LocalODPointers(locations, current_location, masternode)) - return masternode,pointers + return masternode, pointers + def LocalODPointers(locations, current_location, slave): IECLocations = {} @@ -621,39 +663,43 @@ name = location["NAME"] if name in IECLocations: if IECLocations[name] != COlocationtype: - raise PDOmappingException, _("Type conflict for location \"%s\"") % name + raise PDOmappingException(_("Type conflict for location \"%s\"") % name) else: # Get only the part of the location that concern this node loc = location["LOC"][len(current_location):] # loc correspond to (ID, INDEX, SUBINDEX [,BIT]) if len(loc) not in (2, 3, 4): - raise PDOmappingException, _("Bad location size : %s") % str(loc) + raise PDOmappingException(_("Bad location size : %s") % str(loc)) elif len(loc) != 2: continue - + # Extract and check nodeid index, subindex = loc[:2] - + # Extract and check index and subindex if not slave.IsEntry(index, subindex): - raise PDOmappingException, _("No such index/subindex ({a1},{a2}) (variable {a3})").\ - format(a1 = "%x" % index, a2 = "%x" % subindex, a3 = name) - + raise PDOmappingException( + _("No such index/subindex ({a1},{a2}) (variable {a3})"). + format(a1="%x" % index, a2="%x" % subindex, a3=name)) + # Get the entry info - subentry_infos = slave.GetSubentryInfos(index, subindex) + subentry_infos = slave.GetSubentryInfos(index, subindex) if subentry_infos["type"] != COlocationtype: - raise PDOmappingException, _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"").\ - format( a1 = location["IEC_TYPE"], a2 = COlocationtype, a3 = subentry_infos["type"] , a4 = name) - + raise PDOmappingException( + _("Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\""). + format(a1=location["IEC_TYPE"], + a2=COlocationtype, + a3=subentry_infos["type"], + a4=name)) + IECLocations[name] = COlocationtype pointers[(index, subindex)] = name return pointers - + + if __name__ == "__main__": - import os, sys, getopt - def usage(): - print """ + print(""" Usage of config_utils.py test : %s [options] @@ -666,14 +712,14 @@ Reset the reference result of config_utils test. Use with caution. Be sure that config_utils is currently working properly. -"""%sys.argv[0] - +""" % sys.argv[0]) + # Boolean that indicate if reference result must be redefined reset = False # Extract command options try: - opts, args = getopt.getopt(sys.argv[1:], "hr", ["help","reset"]) + opts, args = getopt.getopt(sys.argv[1:], "hr", ["help", "reset"]) except getopt.GetoptError: # print help information and exit: usage() @@ -693,54 +739,54 @@ base_folder = os.path.split(base_folder)[0] # Add CanFestival folder to search pathes sys.path.append(os.path.join(base_folder, "CanFestival-3", "objdictgen")) - + from nodemanager import * from nodelist import * - + # Open the test nodelist contained into test_config folder manager = NodeManager() nodelist = NodeList(manager) result = nodelist.LoadProject("test_config") - + # List of locations, we try to map for test - locations = [{"IEC_TYPE":"BYTE","NAME":"__IB0_1_64_24576_1","DIR":"I","SIZE":"B","LOC":(0,1,64,24576,1)}, - {"IEC_TYPE":"INT","NAME":"__IW0_1_64_25601_2","DIR":"I","SIZE":"W","LOC":(0,1,64,25601,2)}, - {"IEC_TYPE":"INT","NAME":"__IW0_1_64_25601_3","DIR":"I","SIZE":"W","LOC":(0,1,64,25601,3)}, - {"IEC_TYPE":"INT","NAME":"__QW0_1_64_25617_2","DIR":"Q","SIZE":"W","LOC":(0,1,64,25617,1)}, - {"IEC_TYPE":"BYTE","NAME":"__IB0_1_64_24578_1","DIR":"I","SIZE":"B","LOC":(0,1,64,24578,1)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_1","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,1)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_2","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,2)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_3","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,3)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_64_25638_4","DIR":"I","SIZE":"D","LOC":(0,1,64,25638,4)}, - {"IEC_TYPE":"UDINT","NAME":"__ID0_1_4096_0","DIR":"I","SIZE":"D","LOC":(0,1,4096,0)}] - + locations = [ + {"IEC_TYPE": "BYTE", "NAME": "__IB0_1_64_24576_1", "DIR": "I", "SIZE": "B", "LOC": (0, 1, 64, 24576, 1)}, + {"IEC_TYPE": "INT", "NAME": "__IW0_1_64_25601_2", "DIR": "I", "SIZE": "W", "LOC": (0, 1, 64, 25601, 2)}, + {"IEC_TYPE": "INT", "NAME": "__IW0_1_64_25601_3", "DIR": "I", "SIZE": "W", "LOC": (0, 1, 64, 25601, 3)}, + {"IEC_TYPE": "INT", "NAME": "__QW0_1_64_25617_2", "DIR": "Q", "SIZE": "W", "LOC": (0, 1, 64, 25617, 1)}, + {"IEC_TYPE": "BYTE", "NAME": "__IB0_1_64_24578_1", "DIR": "I", "SIZE": "B", "LOC": (0, 1, 64, 24578, 1)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_1", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 1)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_2", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 2)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_3", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 3)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_64_25638_4", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 64, 25638, 4)}, + {"IEC_TYPE": "UDINT", "NAME": "__ID0_1_4096_0", "DIR": "I", "SIZE": "D", "LOC": (0, 1, 4096, 0)} + ] + # Generate MasterNode configuration try: masternode, pointedvariables = GenerateConciseDCF(locations, (0, 1), nodelist, True, "TestNode") except ValueError, message: - print "%s\nTest Failed!"%message + print("%s\nTest Failed!" % message) sys.exit() - + import pprint - # Get Text corresponding to MasterNode + # Get Text corresponding to MasterNode result_node = masternode.PrintString() result_vars = pprint.pformat(pointedvariables) result = result_node + "\n********POINTERS*********\n" + result_vars + "\n" - + # If reset has been choosen if reset: # Write Text into reference result file testfile = open("test_config/result.txt", "w") testfile.write(result) testfile.close() - - print "Reset Successful!" + + print("Reset Successful!") else: - import os - testfile = open("test_config/result_tmp.txt", "w") testfile.write(result) testfile.close() - + os.system("diff test_config/result.txt test_config/result_tmp.txt") os.remove("test_config/result_tmp.txt") diff -r c1298e7ffe3a -r 8391c11477f4 connectors/PYRO/__init__.py --- a/connectors/PYRO/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/connectors/PYRO/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,199 +1,206 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import Pyro -import Pyro.core -import Pyro.util -from Pyro.errors import PyroError -import traceback -from time import sleep -import copy -import socket -service_type = '_PYRO._tcp.local.' -import os.path -# this module attribute contains a list of DNS-SD (Zeroconf) service types -# supported by this connector confnode. -# -# for connectors that do not support DNS-SD, this attribute can be omitted -# or set to an empty list. - -def PYRO_connector_factory(uri, confnodesroot): - """ - This returns the connector to Pyro style PLCobject - """ - confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) - - servicetype, location = uri.split("://") - if servicetype == "PYROS": - schemename = "PYROLOCSSL" - # Protect against name->IP substitution in Pyro3 - Pyro.config.PYRO_DNS_URI = True - # Beware Pyro lib need str path, not unicode - # don't rely on PYRO_STORAGE ! see documentation - Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs') - if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): - confnodesroot.logger.write_error( - 'Error : the directory %s is missing for SSL certificates (certs_dir).' - 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) - return None - else: - confnodesroot.logger.write(_("PYRO using certificates in '%s' \n") - % (Pyro.config.PYROSSL_CERTDIR)) - Pyro.config.PYROSSL_CERT = "client.crt" - Pyro.config.PYROSSL_KEY = "client.key" - # Ugly Monkey Patching - def _gettimeout(self): - return self.timeout - - def _settimeout(self, timeout): - self.timeout = timeout - from M2Crypto.SSL import Connection - Connection.timeout = None - Connection.gettimeout = _gettimeout - Connection.settimeout = _settimeout - # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not - # match host, expected 127.0.0.1, got server - Connection.clientPostConnectionCheck = None - else: - schemename = "PYROLOC" - if location.find(service_type) != -1: - try: - from util.Zeroconf import Zeroconf - r = Zeroconf() - i = r.getServiceInfo(service_type, location) - if i is None: - raise Exception("'%s' not found" % location) - ip = str(socket.inet_ntoa(i.getAddress())) - port = str(i.getPort()) - newlocation = ip + ':' + port - confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1 = location, a2 = newlocation)) - location = newlocation - r.close() - except Exception, msg: - confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location) - confnodesroot.logger.write_error(traceback.format_exc()) - return None - - # Try to get the proxy object - try: - RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") - except Exception, msg: - confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location) - confnodesroot.logger.write_error(traceback.format_exc()) - return None - - def PyroCatcher(func, default=None): - """ - A function that catch a Pyro exceptions, write error to logger - and return default value when it happen - """ - def catcher_func(*args, **kwargs): - try: - return func(*args, **kwargs) - except Pyro.errors.ConnectionClosedError, e: - confnodesroot.logger.write_error(_("Connection lost!\n")) - confnodesroot._SetConnector(None) - except Pyro.errors.ProtocolError, e: - confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) - except Exception, e: - # confnodesroot.logger.write_error(traceback.format_exc()) - errmess = ''.join(Pyro.util.getPyroTraceback(e)) - confnodesroot.logger.write_error(errmess + "\n") - print errmess - confnodesroot._SetConnector(None) - return default - return catcher_func - - # Check connection is effective. - # lambda is for getattr of GetPLCstatus to happen inside catcher - if PyroCatcher(lambda: RemotePLCObjectProxy.GetPLCstatus())() is None: - confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n")) - return None - - class PyroProxyProxy(object): - """ - A proxy proxy class to handle Beremiz Pyro interface specific behavior. - And to put Pyro exception catcher in between caller and Pyro proxy - """ - def __init__(self): - # for safe use in from debug thread, must create a copy - self.RemotePLCObjectProxyCopy = None - - def GetPyroProxy(self): - """ - This func returns the real Pyro Proxy. - Use this if you musn't keep reference to it. - """ - return RemotePLCObjectProxy - - def _PyroStartPLC(self, *args, **kwargs): - """ - confnodesroot._connector.GetPyroProxy() is used - rather than RemotePLCObjectProxy because - object is recreated meanwhile, - so we must not keep ref to it here - """ - current_status, log_count = confnodesroot._connector.GetPyroProxy().GetPLCstatus() - if current_status == "Dirty": - """ - Some bad libs with static symbols may polute PLC - ask runtime to suicide and come back again - """ - confnodesroot.logger.write(_("Force runtime reload\n")) - confnodesroot._connector.GetPyroProxy().ForceReload() - confnodesroot._Disconnect() - # let remote PLC time to resurect.(freeze app) - sleep(0.5) - confnodesroot._Connect() - self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) - return confnodesroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) - StartPLC = PyroCatcher(_PyroStartPLC, False) - - def _PyroGetTraceVariables(self): - """ - for safe use in from debug thread, must use the copy - """ - if self.RemotePLCObjectProxyCopy is None: - self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) - return self.RemotePLCObjectProxyCopy.GetTraceVariables() - GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None)) - - def _PyroGetPLCstatus(self): - return RemotePLCObjectProxy.GetPLCstatus() - GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None)) - - def _PyroRemoteExec(self, script, **kwargs): - return RemotePLCObjectProxy.RemoteExec(script, **kwargs) - RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!")) - - def __getattr__(self, attrName): - member = self.__dict__.get(attrName, None) - if member is None: - def my_local_func(*args, **kwargs): - return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs) - member = PyroCatcher(my_local_func, None) - self.__dict__[attrName] = member - return member - - return PyroProxyProxy() +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +from __future__ import print_function +import traceback +from time import sleep +import copy +import socket +import os.path + +import Pyro +import Pyro.core +import Pyro.util +from Pyro.errors import PyroError + + +service_type = '_PYRO._tcp.local.' +# this module attribute contains a list of DNS-SD (Zeroconf) service types +# supported by this connector confnode. +# +# for connectors that do not support DNS-SD, this attribute can be omitted +# or set to an empty list. + + +def PYRO_connector_factory(uri, confnodesroot): + """ + This returns the connector to Pyro style PLCobject + """ + confnodesroot.logger.write(_("PYRO connecting to URI : %s\n") % uri) + + servicetype, location = uri.split("://") + if servicetype == "PYROS": + schemename = "PYROLOCSSL" + # Protect against name->IP substitution in Pyro3 + Pyro.config.PYRO_DNS_URI = True + # Beware Pyro lib need str path, not unicode + # don't rely on PYRO_STORAGE ! see documentation + Pyro.config.PYROSSL_CERTDIR = os.path.abspath(str(confnodesroot.ProjectPath) + '/certs') + if not os.path.exists(Pyro.config.PYROSSL_CERTDIR): + confnodesroot.logger.write_error( + 'Error : the directory %s is missing for SSL certificates (certs_dir).' + 'Please fix it in your project.\n' % Pyro.config.PYROSSL_CERTDIR) + return None + else: + confnodesroot.logger.write(_("PYRO using certificates in '%s' \n") + % (Pyro.config.PYROSSL_CERTDIR)) + Pyro.config.PYROSSL_CERT = "client.crt" + Pyro.config.PYROSSL_KEY = "client.key" + + # Ugly Monkey Patching + def _gettimeout(self): + return self.timeout + + def _settimeout(self, timeout): + self.timeout = timeout + from M2Crypto.SSL import Connection + Connection.timeout = None + Connection.gettimeout = _gettimeout + Connection.settimeout = _settimeout + # M2Crypto.SSL.Checker.WrongHost: Peer certificate commonName does not + # match host, expected 127.0.0.1, got server + Connection.clientPostConnectionCheck = None + else: + schemename = "PYROLOC" + if location.find(service_type) != -1: + try: + from zeroconf import Zeroconf + r = Zeroconf() + i = r.get_service_info(service_type, location) + if i is None: + raise Exception("'%s' not found" % location) + ip = str(socket.inet_ntoa(i.address)) + port = str(i.port) + newlocation = ip + ':' + port + confnodesroot.logger.write(_("'{a1}' is located at {a2}\n").format(a1=location, a2=newlocation)) + location = newlocation + r.close() + except Exception: + confnodesroot.logger.write_error(_("MDNS resolution failure for '%s'\n") % location) + confnodesroot.logger.write_error(traceback.format_exc()) + return None + + # Try to get the proxy object + try: + RemotePLCObjectProxy = Pyro.core.getAttrProxyForURI(schemename + "://" + location + "/PLCObject") + except Exception: + confnodesroot.logger.write_error(_("Connection to '%s' failed.\n") % location) + confnodesroot.logger.write_error(traceback.format_exc()) + return None + + def PyroCatcher(func, default=None): + """ + A function that catch a Pyro exceptions, write error to logger + and return default value when it happen + """ + def catcher_func(*args, **kwargs): + try: + return func(*args, **kwargs) + except Pyro.errors.ConnectionClosedError, e: + confnodesroot.logger.write_error(_("Connection lost!\n")) + confnodesroot._SetConnector(None) + except Pyro.errors.ProtocolError, e: + confnodesroot.logger.write_error(_("Pyro exception: %s\n") % e) + except Exception, e: + # confnodesroot.logger.write_error(traceback.format_exc()) + errmess = ''.join(Pyro.util.getPyroTraceback(e)) + confnodesroot.logger.write_error(errmess + "\n") + print(errmess) + confnodesroot._SetConnector(None) + return default + return catcher_func + + # Check connection is effective. + # lambda is for getattr of GetPLCstatus to happen inside catcher + if PyroCatcher(RemotePLCObjectProxy.GetPLCstatus)() is None: + confnodesroot.logger.write_error(_("Cannot get PLC status - connection failed.\n")) + return None + + class PyroProxyProxy(object): + """ + A proxy proxy class to handle Beremiz Pyro interface specific behavior. + And to put Pyro exception catcher in between caller and Pyro proxy + """ + def __init__(self): + # for safe use in from debug thread, must create a copy + self.RemotePLCObjectProxyCopy = None + + def GetPyroProxy(self): + """ + This func returns the real Pyro Proxy. + Use this if you musn't keep reference to it. + """ + return RemotePLCObjectProxy + + def _PyroStartPLC(self, *args, **kwargs): + """ + confnodesroot._connector.GetPyroProxy() is used + rather than RemotePLCObjectProxy because + object is recreated meanwhile, + so we must not keep ref to it here + """ + current_status, _log_count = confnodesroot._connector.GetPyroProxy().GetPLCstatus() + if current_status == "Dirty": + # Some bad libs with static symbols may polute PLC + # ask runtime to suicide and come back again + + confnodesroot.logger.write(_("Force runtime reload\n")) + confnodesroot._connector.GetPyroProxy().ForceReload() + confnodesroot._Disconnect() + # let remote PLC time to resurect.(freeze app) + sleep(0.5) + confnodesroot._Connect() + self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) + return confnodesroot._connector.GetPyroProxy().StartPLC(*args, **kwargs) + StartPLC = PyroCatcher(_PyroStartPLC, False) + + def _PyroGetTraceVariables(self): + """ + for safe use in from debug thread, must use the copy + """ + if self.RemotePLCObjectProxyCopy is None: + self.RemotePLCObjectProxyCopy = copy.copy(confnodesroot._connector.GetPyroProxy()) + return self.RemotePLCObjectProxyCopy.GetTraceVariables() + GetTraceVariables = PyroCatcher(_PyroGetTraceVariables, ("Broken", None)) + + def _PyroGetPLCstatus(self): + return RemotePLCObjectProxy.GetPLCstatus() + GetPLCstatus = PyroCatcher(_PyroGetPLCstatus, ("Broken", None)) + + def _PyroRemoteExec(self, script, **kwargs): + return RemotePLCObjectProxy.RemoteExec(script, **kwargs) + RemoteExec = PyroCatcher(_PyroRemoteExec, (-1, "RemoteExec script failed!")) + + def __getattr__(self, attrName): + member = self.__dict__.get(attrName, None) + if member is None: + def my_local_func(*args, **kwargs): + return RemotePLCObjectProxy.__getattr__(attrName)(*args, **kwargs) + member = PyroCatcher(my_local_func, None) + self.__dict__[attrName] = member + return member + + return PyroProxyProxy() diff -r c1298e7ffe3a -r 8391c11477f4 connectors/WAMP/__init__.py --- a/connectors/WAMP/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/connectors/WAMP/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,37 +22,48 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import sys, traceback, atexit -#from twisted.python import log + +from __future__ import absolute_import +from __future__ import print_function +import sys +import traceback +import atexit +from threading import Thread, Event + from twisted.internet import reactor, threads from autobahn.twisted import wamp from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS from autobahn.wamp import types from autobahn.wamp.exception import TransportLost from autobahn.wamp.serializer import MsgPackSerializer -from threading import Thread, Event + _WampSession = None _WampConnection = None _WampSessionEvent = Event() + class WampSession(wamp.ApplicationSession): def onJoin(self, details): - global _WampSession, _WampSessionEvent + global _WampSession _WampSession = self _WampSessionEvent.set() - print 'WAMP session joined for :', self.config.extra["ID"] + print('WAMP session joined for :', self.config.extra["ID"]) def onLeave(self, details): - global _WampSession, _WampSessionEvent + global _WampSession _WampSessionEvent.clear() _WampSession = None - print 'WAMP session left' + print('WAMP session left') -PLCObjDefaults = { "StartPLC": False, - "GetTraceVariables" : ("Broken",None), - "GetPLCstatus" : ("Broken",None), - "RemoteExec" : (-1, "RemoteExec script failed!")} + +PLCObjDefaults = { + "StartPLC": False, + "GetTraceVariables": ("Broken", None), + "GetPLCstatus": ("Broken", None), + "RemoteExec": (-1, "RemoteExec script failed!") +} + def WAMP_connector_factory(uri, confnodesroot): """ @@ -61,37 +72,36 @@ """ servicetype, location = uri.split("://") urlpath, realm, ID = location.split('#') - urlprefix = {"WAMP":"ws", - "WAMPS":"wss"}[servicetype] + urlprefix = {"WAMP": "ws", + "WAMPS": "wss"}[servicetype] url = urlprefix+"://"+urlpath def RegisterWampClient(): - ## start logging to console + # start logging to console # log.startLogging(sys.stdout) # create a WAMP application session factory component_config = types.ComponentConfig( - realm = realm, - extra = {"ID":ID}) + realm=unicode(realm), + extra={"ID": ID}) session_factory = wamp.ApplicationSessionFactory( - config = component_config) + config=component_config) session_factory.session = WampSession # create a WAMP-over-WebSocket transport client factory transport_factory = WampWebSocketClientFactory( session_factory, - url = url, - serializers = [MsgPackSerializer()], - debug = False, - debug_wamp = False) + url=url, + serializers=[MsgPackSerializer()]) # start the client from a Twisted endpoint conn = connectWS(transport_factory) - confnodesroot.logger.write(_("WAMP connecting to URL : %s\n")%url) + confnodesroot.logger.write(_("WAMP connecting to URL : %s\n") % url) return conn AddToDoBeforeQuit = confnodesroot.AppFrame.AddToDoBeforeQuit + def ThreadProc(): global _WampConnection _WampConnection = RegisterWampClient() @@ -99,39 +109,38 @@ reactor.run(installSignalHandlers=False) def WampSessionProcMapper(funcname): - wampfuncname = '.'.join((ID,funcname)) - def catcher_func(*args,**kwargs): - global _WampSession - if _WampSession is not None : + wampfuncname = unicode('.'.join((ID, funcname))) + + def catcher_func(*args, **kwargs): + if _WampSession is not None: try: return threads.blockingCallFromThread( reactor, _WampSession.call, wampfuncname, - *args,**kwargs) - except TransportLost, e: + *args, **kwargs) + except TransportLost: confnodesroot.logger.write_error(_("Connection lost!\n")) confnodesroot._SetConnector(None) - except Exception,e: + except Exception: errmess = traceback.format_exc() confnodesroot.logger.write_error(errmess+"\n") - print errmess - #confnodesroot._SetConnector(None) + print(errmess) + # confnodesroot._SetConnector(None) return PLCObjDefaults.get(funcname) return catcher_func class WampPLCObjectProxy(object): def __init__(self): - global _WampSessionEvent, _WampConnection + global _WampConnection if not reactor.running: Thread(target=ThreadProc).start() else: _WampConnection = threads.blockingCallFromThread( reactor, RegisterWampClient) if not _WampSessionEvent.wait(5): - _WampConnection = stopConnecting() - raise Exception, _("WAMP connection timeout") + _WampConnection.stopConnecting() + raise Exception(_("WAMP connection timeout")) def __del__(self): - global _WampConnection _WampConnection.disconnect() # # reactor.stop() @@ -144,13 +153,9 @@ return member # Try to get the proxy object - try : + try: return WampPLCObjectProxy() - except Exception, msg: - confnodesroot.logger.write_error(_("WAMP connection to '%s' failed.\n")%location) + except Exception: + confnodesroot.logger.write_error(_("WAMP connection to '%s' failed.\n") % location) confnodesroot.logger.write_error(traceback.format_exc()) return None - - - - diff -r c1298e7ffe3a -r 8391c11477f4 connectors/__init__.py --- a/connectors/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/connectors/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,65 +1,70 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# Package initialisation - -from os import listdir, path - - -_base_path = path.split(__file__)[0] - - -def _GetLocalConnectorClassFactory(name): - return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory") - -connectors = {name:_GetLocalConnectorClassFactory(name) - for name in listdir(_base_path) - if path.isdir(path.join(_base_path, name)) - and not name.startswith("__")} - - -def ConnectorFactory(uri, confnodesroot): - """ - Return a connector corresponding to the URI - or None if cannot connect to URI - """ - servicetype = uri.split("://")[0].upper() - if servicetype == "LOCAL": - # Local is special case - # pyro connection to local runtime - # started on demand, listening on random port - servicetype = "PYRO" - runtime_port = confnodesroot.AppFrame.StartLocalRuntime( - taskbaricon=True) - uri = "PYROLOC://127.0.0.1:" + str(runtime_port) - elif servicetype in connectors: - pass - elif servicetype[-1] == 'S' and servicetype[:-1] in connectors: - servicetype = servicetype[:-1] - else: - return None - - # import module according to uri type - connectorclass = connectors[servicetype]() - return connectorclass(uri, confnodesroot) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Package initialisation + + +from __future__ import absolute_import +from os import listdir, path +import util.paths as paths + +_base_path = paths.AbsDir(__file__) + + +def _GetLocalConnectorClassFactory(name): + return lambda: getattr(__import__(name, globals(), locals()), name + "_connector_factory") + + +connectors = {name: + _GetLocalConnectorClassFactory(name) + for name in listdir(_base_path) + if (path.isdir(path.join(_base_path, name)) and + not name.startswith("__"))} + + +def ConnectorFactory(uri, confnodesroot): + """ + Return a connector corresponding to the URI + or None if cannot connect to URI + """ + servicetype = uri.split("://")[0].upper() + if servicetype == "LOCAL": + # Local is special case + # pyro connection to local runtime + # started on demand, listening on random port + servicetype = "PYRO" + runtime_port = confnodesroot.AppFrame.StartLocalRuntime( + taskbaricon=True) + uri = "PYROLOC://127.0.0.1:" + str(runtime_port) + elif servicetype in connectors: + pass + elif servicetype[-1] == 'S' and servicetype[:-1] in connectors: + servicetype = servicetype[:-1] + else: + return None + + # import module according to uri type + connectorclass = connectors[servicetype]() + return connectorclass(uri, confnodesroot) diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomEditableListBox.py --- a/controls/CustomEditableListBox.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/CustomEditableListBox.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,19 +22,22 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.gizmos + class CustomEditableListBox(wx.gizmos.EditableListBox): - + def __init__(self, *args, **kwargs): wx.gizmos.EditableListBox.__init__(self, *args, **kwargs) - + listbox = self.GetListCtrl() listbox.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) listbox.Bind(wx.EVT_LIST_BEGIN_LABEL_EDIT, self.OnLabelBeginEdit) listbox.Bind(wx.EVT_LIST_END_LABEL_EDIT, self.OnLabelEndEdit) - + for button, tooltip, call_function in [ (self.GetEditButton(), _("Edit item"), "_OnEditButton"), (self.GetNewButton(), _("New item"), "_OnNewButton"), @@ -43,13 +46,13 @@ (self.GetDownButton(), _("Move down"), "_OnDownButton")]: button.SetToolTipString(tooltip) button.Bind(wx.EVT_BUTTON, self.GetButtonPressedFunction(call_function)) - + self.Editing = False - + def EnsureCurrentItemVisible(self): listctrl = self.GetListCtrl() listctrl.EnsureVisible(listctrl.GetFocusedItem()) - + def OnLabelBeginEdit(self, event): self.Editing = True func = getattr(self, "_OnLabelBeginEdit", None) @@ -57,7 +60,7 @@ func(event) else: event.Skip() - + def OnLabelEndEdit(self, event): self.Editing = False func = getattr(self, "_OnLabelEndEdit", None) @@ -65,7 +68,7 @@ func(event) else: event.Skip() - + def GetButtonPressedFunction(self, call_function): def OnButtonPressed(event): if wx.Platform != '__WXMSW__' or not self.Editing: @@ -77,7 +80,7 @@ wx.CallAfter(self.EnsureCurrentItemVisible) event.Skip() return OnButtonPressed - + def OnKeyDown(self, event): button = None keycode = event.GetKeyCode() diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomGrid.py --- a/controls/CustomGrid.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/CustomGrid.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,49 +22,52 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.grid + class CustomGrid(wx.grid.Grid): - + def __init__(self, *args, **kwargs): wx.grid.Grid.__init__(self, *args, **kwargs) - + self.Editable = True - + self.AddButton = None self.DeleteButton = None self.UpButton = None self.DownButton = None - + self.SetFont(wx.Font(12, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, 'Sans')) self.SetLabelFont(wx.Font(10, wx.DEFAULT, wx.NORMAL, wx.NORMAL, False, 'Sans')) self.SetSelectionBackground(wx.WHITE) self.SetSelectionForeground(wx.BLACK) self.DisableDragRowSize() - + self.Bind(wx.grid.EVT_GRID_SELECT_CELL, self.OnSelectCell) self.Bind(wx.grid.EVT_GRID_EDITOR_HIDDEN, self.OnEditorHidden) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - + def SetFocus(self): if self: wx.grid.Grid.SetFocus(self) - + def SetDefaultValue(self, default_value): self.DefaultValue = default_value - + def SetEditable(self, editable=True): self.Editable = editable self.RefreshButtons() - + def SetButtons(self, buttons): for name in ["Add", "Delete", "Up", "Down"]: button = buttons.get(name, None) setattr(self, "%sButton" % name, button) if button is not None: button.Bind(wx.EVT_BUTTON, getattr(self, "On%sButton" % name)) - + def RefreshButtons(self): if self: rows = self.Table.GetNumberRows() @@ -77,7 +80,7 @@ self.UpButton.Enable(self.Editable and row > 0) if self.DownButton is not None: self.DownButton.Enable(self.Editable and 0 <= row < rows - 1) - + def CloseEditControl(self): row, col = self.GetGridCursorRow(), self.GetGridCursorCol() if row != -1 and col != -1: @@ -119,27 +122,27 @@ self.Table.ResetView(self) if new_row != row: self.SetSelectedCell(new_row, col) - + def SetSelectedCell(self, row, col): self.SetGridCursor(row, col) self.MakeCellVisible(row, col) self.RefreshButtons() - + def OnAddButton(self, event): self.AddRow() self.SetFocus() event.Skip() - + def OnDeleteButton(self, event): self.DeleteRow() self.SetFocus() event.Skip() - + def OnUpButton(self, event): self.MoveRow(self.GetGridCursorRow(), -1) self.SetFocus() event.Skip() - + def OnDownButton(self, event): self.MoveRow(self.GetGridCursorRow(), 1) self.SetFocus() diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomIntCtrl.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/controls/CustomIntCtrl.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,89 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import wx +import wx.lib.intctrl + + +class CustomIntUpdatedEvent(wx.PyCommandEvent): + def __init__(self, id, value=0, object=None): + wx.PyCommandEvent.__init__(self, CustomIntCtrl.wxEVT_COMMAND_CUSTOM_INT_UPDATED, id) + + self.__value = value + self.SetEventObject(object) + + def GetValue(self): + """Retrieve the value of the control at the time + this event was generated.""" + return self.__value + + +class CustomIntCtrl(wx.lib.intctrl.IntCtrl): + """ + This class provides a control that takes and returns long as + value, and provides bounds support and optional value limiting. + + It handles entering negative numbers more user-friendly than + original wx.lib.intctrl.IntCtrl. + + It applies limits as focus is changed to other control and + sends event afterwards to signal that editing is done. + """ + + # Used to trap events indicating that the current + # integer value of the control has been changed. + wxEVT_COMMAND_CUSTOM_INT_UPDATED = wx.NewEventType() + EVT_CUSTOM_INT = wx.PyEventBinder(wxEVT_COMMAND_CUSTOM_INT_UPDATED, 1) + + def __init__(self, *args, **kwargs): + wx.lib.intctrl.IntCtrl.__init__(self, *args, **kwargs) + self.Bind(wx.EVT_KILL_FOCUS, self.UpdateValue) + self.SetLongAllowed(True) + self.SetLimited(False) + + def GetValue(self): + """ + Returns integer (long) value of the control, + but handles entering negative numbers + """ + s = wx.TextCtrl.GetValue(self) + if s == '-': + s = '' + return self._fromGUI(s) + + def GetValueStr(self): + """Returns string value of TextCtrl""" + return wx.TextCtrl.GetValue(self) + + def UpdateValue(self, event): + self.SetLimited(True) + self.SetLimited(False) + try: + self.GetEventHandler().ProcessEvent( + CustomIntUpdatedEvent(self.GetId(), self.GetValue(), self)) + except ValueError: + return + event.Skip() diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomStyledTextCtrl.py --- a/controls/CustomStyledTextCtrl.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/CustomStyledTextCtrl.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,23 +22,27 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.stc if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 10, - } + faces = { + 'times': 'Times New Roman', + 'mono': 'Courier New', + 'helv': 'Arial', + 'other': 'Comic Sans MS', + 'size': 10, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 12, - } + faces = { + 'times': 'Times', + 'mono': 'Courier', + 'helv': 'Helvetica', + 'other': 'new century schoolbook', + 'size': 12, + } NAVIGATION_KEYS = [ wx.WXK_END, @@ -58,6 +62,7 @@ wx.WXK_NUMPAD_PAGEDOWN, wx.WXK_NUMPAD_END] + def GetCursorPos(old, new): if old == "": return 0 @@ -81,22 +86,22 @@ else: return None + class CustomStyledTextCtrl(wx.stc.StyledTextCtrl): - + def __init__(self, *args, **kwargs): wx.stc.StyledTextCtrl.__init__(self, *args, **kwargs) - + self.Bind(wx.EVT_MOTION, self.OnMotion) - + def OnMotion(self, event): if wx.Platform == '__WXMSW__': if not event.Dragging(): - x, y = event.GetPosition() + x, _y = event.GetPosition() margin_width = reduce( - lambda x, y: x + y, - [self.GetMarginWidth(i) - for i in xrange(3)], - 0) + lambda x, y: x + y, + [self.GetMarginWidth(i) for i in xrange(3)], + 0) if x <= margin_width: self.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) else: diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomTable.py --- a/controls/CustomTable.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/CustomTable.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.grid @@ -30,8 +32,9 @@ else: ROW_HEIGHT = 28 + class CustomTable(wx.grid.PyGridTableBase): - + """ A custom wx.grid.Grid Table using user supplied data """ @@ -47,10 +50,10 @@ # see if the table has changed size self._rows = self.GetNumberRows() self._cols = self.GetNumberCols() - + def GetNumberCols(self): return len(self.colnames) - + def GetNumberRows(self): return len(self.data) @@ -66,11 +69,11 @@ def GetValue(self, row, col): if row < self.GetNumberRows(): return self.data[row].get(self.GetColLabelValue(col, False), "") - + def SetValue(self, row, col, value): if col < len(self.colnames): self.data[row][self.GetColLabelValue(col, False)] = value - + def GetValueByName(self, row, colname): if row < self.GetNumberRows(): return self.data[row].get(colname) @@ -87,14 +90,24 @@ grid.CloseEditControl() grid.BeginBatch() for current, new, delmsg, addmsg in [ - (self._rows, self.GetNumberRows(), wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED), - (self._cols, self.GetNumberCols(), wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED), + ( + self._rows, + self.GetNumberRows(), + wx.grid.GRIDTABLE_NOTIFY_ROWS_DELETED, + wx.grid.GRIDTABLE_NOTIFY_ROWS_APPENDED + ), + ( + self._cols, + self.GetNumberCols(), + wx.grid.GRIDTABLE_NOTIFY_COLS_DELETED, + wx.grid.GRIDTABLE_NOTIFY_COLS_APPENDED + ), ]: if new < current: - msg = wx.grid.GridTableMessage(self,delmsg,new,current-new) + msg = wx.grid.GridTableMessage(self, delmsg, new, current-new) grid.ProcessTableMessage(msg) elif new > current: - msg = wx.grid.GridTableMessage(self,addmsg,new-current) + msg = wx.grid.GridTableMessage(self, addmsg, new-current) grid.ProcessTableMessage(msg) self.UpdateValues(grid) grid.EndBatch() @@ -125,33 +138,33 @@ row_highlights = self.Highlights.get(row, {}) for col in range(self.GetNumberCols()): colname = self.GetColLabelValue(col, False) - + grid.SetReadOnly(row, col, True) grid.SetCellEditor(row, col, None) grid.SetCellRenderer(row, col, None) - + highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] grid.SetCellBackgroundColour(row, col, highlight_colours[0]) grid.SetCellTextColour(row, col, highlight_colours[1]) self.ResizeRow(grid, row) - + def ResizeRow(self, grid, row): if grid.GetRowSize(row) < ROW_HEIGHT: grid.SetRowMinimalHeight(row, ROW_HEIGHT) grid.AutoSizeRow(row, False) - + def SetData(self, data): self.data = data - + def GetData(self): return self.data - + def GetCurrentIndex(self): return self.CurrentIndex - + def SetCurrentIndex(self, index): self.CurrentIndex = index - + def AppendRow(self, row_content): self.data.append(row_content) @@ -191,7 +204,7 @@ if highlight_type is None: self.Highlights = {} else: - for row, row_highlights in self.Highlights.iteritems(): + for _row, row_highlights in self.Highlights.iteritems(): row_items = row_highlights.items() for col, col_highlights in row_items: if highlight_type in col_highlights: diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomToolTip.py --- a/controls/CustomToolTip.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/CustomToolTip.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,49 +22,50 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx from controls.CustomStyledTextCtrl import faces -TOOLTIP_MAX_CHARACTERS = 30 # Maximum number of characters by line in ToolTip -TOOLTIP_MAX_LINE = 5 # Maximum number of line in ToolTip -TOOLTIP_WAIT_PERIOD = 0.5 # Wait period before displaying tooltip in second +TOOLTIP_MAX_CHARACTERS = 30 # Maximum number of characters by line in ToolTip +TOOLTIP_MAX_LINE = 5 # Maximum number of line in ToolTip +TOOLTIP_WAIT_PERIOD = 0.5 # Wait period before displaying tooltip in second -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Custom ToolTip -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a custom tool tip -""" class CustomToolTip(wx.PopupWindow): - + """ + Class that implements a custom tool tip + """ + def __init__(self, parent, tip, restricted=True): """ Constructor @param parent: Parent window @param tip: Tip text (may be multiline) - @param restricted: Tool tip must follow size restriction in line and + @param restricted: Tool tip must follow size restriction in line and characters number defined (default True) """ wx.PopupWindow.__init__(self, parent) - + self.Restricted = restricted - + self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetTip(tip) - + # Initialize text font style self.Font = wx.Font( - faces["size"], - wx.SWISS, - wx.NORMAL, - wx.NORMAL, - faceName = faces["mono"]) - + faces["size"], + wx.SWISS, + wx.NORMAL, + wx.NORMAL, + faceName=faces["mono"]) + self.Bind(wx.EVT_PAINT, self.OnPaint) - + def SetFont(self, font): """ Set tool tip text font style @@ -72,7 +73,7 @@ """ self.Font = font self.RefreshTip() - + def SetTip(self, tip): """ Set tool tip text @@ -87,8 +88,7 @@ new_line = words[0] for word in words[1:]: # Add word to line - if len(new_line + " " + word) <= \ - TOOLTIP_MAX_CHARACTERS: + if len(new_line + " " + word) <= TOOLTIP_MAX_CHARACTERS: new_line += " " + word # Create new line else: @@ -97,24 +97,24 @@ self.Tip.append(new_line) else: self.Tip.append(line) - + # Restrict number of lines if len(self.Tip) > TOOLTIP_MAX_LINE: self.Tip = self.Tip[:TOOLTIP_MAX_LINE] - + # Add ... to the end of last line to indicate that tool tip # text is too long if len(self.Tip[-1]) < TOOLTIP_MAX_CHARACTERS - 3: self.Tip[-1] += "..." else: - self.Tip[-1] = self.Tip[-1]\ - [:TOOLTIP_MAX_CHARACTERS - 3] + "..." + self.Tip[-1] = self.Tip[-1][:TOOLTIP_MAX_CHARACTERS - 3] + \ + "..." else: self.Tip = tip.splitlines() - + # Prevent to call wx method in non-wx threads wx.CallAfter(self.RefreshTip) - + def SetToolTipPosition(self, pos): """ Set tool tip position @@ -122,32 +122,32 @@ """ # Get screen size to prevent tool tip to go out of the screen screen_width, screen_height = wx.GetDisplaySize() - + # Calculate position of tool tip to stay in screen limits tip_width, tip_height = self.GetToolTipSize() self.SetPosition(wx.Point( max(0, min(pos.x, screen_width - tip_width)), max(0, min(pos.y, screen_height - tip_height)))) - + def GetToolTipSize(self): """ Get tool tip size according to tip text and restriction @return: wx.Size(tool_tip_width, tool_tip_height) """ max_width = max_height = 0 - + # Create a memory DC for calculating text extent dc = wx.MemoryDC() dc.SetFont(self.Font) - + # Compute max tip text size for line in self.Tip: w, h = dc.GetTextExtent(line) max_width = max(max_width, w) max_height += h - + return wx.Size(max_width + 4, max_height + 4) - + def RefreshTip(self): """ Refresh tip on screen @@ -156,10 +156,10 @@ if self: # Refresh tool tip size and position self.SetClientSize(self.GetToolTipSize()) - + # Redraw tool tip self.Refresh() - + def OnPaint(self, event): """ Callback for Paint Event @@ -168,26 +168,26 @@ # Get buffered paint DC for tool tip dc = wx.AutoBufferedPaintDC(self) dc.Clear() - + # Set DC drawing style dc.SetPen(wx.BLACK_PEN) dc.SetBrush(wx.Brush(wx.Colour(255, 238, 170))) dc.SetFont(self.Font) - + # Draw Tool tip dc.BeginDrawing() tip_width, tip_height = self.GetToolTipSize() - + # Draw background rectangle dc.DrawRectangle(0, 0, tip_width, tip_height) - + # Draw tool tip text line_offset = 0 for line in self.Tip: dc.DrawText(line, 2, line_offset + 2) - line_width, line_height = dc.GetTextExtent(line) + _line_width, line_height = dc.GetTextExtent(line) line_offset += line_height - + dc.EndDrawing() - + event.Skip() diff -r c1298e7ffe3a -r 8391c11477f4 controls/CustomTree.py --- a/controls/CustomTree.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/CustomTree.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.lib.agw.customtreectrl as CT @@ -30,44 +32,51 @@ # Customize CustomTreeItem for adding icon on item left CT.GenericTreeItem._ExtraImage = None + def SetExtraImage(self, image): self._type = (1 if image is not None else 0) self._ExtraImage = image + CT.GenericTreeItem.SetExtraImage = SetExtraImage _DefaultGetCurrentCheckedImage = CT.GenericTreeItem.GetCurrentCheckedImage + + def GetCurrentCheckedImage(self): if self._ExtraImage is not None: return self._ExtraImage return _DefaultGetCurrentCheckedImage(self) + + CT.GenericTreeItem.GetCurrentCheckedImage = GetCurrentCheckedImage + class CustomTree(CT.CustomTreeCtrl): - + def __init__(self, *args, **kwargs): CT.CustomTreeCtrl.__init__(self, *args, **kwargs) - + self.BackgroundBitmap = None - self.BackgroundAlign = wx.ALIGN_LEFT|wx.ALIGN_TOP - + self.BackgroundAlign = wx.ALIGN_LEFT | wx.ALIGN_TOP + self.AddMenu = None self.Enabled = False - + self.Bind(wx.EVT_SCROLLWIN, self.OnScroll) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) - + def SetBackgroundBitmap(self, bitmap, align): self.BackgroundBitmap = bitmap self.BackgroundAlign = align - + def SetImageListCheck(self, sizex, sizey, imglist=None): CT.CustomTreeCtrl.SetImageListCheck(self, sizex, sizey, imglist=None) - + self.ExtraImages = {} for image in ["function", "functionBlock", "program"]: self.ExtraImages[image] = self._imageListCheck.Add(GetBitmap(image.upper())) - + def SetItemExtraImage(self, item, bitmap): dc = wx.ClientDC(self) image = self.ExtraImages.get(bitmap) @@ -76,45 +85,45 @@ else: item.SetExtraImage(None) self.CalculateSize(item, dc) - self.RefreshLine(item) - + self.RefreshLine(item) + def SetAddMenu(self, add_menu): self.AddMenu = add_menu - + def Enable(self, enabled): self.Enabled = enabled - + def GetBitmapRect(self): client_size = self.GetClientSize() bitmap_size = self.BackgroundBitmap.GetSize() - + if self.BackgroundAlign & wx.ALIGN_RIGHT: x = client_size[0] - bitmap_size[0] elif self.BackgroundAlign & wx.ALIGN_CENTER_HORIZONTAL: x = (client_size[0] - bitmap_size[0]) / 2 else: x = 0 - + if self.BackgroundAlign & wx.ALIGN_BOTTOM: y = client_size[1] - bitmap_size[1] elif self.BackgroundAlign & wx.ALIGN_CENTER_VERTICAL: y = (client_size[1] - bitmap_size[1]) / 2 else: y = 0 - + return wx.Rect(x, y, bitmap_size[0], bitmap_size[1]) - + def OnLeftUp(self, event): if self.Enabled: pos = event.GetPosition() - item, flags = self.HitTest(pos) - + _item, flags = self.HitTest(pos) + bitmap_rect = self.GetBitmapRect() - if (bitmap_rect.InsideXY(pos.x, pos.y) or - flags & wx.TREE_HITTEST_NOWHERE) and self.AddMenu is not None: + if ((bitmap_rect.InsideXY(pos.x, pos.y) or + flags & wx.TREE_HITTEST_NOWHERE) and self.AddMenu is not None): wx.CallAfter(self.PopupMenuXY, self.AddMenu, pos.x, pos.y) event.Skip() - + def OnEraseBackground(self, event): dc = event.GetDC() @@ -122,12 +131,12 @@ dc = wx.ClientDC(self) rect = self.GetUpdateRegion().GetBox() dc.SetClippingRect(rect) - + dc.Clear() - + bitmap_rect = self.GetBitmapRect() dc.DrawBitmap(self.BackgroundBitmap, bitmap_rect.x, bitmap_rect.y) - + def OnScroll(self, event): wx.CallAfter(self.Refresh) event.Skip() diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/DebugVariableGraphicViewer.py --- a/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableGraphicViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,12 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from types import TupleType from time import time as gettime +from distutils.version import LooseVersion + import numpy - import wx - import matplotlib import matplotlib.pyplot from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas @@ -36,13 +38,10 @@ from mpl_toolkits.mplot3d import Axes3D from editors.DebugViewer import REFRESH_PERIOD - -from DebugVariableItem import DebugVariableItem -from DebugVariableViewer import * -from GraphButton import GraphButton - - -from distutils.version import LooseVersion +from controls.DebugVariablePanel.DebugVariableViewer import * +from controls.DebugVariablePanel.GraphButton import GraphButton + + if LooseVersion(matplotlib.__version__) >= LooseVersion("1.5.0"): from cycler import cycler @@ -53,19 +52,20 @@ # Canvas height [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI] = [0, 100, 200] -CANVAS_BORDER = (20., 10.) # Border height on at bottom and top of graph -CANVAS_PADDING = 8.5 # Border inside graph where no label is drawn -VALUE_LABEL_HEIGHT = 17. # Height of variable label in graph -AXES_LABEL_HEIGHT = 12.75 # Height of variable value in graph +CANVAS_BORDER = (20., 10.) # Border height on at bottom and top of graph +CANVAS_PADDING = 8.5 # Border inside graph where no label is drawn +VALUE_LABEL_HEIGHT = 17. # Height of variable label in graph +AXES_LABEL_HEIGHT = 12.75 # Height of variable value in graph # Colors used cyclically for graph curves COLOR_CYCLE = ['r', 'b', 'g', 'm', 'y', 'k'] # Color for graph cursor CURSOR_COLOR = '#800080' -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Viewer Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def merge_ranges(ranges): """ @@ -83,37 +83,37 @@ min_value = range_min elif range_min is not None: min_value = min(min_value, range_min) - + # Update maximal range value if max_value is None: max_value = range_max elif range_min is not None: max_value = max(max_value, range_max) - + # Calculate range center and width if at least one valid range is defined if min_value is not None and max_value is not None: center = (min_value + max_value) / 2. range_size = max(1.0, max_value - min_value) - + # Set default center and with if no valid range is defined else: center = 0.5 range_size = 1.0 - + # Return range expended from 10 % return center - range_size * 0.55, center + range_size * 0.55 -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Viewer Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Graphic -Viewer -""" +# ------------------------------------------------------------------------------- + class DebugVariableGraphicDropTarget(wx.TextDropTarget): - + """ + Class that implements a custom drop target class for Debug Variable Graphic + Viewer + """ + def __init__(self, parent, window): """ Constructor @@ -123,7 +123,7 @@ wx.TextDropTarget.__init__(self) self.ParentControl = parent self.ParentWindow = window - + def __del__(self): """ Destructor @@ -132,7 +132,7 @@ # Panel self.ParentControl = None self.ParentWindow = None - + def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -142,9 +142,9 @@ """ # Signal parent that mouse is dragged over self.ParentControl.OnMouseDragging(x, y) - + return wx.TextDropTarget.OnDragOver(self, x, y, d) - + def OnDropText(self, x, y, data): """ Function called when mouse is released in Drop Target @@ -154,65 +154,65 @@ """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + message = None - + # Check that data is valid regarding DebugVariablePanel try: values = eval(data) if not isinstance(values, TupleType): raise ValueError - except: - message = _("Invalid value \"%s\" for debug variable")%data + except Exception: + message = _("Invalid value \"%s\" for debug variable") % data values = None - + # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) - + # Data contain a reference to a variable to debug elif values[1] == "debug": target_idx = self.ParentControl.GetIndex() - + # If mouse is dropped in graph canvas bounding box and graph is # not 3D canvas, graphs will be merged rect = self.ParentControl.GetAxesBoundingBox() if not self.ParentControl.Is3DCanvas() and rect.InsideXY(x, y): # Default merge type is parallel merge_type = GRAPH_PARALLEL - + # If mouse is dropped in left part of graph canvas, graph # wall be merged orthogonally - merge_rect = wx.Rect(rect.x, rect.y, + merge_rect = wx.Rect(rect.x, rect.y, rect.width / 2., rect.height) if merge_rect.InsideXY(x, y): merge_type = GRAPH_ORTHOGONAL - + # Merge graphs - wx.CallAfter(self.ParentWindow.MergeGraphs, - values[0], target_idx, + wx.CallAfter(self.ParentWindow.MergeGraphs, + values[0], target_idx, merge_type, force=True) - + else: - width, height = self.ParentControl.GetSize() - + _width, height = self.ParentControl.GetSize() + # Get Before which Viewer the variable has to be moved or added # according to the position of mouse in Viewer. if y > height / 2: target_idx += 1 - + # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel + # Variable Panel if len(values) > 2 and values[2] == "move": - self.ParentWindow.MoveValue(values[0], + self.ParentWindow.MoveValue(values[0], target_idx) - + # Drag'n Drop was initiated by another control of Beremiz else: - self.ParentWindow.InsertValue(values[0], - target_idx, + self.ParentWindow.InsertValue(values[0], + target_idx, force=True) - + def OnLeave(self): """ Function called when mouse is leave Drop Target @@ -220,30 +220,30 @@ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() return wx.TextDropTarget.OnLeave(self) - + def ShowMessage(self, message): """ Show error message in Error Dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self.ParentWindow, + message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a graphs -""" +# ------------------------------------------------------------------------------- + class DebugVariableGraphicViewer(DebugVariableViewer, FigureCanvas): - + """ + Class that implements a Viewer that display variable values as a graphs + """ + def __init__(self, parent, window, items, graph_type): """ Constructor @@ -253,36 +253,36 @@ @param graph_type: Graph display type (Parallel or orthogonal) """ DebugVariableViewer.__init__(self, window, items) - + self.GraphType = graph_type # Graph type display self.CursorTick = None # Tick of the graph cursor - + # Mouse position when start dragging self.MouseStartPos = None # Tick when moving tick start self.StartCursorTick = None # Canvas size when starting to resize canvas - self.CanvasStartSize = None - + self.CanvasStartSize = None + # List of current displayed contextual buttons self.ContextualButtons = [] # Reference to item for which contextual buttons was displayed self.ContextualButtonsItem = None - + # Flag indicating that zoom fit current displayed data range or whole # data range if False self.ZoomFit = False - + # Create figure for drawing graphs self.Figure = matplotlib.figure.Figure(facecolor='w') # Defined border around figure in canvas - self.Figure.subplotpars.update(top=0.95, left=0.1, + self.Figure.subplotpars.update(top=0.95, left=0.1, bottom=0.1, right=0.95) - + FigureCanvas.__init__(self, parent, -1, self.Figure) self.SetWindowStyle(wx.WANTS_CHARS) self.SetBackgroundColour(wx.WHITE) - + # Bind wx events self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) @@ -290,42 +290,41 @@ self.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeave) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_SIZE, self.OnResize) - + # Set canvas min size canvas_size = self.GetCanvasMinSize() self.SetMinSize(canvas_size) - + # Define Viewer drop target self.SetDropTarget(DebugVariableGraphicDropTarget(self, window)) - + # Connect matplotlib events self.mpl_connect('button_press_event', self.OnCanvasButtonPressed) self.mpl_connect('motion_notify_event', self.OnCanvasMotion) self.mpl_connect('button_release_event', self.OnCanvasButtonReleased) self.mpl_connect('scroll_event', self.OnCanvasScroll) - + # Add buttons for zooming on current displayed data range self.Buttons.append( - GraphButton(0, 0, "fit_graph", self.OnZoomFitButton)) - + GraphButton(0, 0, "fit_graph", self.OnZoomFitButton)) + # Add buttons for changing canvas size with predefined height for size, bitmap in zip( [SIZE_MINI, SIZE_MIDDLE, SIZE_MAXI], ["minimize_graph", "middle_graph", "maximize_graph"]): - self.Buttons.append( - GraphButton(0, 0, bitmap, - self.GetOnChangeSizeButton(size))) - + self.Buttons.append(GraphButton(0, 0, bitmap, + self.GetOnChangeSizeButton(size))) + # Add buttons for exporting graph values to clipboard and close graph for bitmap, callback in [ ("export_graph_mini", self.OnExportGraphButton), ("delete_graph", self.OnCloseButton)]: self.Buttons.append(GraphButton(0, 0, bitmap, callback)) - + # Update graphs elements self.ResetGraphics() self.RefreshLabelsPosition(canvas_size.height) - + def AddItem(self, item): """ Add an item to the list of items displayed by Viewer @@ -333,14 +332,14 @@ """ DebugVariableViewer.AddItem(self, item) self.ResetGraphics() - + def RemoveItem(self, item): """ Remove an item from the list of items displayed by Viewer @param item: Item to remove from the list """ DebugVariableViewer.RemoveItem(self, item) - + # If list of items is not empty if not self.ItemsIsEmpty(): # Return to parallel graph if there is only one item @@ -348,51 +347,51 @@ if len(self.Items) == 1: self.GraphType = GRAPH_PARALLEL self.ResetGraphics() - + def SetCursorTick(self, cursor_tick): """ Set cursor tick @param cursor_tick: Cursor tick """ self.CursorTick = cursor_tick - + def SetZoomFit(self, zoom_fit): """ Set flag indicating that zoom fit current displayed data range @param zoom_fit: Flag for zoom fit (False: zoom fit whole data range) """ - # Flag is different from the actual one + # Flag is different from the actual one if zoom_fit != self.ZoomFit: # Save new flag value self.ZoomFit = zoom_fit - + # Update button for zoom fit bitmap self.Buttons[0].SetBitmap("full_graph" if zoom_fit else "fit_graph") - + # Refresh canvas self.RefreshViewer() - + def SubscribeAllDataConsumers(self): """ Function that unsubscribe and remove every item that store values of a variable that doesn't exist in PLC anymore """ DebugVariableViewer.SubscribeAllDataConsumers(self) - + # Graph still have data to display if not self.ItemsIsEmpty(): # Reset flag indicating that zoom fit current displayed data range self.SetZoomFit(False) - + self.ResetGraphics() - + def Is3DCanvas(self): """ Return if Viewer is a 3D canvas @return: True if Viewer is a 3D canvas """ return self.GraphType == GRAPH_ORTHOGONAL and len(self.Items) == 3 - + def GetButtons(self): """ Return list of buttons defined in Viewer @@ -400,7 +399,7 @@ """ # Add contextual buttons to default buttons return self.Buttons + self.ContextualButtons - + def PopupContextualButtons(self, item, rect, direction=wx.RIGHT): """ Show contextual menu for item aside a label of this item defined @@ -412,33 +411,32 @@ # Return immediately if contextual menu for item is already shown if self.ContextualButtonsItem == item: return - + # Close already shown contextual menu self.DismissContextualButtons() - + # Save item for which contextual menu is shown self.ContextualButtonsItem = item - + # If item variable is forced, add button for release variable to # contextual menu if self.ContextualButtonsItem.IsForced(): self.ContextualButtons.append( GraphButton(0, 0, "release", self.OnReleaseItemButton)) - + # Add other buttons to contextual menu for bitmap, callback in [ ("force", self.OnForceItemButton), ("export_graph_mini", self.OnExportItemGraphButton), ("delete_graph", self.OnRemoveItemButton)]: - self.ContextualButtons.append( - GraphButton(0, 0, bitmap, callback)) - + self.ContextualButtons.append(GraphButton(0, 0, bitmap, callback)) + # If buttons are shown at left side or upper side of rect, positions # will be set in reverse order buttons = self.ContextualButtons[:] if direction in [wx.TOP, wx.LEFT]: - buttons.reverse() - + buttons.reverse() + # Set contextual menu buttons position aside rect depending on # direction given offset = 0 @@ -446,22 +444,22 @@ w, h = button.GetSize() if direction in [wx.LEFT, wx.RIGHT]: x = rect.x + (- w - offset - if direction == wx.LEFT - else rect.width + offset) + if direction == wx.LEFT + else rect.width + offset) y = rect.y + (rect.height - h) / 2 offset += w else: - x = rect.x + (rect.width - w ) / 2 + x = rect.x + (rect.width - w) / 2 y = rect.y + (- h - offset if direction == wx.TOP else rect.height + offset) offset += h button.SetPosition(x, y) button.Show() - + # Refresh canvas self.ParentWindow.ForceRefresh() - + def DismissContextualButtons(self): """ Close current shown contextual menu @@ -469,14 +467,14 @@ # Return immediately if no contextual menu is shown if self.ContextualButtonsItem is None: return - + # Reset variables corresponding to contextual menu self.ContextualButtonsItem = None self.ContextualButtons = [] - + # Refresh canvas self.ParentWindow.ForceRefresh() - + def IsOverContextualButton(self, x, y): """ Return if point is over one contextual button of Viewer @@ -488,7 +486,7 @@ if button.HitTest(x, y): return button return None - + def ExportGraph(self, item=None): """ Export item(s) data to clipboard in CSV format @@ -497,17 +495,17 @@ """ self.ParentWindow.CopyDataToClipboard( [(item, [entry for entry in item.GetData()]) - for item in (self.Items - if item is None + for item in (self.Items + if item is None else [item])]) - + def OnZoomFitButton(self): """ Function called when Viewer Zoom Fit button is pressed """ # Toggle zoom fit flag value self.SetZoomFit(not self.ZoomFit) - + def GetOnChangeSizeButton(self, height): """ Function that generate callback function for change Viewer height to @@ -518,32 +516,32 @@ def OnChangeSizeButton(): self.SetCanvasHeight(height) return OnChangeSizeButton - + def OnExportGraphButton(self): """ Function called when Viewer Export button is pressed """ # Export data of every item in Viewer self.ExportGraph() - + def OnForceItemButton(self): """ Function called when contextual menu Force button is pressed """ - # Open dialog for forcing item variable value + # Open dialog for forcing item variable value self.ForceValue(self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - + def OnReleaseItemButton(self): """ Function called when contextual menu Release button is pressed """ - # Release item variable value + # Release item variable value self.ReleaseValue(self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - + def OnExportItemGraphButton(self): """ Function called when contextual menu Export button is pressed @@ -552,17 +550,17 @@ self.ExportGraph(self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - - def OnRemoveItemButton(self): + + def OnRemoveItemButton(self): """ Function called when contextual menu Remove button is pressed """ # Remove item from Viewer - wx.CallAfter(self.ParentWindow.DeleteValue, self, + wx.CallAfter(self.ParentWindow.DeleteValue, self, self.ContextualButtonsItem) # Close contextual menu self.DismissContextualButtons() - + def HandleCursorMove(self, event): """ Update Cursor position according to mouse position and graph type @@ -571,7 +569,7 @@ start_tick, end_tick = self.ParentWindow.GetRange() cursor_tick = None items = self.ItemsDict.values() - + # Graph is orthogonal if self.GraphType == GRAPH_ORTHOGONAL: # Extract items data displayed in canvas figure @@ -579,31 +577,31 @@ end_tick = max(end_tick, start_tick) x_data = items[0].GetData(start_tick, end_tick) y_data = items[1].GetData(start_tick, end_tick) - + # Search for the nearest point from mouse position if len(x_data) > 0 and len(y_data) > 0: - length = min(len(x_data), len(y_data)) - d = numpy.sqrt((x_data[:length,1]-event.xdata) ** 2 + \ - (y_data[:length,1]-event.ydata) ** 2) - + length = min(len(x_data), len(y_data)) + d = numpy.sqrt((x_data[:length, 1]-event.xdata) ** 2 + + (y_data[:length, 1]-event.ydata) ** 2) + # Set cursor tick to the tick of this point cursor_tick = x_data[numpy.argmin(d), 0] - + # Graph is parallel else: # Extract items tick data = items[0].GetData(start_tick, end_tick) - + # Search for point that tick is the nearest from mouse X position # and set cursor tick to the tick of this point if len(data) > 0: cursor_tick = data[numpy.argmin( - numpy.abs(data[:,0] - event.xdata)), 0] - + numpy.abs(data[:, 0] - event.xdata)), 0] + # Update cursor tick if cursor_tick is not None: self.ParentWindow.SetCursorTick(cursor_tick) - + def OnCanvasButtonPressed(self, event): """ Function called when a button of mouse is pressed @@ -611,20 +609,20 @@ """ # Get mouse position, graph Y coordinate is inverted in matplotlib # comparing to wx - width, height = self.GetSize() + _width, height = self.GetSize() x, y = event.x, height - event.y - + # Return immediately if mouse is over a button if self.IsOverButton(x, y): - return - + return + # Mouse was clicked inside graph figure if event.inaxes == self.Axes: - + # Find if it was on an item label item_idx = None # Check every label paired with corresponding item - for i, t in ([pair for pair in enumerate(self.AxesLabels)] + + for i, t in ([pair for pair in enumerate(self.AxesLabels)] + [pair for pair in enumerate(self.Labels)]): # Get label bounding box (x0, y0), (x1, y1) = t.get_window_extent().get_points() @@ -633,46 +631,46 @@ if rect.InsideXY(x, y): item_idx = i break - + # If an item label have been clicked if item_idx is not None: # Hide buttons and contextual buttons self.ShowButtons(False) self.DismissContextualButtons() - + # Start a drag'n drop from mouse position in wx coordinate of # parent xw, yw = self.GetPosition() - self.ParentWindow.StartDragNDrop(self, - self.ItemsDict.values()[item_idx], - x + xw, y + yw, # Current mouse position - x + xw, y + yw) # Mouse position when button was clicked - + self.ParentWindow.StartDragNDrop( + self, self.ItemsDict.values()[item_idx], + x + xw, y + yw, # Current mouse position + x + xw, y + yw) # Mouse position when button was clicked + # Don't handle mouse button if canvas is 3D and let matplotlib do # the default behavior (rotate 3D axes) elif not self.Is3DCanvas(): # Save mouse position when clicked self.MouseStartPos = wx.Point(x, y) - + # Mouse button was left button, start moving cursor if event.button == 1: # Save current tick in case a drag'n drop is initiate to # restore it self.StartCursorTick = self.CursorTick - + self.HandleCursorMove(event) - + # Mouse button is middle button and graph is parallel, start # moving graph along X coordinate (tick) elif event.button == 2 and self.GraphType == GRAPH_PARALLEL: self.StartCursorTick = self.ParentWindow.GetRange()[0] - + # Mouse was clicked outside graph figure and over resize highlight with # left button, start resizing Viewer elif event.button == 1 and event.y <= 5: self.MouseStartPos = wx.Point(x, y) self.CanvasStartSize = height - + def OnCanvasButtonReleased(self, event): """ Function called when a button of mouse is released @@ -680,62 +678,62 @@ """ # If a drag'n drop is in progress, stop it if self.ParentWindow.IsDragging(): - width, height = self.GetSize() + _width, height = self.GetSize() xw, yw = self.GetPosition() item = self.ParentWindow.DraggingAxesPanel.ItemsDict.values()[0] # Give mouse position in wx coordinate of parent self.ParentWindow.StopDragNDrop(item.GetVariable(), - xw + event.x, yw + height - event.y) - + xw + event.x, yw + height - event.y) + else: # Reset any move in progress self.MouseStartPos = None self.CanvasStartSize = None - + # Handle button under mouse if it exist - width, height = self.GetSize() + _width, height = self.GetSize() self.HandleButton(event.x, height - event.y) - + def OnCanvasMotion(self, event): """ Function called when a button of mouse is moved over Viewer @param event: Mouse event """ - width, height = self.GetSize() - + _width, height = self.GetSize() + # If a drag'n drop is in progress, move canvas dragged if self.ParentWindow.IsDragging(): xw, yw = self.GetPosition() # Give mouse position in wx coordinate of parent self.ParentWindow.MoveDragNDrop( - xw + event.x, + xw + event.x, yw + height - event.y) - - # If a Viewer resize is in progress, change Viewer size + + # If a Viewer resize is in progress, change Viewer size elif event.button == 1 and self.CanvasStartSize is not None: - width, height = self.GetSize() + _width, height = self.GetSize() self.SetCanvasHeight( self.CanvasStartSize + height - event.y - self.MouseStartPos.y) - + # If no button is pressed, show or hide contextual buttons or resize # highlight elif event.button is None: # Compute direction for items label according graph type - if self.GraphType == GRAPH_PARALLEL: # Graph is parallel + if self.GraphType == GRAPH_PARALLEL: # Graph is parallel directions = [wx.RIGHT] * len(self.AxesLabels) + \ [wx.LEFT] * len(self.Labels) - elif len(self.AxesLabels) > 0: # Graph is orthogonal in 2D - directions = [wx.RIGHT, wx.TOP, # Directions for AxesLabels - wx.LEFT, wx.BOTTOM] # Directions for Labels - else: # Graph is orthogonal in 3D + elif len(self.AxesLabels) > 0: # Graph is orthogonal in 2D + directions = [wx.RIGHT, wx.TOP, # Directions for AxesLabels + wx.LEFT, wx.BOTTOM] # Directions for Labels + else: # Graph is orthogonal in 3D directions = [wx.LEFT] * len(self.Labels) - + # Find if mouse is over an item label item_idx = None menu_direction = None for (i, t), dir in zip( - [pair for pair in enumerate(self.AxesLabels)] + - [pair for pair in enumerate(self.Labels)], + [pair for pair in enumerate(self.AxesLabels)] + + [pair for pair in enumerate(self.Labels)], directions): # Check every label paired with corresponding item (x0, y0), (x1, y1) = t.get_window_extent().get_points() @@ -745,19 +743,19 @@ item_idx = i menu_direction = dir break - - # If mouse is over an item label, + + # If mouse is over an item label, if item_idx is not None: self.PopupContextualButtons( - self.ItemsDict.values()[item_idx], + self.ItemsDict.values()[item_idx], rect, menu_direction) return - + # If mouse isn't over a contextual menu, hide the current shown one - # if it exists + # if it exists if self.IsOverContextualButton(event.x, height - event.y) is None: self.DismissContextualButtons() - + # Update resize highlight if event.y <= 5: if self.SetHighlight(HIGHLIGHT_RESIZE): @@ -767,46 +765,47 @@ if self.SetHighlight(HIGHLIGHT_NONE): self.SetCursor(wx.NullCursor) self.ParentWindow.ForceRefresh() - - # Handle buttons if canvas is not 3D + + # Handle buttons if canvas is not 3D elif not self.Is3DCanvas(): - + # If left button is pressed if event.button == 1: - + # Mouse is inside graph figure if event.inaxes == self.Axes: - + # If a cursor move is in progress, update cursor position if self.MouseStartPos is not None: self.HandleCursorMove(event) - + # Mouse is outside graph figure, cursor move is in progress and # there is only one item in Viewer, start a drag'n drop elif self.MouseStartPos is not None and len(self.Items) == 1: xw, yw = self.GetPosition() self.ParentWindow.SetCursorTick(self.StartCursorTick) - self.ParentWindow.StartDragNDrop(self, - self.ItemsDict.values()[0], + self.ParentWindow.StartDragNDrop( + self, self.ItemsDict.values()[0], # Current mouse position event.x + xw, height - event.y + yw, # Mouse position when button was clicked self.MouseStartPos.x + xw, self.MouseStartPos.y + yw) - + # If middle button is pressed and moving graph along X coordinate # is in progress - elif event.button == 2 and self.GraphType == GRAPH_PARALLEL and \ - self.MouseStartPos is not None: + elif (event.button == 2 and + self.GraphType == GRAPH_PARALLEL and + self.MouseStartPos is not None): start_tick, end_tick = self.ParentWindow.GetRange() rect = self.GetAxesBoundingBox() - + # Move graph along X coordinate self.ParentWindow.SetCanvasPosition( - self.StartCursorTick + + self.StartCursorTick + (self.MouseStartPos.x - event.x) * (end_tick - start_tick) / rect.width) - + def OnCanvasScroll(self, event): """ Function called when a wheel mouse is use in Viewer @@ -815,7 +814,7 @@ # Change X range of graphs if mouse is in canvas figure and ctrl is # pressed if event.inaxes is not None and event.guiEvent.ControlDown(): - + # Calculate position of fixed tick point according to graph type # and mouse position if self.GraphType == GRAPH_ORTHOGONAL: @@ -824,10 +823,10 @@ else: tick = event.xdata self.ParentWindow.ChangeRange(int(-event.step) / 3, tick) - + # Vetoing event to prevent parent panel to be scrolled self.ParentWindow.VetoScrollEvent = True - + def OnLeftDClick(self, event): """ Function called when a left mouse button is double clicked @@ -841,17 +840,17 @@ self.ParentWindow.SetCursorTick(self.StartCursorTick) # Toggle to text Viewer(s) self.ParentWindow.ToggleViewerType(self) - + else: event.Skip() - + # Cursor tick move for each arrow key KEY_CURSOR_INCREMENT = { wx.WXK_LEFT: -1, wx.WXK_RIGHT: 1, wx.WXK_UP: 10, wx.WXK_DOWN: -10} - + def OnKeyDown(self, event): """ Function called when key is pressed @@ -863,7 +862,7 @@ if move is not None: self.ParentWindow.MoveCursorTick(move) event.Skip() - + def OnLeave(self, event): """ Function called when mouse leave Viewer @@ -876,7 +875,7 @@ DebugVariableViewer.OnLeave(self, event) else: event.Skip() - + def GetCanvasMinSize(self): """ Return the minimum size of Viewer so that all items label can be @@ -885,10 +884,10 @@ """ # The minimum height take in account the height of all items, padding # inside figure and border around figure - return wx.Size(200, - CANVAS_BORDER[0] + CANVAS_BORDER[1] + - 2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items)) - + return wx.Size(200, + CANVAS_BORDER[0] + CANVAS_BORDER[1] + + 2 * CANVAS_PADDING + VALUE_LABEL_HEIGHT * len(self.Items)) + def SetCanvasHeight(self, height): """ Set Viewer size checking that it respects Viewer minimum size @@ -899,7 +898,7 @@ self.SetMinSize(wx.Size(min_width, height)) self.RefreshLabelsPosition(height) self.ParentWindow.RefreshGraphicsSizer() - + def GetAxesBoundingBox(self, parent_coordinate=False): """ Return figure bounding box in wx coordinate @@ -911,23 +910,23 @@ ax, ay, aw, ah = self.figure.gca().get_position().bounds bbox = wx.Rect(ax * width, height - (ay + ah) * height - 1, aw * width + 2, ah * height + 1) - + # If parent_coordinate, add Viewer position in parent if parent_coordinate: xw, yw = self.GetPosition() bbox.x += xw bbox.y += yw - + return bbox - + def RefreshHighlight(self, x, y): """ Refresh Viewer highlight according to mouse position @param x: X coordinate of mouse pointer @param y: Y coordinate of mouse pointer """ - width, height = self.GetSize() - + _width, height = self.GetSize() + # Mouse is over Viewer figure and graph is not 3D bbox = self.GetAxesBoundingBox() if bbox.InsideXY(x, y) and not self.Is3DCanvas(): @@ -935,28 +934,28 @@ # Mouse is over Viewer left part of figure if rect.InsideXY(x, y): self.SetHighlight(HIGHLIGHT_LEFT) - + # Mouse is over Viewer right part of figure else: self.SetHighlight(HIGHLIGHT_RIGHT) - + # Mouse is over upper part of Viewer elif y < height / 2: # Viewer is upper one in Debug Variable Panel, show highlight if self.ParentWindow.IsViewerFirst(self): self.SetHighlight(HIGHLIGHT_BEFORE) - + # Viewer is not the upper one, show highlight in previous one # It prevents highlight to move when mouse leave one Viewer to # another else: self.SetHighlight(HIGHLIGHT_NONE) self.ParentWindow.HighlightPreviousViewer(self) - + # Mouse is over lower part of Viewer else: self.SetHighlight(HIGHLIGHT_AFTER) - + def OnAxesMotion(self, event): """ Function overriding default function called when mouse is dragged for @@ -969,13 +968,14 @@ if current_time - self.LastMotionTime > REFRESH_PERIOD: self.LastMotionTime = current_time Axes3D._on_move(self.Axes, event) - + def GetAddTextFunction(self): """ Return function for adding text in figure according to graph type @return: Function adding text to figure """ text_func = (self.Axes.text2D if self.Is3DCanvas() else self.Axes.text) + def AddText(*args, **kwargs): args = [0, 0, ""] kwargs["transform"] = self.Axes.transAxes @@ -984,61 +984,61 @@ def SetAxesColor(self, color): if LooseVersion(matplotlib.__version__) >= LooseVersion("1.5.0"): - self.Axes.set_prop_cycle(cycler('color',color)) + self.Axes.set_prop_cycle(cycler('color', color)) else: self.Axes.set_color_cycle(color) - + def ResetGraphics(self): """ Reset figure and graphical elements displayed in it - Called any time list of items or graph type change + Called any time list of items or graph type change """ # Clear figure from any axes defined self.Figure.clear() - + # Add 3D projection if graph is in 3D if self.Is3DCanvas(): self.Axes = self.Figure.gca(projection='3d') self.SetAxesColor(['b']) - - # Override function to prevent too much refresh when graph is + + # Override function to prevent too much refresh when graph is # rotated self.LastMotionTime = gettime() setattr(self.Axes, "_on_move", self.OnAxesMotion) - + # Init graph mouse event so that graph can be rotated self.Axes.mouse_init() - + # Set size of Z axis labels self.Axes.tick_params(axis='z', labelsize='small') - + else: self.Axes = self.Figure.gca() self.SetAxesColor(COLOR_CYCLE) - + # Set size of X and Y axis labels self.Axes.tick_params(axis='x', labelsize='small') self.Axes.tick_params(axis='y', labelsize='small') - + # Init variables storing graphical elements added to figure - self.Plots = [] # List of curves - self.VLine = None # Vertical line for cursor - self.HLine = None # Horizontal line for cursor (only orthogonal 2D) - self.AxesLabels = [] # List of items variable path text label - self.Labels = [] # List of items text label - - # Get function to add a text in figure according to graph type + self.Plots = [] # List of curves + self.VLine = None # Vertical line for cursor + self.HLine = None # Horizontal line for cursor (only orthogonal 2D) + self.AxesLabels = [] # List of items variable path text label + self.Labels = [] # List of items text label + + # Get function to add a text in figure according to graph type add_text_func = self.GetAddTextFunction() - + # Graph type is parallel or orthogonal in 3D if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas(): num_item = len(self.Items) for idx in xrange(num_item): - + # Get color from color cycle (black if only one item) - color = ('k' if num_item == 1 - else COLOR_CYCLE[idx % len(COLOR_CYCLE)]) - + color = ('k' if num_item == 1 else + COLOR_CYCLE[idx % len(COLOR_CYCLE)]) + # In 3D graph items variable label are not displayed as text # in figure, but as axis title if not self.Is3DCanvas(): @@ -1046,12 +1046,12 @@ self.AxesLabels.append( add_text_func(size='small', color=color, verticalalignment='top')) - + # Items variable labels are in figure lower right corner self.Labels.append( - add_text_func(size='large', color=color, + add_text_func(size='large', color=color, horizontalalignment='right')) - + # Graph type is orthogonal in 2D else: # X coordinate labels are in figure lower side @@ -1059,7 +1059,7 @@ self.Labels.append( add_text_func(size='large', horizontalalignment='right')) - + # Y coordinate labels are vertical and in figure left side self.AxesLabels.append( add_text_func(size='small', rotation='vertical', @@ -1067,11 +1067,11 @@ self.Labels.append( add_text_func(size='large', rotation='vertical', verticalalignment='top')) - + # Refresh position of labels according to Viewer size - width, height = self.GetSize() + _width, height = self.GetSize() self.RefreshLabelsPosition(height) - + def RefreshLabelsPosition(self, height): """ Function called when mouse leave Viewer @@ -1083,51 +1083,51 @@ # expressed border and text position in pixel on screen and apply the # ratio calculated hereafter to get border and text position in # matplotlib coordinate - canvas_ratio = 1. / height # Divide by canvas height in pixel + canvas_ratio = 1. / height # Divide by canvas height in pixel graph_ratio = 1. / ( - (1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio) - * height) # Divide by figure height in pixel - + (1.0 - (CANVAS_BORDER[0] + CANVAS_BORDER[1]) * canvas_ratio) * + height) # Divide by figure height in pixel + # Update position of figure (keeping up and bottom border the same # size) self.Figure.subplotpars.update( - top= 1.0 - CANVAS_BORDER[1] * canvas_ratio, - bottom= CANVAS_BORDER[0] * canvas_ratio) - + top=1.0 - CANVAS_BORDER[1] * canvas_ratio, + bottom=CANVAS_BORDER[0] * canvas_ratio) + # Update position of items labels if self.GraphType == GRAPH_PARALLEL or self.Is3DCanvas(): num_item = len(self.Items) for idx in xrange(num_item): - + # In 3D graph items variable label are not displayed if not self.Is3DCanvas(): # Items variable labels are in figure upper left corner self.AxesLabels[idx].set_position( - (0.05, - 1.0 - (CANVAS_PADDING + + (0.05, + 1.0 - (CANVAS_PADDING + AXES_LABEL_HEIGHT * idx) * graph_ratio)) - + # Items variable labels are in figure lower right corner self.Labels[idx].set_position( - (0.95, - CANVAS_PADDING * graph_ratio + + (0.95, + CANVAS_PADDING * graph_ratio + (num_item - idx - 1) * VALUE_LABEL_HEIGHT * graph_ratio)) else: # X coordinate labels are in figure lower side self.AxesLabels[0].set_position( - (0.1, CANVAS_PADDING * graph_ratio)) + (0.1, CANVAS_PADDING * graph_ratio)) self.Labels[0].set_position( - (0.95, CANVAS_PADDING * graph_ratio)) - + (0.95, CANVAS_PADDING * graph_ratio)) + # Y coordinate labels are vertical and in figure left side self.AxesLabels[1].set_position( - (0.05, 2 * CANVAS_PADDING * graph_ratio)) + (0.05, 2 * CANVAS_PADDING * graph_ratio)) self.Labels[1].set_position( - (0.05, 1.0 - CANVAS_PADDING * graph_ratio)) - + (0.05, 1.0 - CANVAS_PADDING * graph_ratio)) + # Update subplots self.Figure.subplots_adjust() - + def RefreshViewer(self, refresh_graphics=True): """ Function called to refresh displayed by matplotlib canvas @@ -1138,55 +1138,55 @@ if refresh_graphics: # Get tick range of values to display start_tick, end_tick = self.ParentWindow.GetRange() - + # Graph is parallel - if self.GraphType == GRAPH_PARALLEL: + if self.GraphType == GRAPH_PARALLEL: # Init list of data range for each variable displayed ranges = [] - + # Get data and range for each variable displayed for idx, item in enumerate(self.Items): data, min_value, max_value = item.GetDataAndValueRange( - start_tick, end_tick, not self.ZoomFit) - + start_tick, end_tick, not self.ZoomFit) + # Check that data is not empty if data is not None: # Add variable range to list of variable data range ranges.append((min_value, max_value)) - + # Add plot to canvas if not yet created if len(self.Plots) <= idx: self.Plots.append( self.Axes.plot(data[:, 0], data[:, 1])[0]) - + # Set data to already created plot in canvas else: self.Plots[idx].set_data(data[:, 0], data[:, 1]) - + # Get X and Y axis ranges x_min, x_max = start_tick, end_tick y_min, y_max = merge_ranges(ranges) - + # Display cursor in canvas if a cursor tick is defined and it is # include in values tick range - if (self.CursorTick is not None and - start_tick <= self.CursorTick <= end_tick): - + if self.CursorTick is not None and \ + start_tick <= self.CursorTick <= end_tick: + # Define a vertical line to display cursor position if no # line is already defined if self.VLine is None: - self.VLine = self.Axes.axvline(self.CursorTick, + self.VLine = self.Axes.axvline(self.CursorTick, color=CURSOR_COLOR) - + # Set value of vertical line if already defined else: self.VLine.set_xdata((self.CursorTick, self.CursorTick)) self.VLine.set_visible(True) - + # Hide vertical line if cursor tick is not defined or reset elif self.VLine is not None: self.VLine.set_visible(False) - + # Graph is orthogonal else: # Update tick range, removing ticks that don't have a value for @@ -1194,76 +1194,76 @@ start_tick = max(start_tick, self.GetItemsMinCommonTick()) end_tick = max(end_tick, start_tick) items = self.ItemsDict.values() - + # Get data and range for first variable (X coordinate) x_data, x_min, x_max = items[0].GetDataAndValueRange( - start_tick, end_tick, not self.ZoomFit) + start_tick, end_tick, not self.ZoomFit) + # Get data and range for second variable (Y coordinate) y_data, y_min, y_max = items[1].GetDataAndValueRange( - start_tick, end_tick, not self.ZoomFit) - + start_tick, end_tick, not self.ZoomFit) + # Normalize X and Y coordinates value range x_min, x_max = merge_ranges([(x_min, x_max)]) y_min, y_max = merge_ranges([(y_min, y_max)]) - - # Get X and Y coordinates for cursor if cursor tick is defined + + # Get X and Y coordinates for cursor if cursor tick is defined if self.CursorTick is not None: - x_cursor, x_forced = items[0].GetValue( - self.CursorTick, raw=True) - y_cursor, y_forced = items[1].GetValue( - self.CursorTick, raw=True) - + x_cursor, _x_forced = items[0].GetValue( + self.CursorTick, raw=True) + y_cursor, _y_forced = items[1].GetValue( + self.CursorTick, raw=True) + # Get common data length so that each value has an x and y # coordinate length = (min(len(x_data), len(y_data)) if x_data is not None and y_data is not None else 0) - - # Graph is orthogonal 2D + + # Graph is orthogonal 2D if len(self.Items) < 3: - + # Check that x and y data are not empty if x_data is not None and y_data is not None: - + # Add plot to canvas if not yet created if len(self.Plots) == 0: self.Plots.append( - self.Axes.plot(x_data[:, 1][:length], + self.Axes.plot(x_data[:, 1][:length], y_data[:, 1][:length])[0]) - + # Set data to already created plot in canvas else: self.Plots[0].set_data( - x_data[:, 1][:length], + x_data[:, 1][:length], y_data[:, 1][:length]) - + # Display cursor in canvas if a cursor tick is defined and it is # include in values tick range - if (self.CursorTick is not None and - start_tick <= self.CursorTick <= end_tick): - + if self.CursorTick is not None and \ + start_tick <= self.CursorTick <= end_tick: + # Define a vertical line to display cursor x coordinate # if no line is already defined if self.VLine is None: - self.VLine = self.Axes.axvline(x_cursor, + self.VLine = self.Axes.axvline(x_cursor, color=CURSOR_COLOR) # Set value of vertical line if already defined else: self.VLine.set_xdata((x_cursor, x_cursor)) - - + # Define a horizontal line to display cursor y # coordinate if no line is already defined if self.HLine is None: - self.HLine = self.Axes.axhline(y_cursor, + self.HLine = self.Axes.axhline(y_cursor, color=CURSOR_COLOR) # Set value of horizontal line if already defined else: self.HLine.set_ydata((y_cursor, y_cursor)) - + self.VLine.set_visible(True) self.HLine.set_visible(True) - + # Hide vertical and horizontal line if cursor tick is not # defined or reset else: @@ -1271,42 +1271,43 @@ self.VLine.set_visible(False) if self.HLine is not None: self.HLine.set_visible(False) - + # Graph is orthogonal 3D else: # Remove all plots already defined in 3D canvas while len(self.Axes.lines) > 0: self.Axes.lines.pop() - + # Get data and range for third variable (Z coordinate) z_data, z_min, z_max = items[2].GetDataAndValueRange( - start_tick, end_tick, not self.ZoomFit) - + start_tick, end_tick, not self.ZoomFit) + # Normalize Z coordinate value range z_min, z_max = merge_ranges([(z_min, z_max)]) - + # Check that x, y and z data are not empty - if (x_data is not None and y_data is not None and - z_data is not None): - + if x_data is not None and \ + y_data is not None and \ + z_data is not None: + # Get common data length so that each value has an x, y # and z coordinate length = min(length, len(z_data)) - + # Add plot to canvas self.Axes.plot(x_data[:, 1][:length], y_data[:, 1][:length], - zs = z_data[:, 1][:length]) - + zs=z_data[:, 1][:length]) + # Display cursor in canvas if a cursor tick is defined and # it is include in values tick range - if (self.CursorTick is not None and - start_tick <= self.CursorTick <= end_tick): - + if self.CursorTick is not None and \ + start_tick <= self.CursorTick <= end_tick: + # Get Z coordinate for cursor - z_cursor, z_forced = items[2].GetValue( - self.CursorTick, raw=True) - + z_cursor, _z_forced = items[2].GetValue( + self.CursorTick, raw=True) + # Add 3 lines parallel to x, y and z axis to display # cursor position in 3D for kwargs in [{"xs": numpy.array([x_min, x_max])}, @@ -1319,50 +1320,50 @@ kwargs.setdefault(param, value) kwargs["color"] = CURSOR_COLOR self.Axes.plot(**kwargs) - + # Set Z axis limits self.Axes.set_zlim(z_min, z_max) - + # Set X and Y axis limits self.Axes.set_xlim(x_min, x_max) self.Axes.set_ylim(y_min, y_max) - + # Get value and forced flag for each variable displayed in graph # If cursor tick is not defined get value and flag of last received # or get value and flag of variable at cursor tick - values, forced = apply(zip, [ - (item.GetValue(self.CursorTick) - if self.CursorTick is not None - else (item.GetValue(), item.IsForced())) - for item in self.Items]) - + values, forced = apply(zip, [( + item.GetValue(self.CursorTick) + if self.CursorTick is not None + else (item.GetValue(), item.IsForced()) + ) for item in self.Items]) + # Get path of each variable displayed simplified using panel variable # name mask - labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask()) + labels = [item.GetVariable(self.ParentWindow.GetVariableNameMask()) for item in self.Items] - - # Get style for each variable according to + + # Get style for each variable according to styles = map(lambda x: {True: 'italic', False: 'normal'}[x], forced) - + # Graph is orthogonal 3D, set variables path as 3D axis label if self.Is3DCanvas(): - for idx, label_func in enumerate([self.Axes.set_xlabel, + for idx, label_func in enumerate([self.Axes.set_xlabel, self.Axes.set_ylabel, self.Axes.set_zlabel]): label_func(labels[idx], fontdict={'size': 'small', 'color': COLOR_CYCLE[idx]}) - + # Graph is not orthogonal 3D, set variables path in axes labels else: for label, text in zip(self.AxesLabels, labels): label.set_text(text) - + # Set value label text and style according to value and forced flag for # each variable displayed for label, value, style in zip(self.Labels, values, styles): label.set_text(value) label.set_style(style) - + # Refresh figure self.draw() @@ -1372,51 +1373,49 @@ """ # Render figure using agg FigureCanvasAgg.draw(self) - + # Get bitmap of figure rendered self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None) - if wx.VERSION < (3, 0, 0): + if wx.VERSION < (3, 0, 0): self.bitmap.UseAlpha() - + # Create DC for rendering graphics in bitmap destDC = wx.MemoryDC() destDC.SelectObject(self.bitmap) - + # Get Graphics Context for DC, for anti-aliased and transparent # rendering destGC = wx.GCDC(destDC) - + destGC.BeginDrawing() - + # Get canvas size and figure bounding box in canvas width, height = self.GetSize() bbox = self.GetAxesBoundingBox() - + # If highlight to display is resize, draw thick grey line at bottom - # side of canvas + # side of canvas if self.Highlight == HIGHLIGHT_RESIZE: - destGC.SetPen(HIGHLIGHT_RESIZE_PEN) - destGC.SetBrush(HIGHLIGHT_RESIZE_BRUSH) + destGC.SetPen(HIGHLIGHT['RESIZE_PEN']) + destGC.SetBrush(HIGHLIGHT['RESIZE_BRUSH']) destGC.DrawRectangle(0, height - 5, width, 5) - + # If highlight to display is merging graph, draw 50% transparent blue # rectangle on left or right part of figure depending on highlight type elif self.Highlight in [HIGHLIGHT_LEFT, HIGHLIGHT_RIGHT]: - destGC.SetPen(HIGHLIGHT_DROP_PEN) - destGC.SetBrush(HIGHLIGHT_DROP_BRUSH) - - x_offset = (bbox.width / 2 + destGC.SetPen(HIGHLIGHT['DROP_PEN']) + destGC.SetBrush(HIGHLIGHT['DROP_BRUSH']) + + x_offset = (bbox.width / 2 if self.Highlight == HIGHLIGHT_RIGHT else 0) - destGC.DrawRectangle(bbox.x + x_offset, bbox.y, + destGC.DrawRectangle(bbox.x + x_offset, bbox.y, bbox.width / 2, bbox.height) - + # Draw other Viewer common elements self.DrawCommonElements(destGC, self.GetButtons()) - + destGC.EndDrawing() - + self._isDrawn = True self.gui_repaint(drawDC=drawDC) - - diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/DebugVariableItem.py --- a/controls/DebugVariablePanel/DebugVariableItem.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableItem.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,29 +22,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +import binascii import numpy -import binascii - from graphics.DebugDataConsumer import DebugDataConsumer, TYPE_TRANSLATOR -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Constant for calculate CRC for string variables -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- STRING_CRC_SIZE = 8 STRING_CRC_MASK = 2 ** STRING_CRC_SIZE - 1 -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Item Class -#------------------------------------------------------------------------------- - -""" -Class that implements an element that consumes debug values for PLC variable and -stores received values for displaying them in graphic panel or table -""" +# ------------------------------------------------------------------------------- + class DebugVariableItem(DebugDataConsumer): - + """ + Class that implements an element that consumes debug values for PLC variable and + stores received values for displaying them in graphic panel or table + """ + def __init__(self, parent, variable, store_data=False): """ Constructor @@ -52,24 +53,24 @@ @param variable: Path of variable to debug """ DebugDataConsumer.__init__(self) - + self.Parent = parent self.Variable = variable self.StoreData = store_data - + # Get Variable data type self.RefreshVariableType() - + def __del__(self): """ Destructor """ # Reset reference to debug variable panel self.Parent = None - + def SetVariable(self, variable): """ - Set path of variable + Set path of variable @param variable: Path of variable to debug """ if self.Parent is not None and self.Variable != variable: @@ -77,46 +78,46 @@ self.Variable = variable # Get Variable data type self.RefreshVariableType() - + # Refresh debug variable panel self.Parent.RefreshView() - + def GetVariable(self, mask=None): """ - Return path of variable + Return path of variable @param mask: Mask to apply to variable path [var_name, '*',...] @return: String containing masked variable path """ # Apply mask to variable name if mask is not None: # '#' correspond to parts that are different between all items - + # Extract variable path parts parts = self.Variable.split('.') # Adjust mask size to size of variable path mask = mask + ['*'] * max(0, len(parts) - len(mask)) - + # Store previous mask last = None # Init masked variable path variable = "" - + for m, p in zip(mask, parts): # Part is not masked, add part prefixed with '.' is previous # wasn't masked if m == '*': variable += ('.' if last == '*' else '') + p - + # Part is mask, add '..' if first or previous wasn't masked elif last is None or last == '*': variable += '..' - + last = m - + return variable - + return self.Variable - + def RefreshVariableType(self): """ Get and store variable data type @@ -124,14 +125,14 @@ self.VariableType = self.Parent.GetDataType(self.Variable) # Reset data stored self.ResetData() - + def GetVariableType(self): """ Return variable data type @return: Variable data type """ return self.VariableType - + def GetData(self, start_tick=None, end_tick=None): """ Return data stored contained in given range @@ -142,7 +143,7 @@ # Return immediately if data empty or none if self.Data is None or len(self.Data) == 0: return self.Data - + # Find nearest data outside given range indexes start_idx = (self.GetNearestData(start_tick, -1) if start_tick is not None @@ -150,28 +151,27 @@ end_idx = (self.GetNearestData(end_tick, 1) if end_tick is not None else len(self.Data)) - + # Return data between indexes return self.Data[start_idx:end_idx] - + def GetRawValue(self, index): """ Return raw value at given index for string variables @param index: Variable value index @return: Variable data type """ - if (self.VariableType in ["STRING", "WSTRING"] and - index < len(self.RawData)): + if self.VariableType in ["STRING", "WSTRING"] and index < len(self.RawData): return self.RawData[index][0] return "" - + def GetValueRange(self): """ Return variable value range @return: (minimum_value, maximum_value) """ return self.MinValue, self.MaxValue - + def GetDataAndValueRange(self, start_tick, end_tick, full_range=True): """ Return variable data and value range for a given tick range @@ -179,16 +179,16 @@ @param end_tick: end tick of given range (default None, last data) @param full_range: Value range is calculated on whole data (False: only calculated on data in given range) - @return: (numpy.array([(tick, value, forced),...]), + @return: (numpy.array([(tick, value, forced),...]), min_value, max_value) """ # Get data in given tick range data = self.GetData(start_tick, end_tick) - + # Value range is calculated on whole data if full_range: return data, self.MinValue, self.MaxValue - + # Check that data in given range is not empty values = data[:, 1] if len(values) > 0: @@ -196,10 +196,10 @@ return (data, data[numpy.argmin(values), 1], data[numpy.argmax(values), 1]) - + # Return default values return data, None, None - + def ResetData(self): """ Reset data stored when store data option enabled @@ -207,31 +207,31 @@ if self.StoreData and self.IsNumVariable(): # Init table storing data self.Data = numpy.array([]).reshape(0, 3) - + # Init table storing raw data if variable is strin self.RawData = ([] if self.VariableType in ["STRING", "WSTRING"] else None) - + # Init Value range variables self.MinValue = None self.MaxValue = None - + else: self.Data = None - + # Init variable value self.Value = "" - + def IsNumVariable(self): """ Return if variable data type is numeric. String variables are considered as numeric (string CRC) @return: True if data type is numeric """ - return (self.Parent.IsNumType(self.VariableType) or + return (self.Parent.IsNumType(self.VariableType) or self.VariableType in ["STRING", "WSTRING"]) - + def NewValues(self, ticks, values): """ Function called by debug thread when a new debug value is available @@ -240,24 +240,24 @@ @param forced: Forced flag, True if value is forced (default: False) """ DebugDataConsumer.NewValues(self, ticks[-1], values[-1], raw=None) - + if self.Data is not None: - + if self.VariableType in ["STRING", "WSTRING"]: last_raw_data = (self.RawData[-1] if len(self.RawData) > 0 else None) last_raw_data_idx = len(self.RawData) - 1 - + data_values = [] for tick, (value, forced) in zip(ticks, values): # Translate forced flag to float for storing in Data table forced_value = float(forced) - + # String data value is CRC num_value = (binascii.crc32(value) & STRING_CRC_MASK if self.VariableType in ["STRING", "WSTRING"] else float(value)) - + # Update variable range values self.MinValue = (min(self.MinValue, num_value) if self.MinValue is not None @@ -265,10 +265,10 @@ self.MaxValue = (max(self.MaxValue, num_value) if self.MaxValue is not None else num_value) - + # In the case of string variables, we store raw string value and # forced flag in raw data table. Only changes in this two values - # are stored. Index to the corresponding raw value is stored in + # are stored. Index to the corresponding raw value is stored in # data third column if self.VariableType in ["STRING", "WSTRING"]: raw_data = (value, forced_value) @@ -277,20 +277,20 @@ last_raw_data = raw_data self.RawData.append(raw_data) extra_value = last_raw_data_idx - + # In other case, data third column is forced flag else: extra_value = forced_value - + data_values.append( [float(tick), num_value, extra_value]) - + # Add New data to stored data table self.Data = numpy.append(self.Data, data_values, axis=0) - + # Signal to debug variable panel to refresh self.Parent.HasNewData = True - + def SetForced(self, forced): """ Update Forced flag @@ -299,29 +299,27 @@ # Store forced flag if self.Forced != forced: self.Forced = forced - + # Signal to debug variable panel to refresh self.Parent.HasNewData = True - + def SetValue(self, value): """ Update value. @param value: New value """ # Remove quote and double quote surrounding string value to get raw value - if (self.VariableType == "STRING" and - value.startswith("'") and value.endswith("'") or - self.VariableType == "WSTRING" and - value.startswith('"') and value.endswith('"')): + if self.VariableType == "STRING" and value.startswith("'") and value.endswith("'") or \ + self.VariableType == "WSTRING" and value.startswith('"') and value.endswith('"'): value = value[1:-1] - + # Store variable value if self.Value != value: self.Value = value - + # Signal to debug variable panel to refresh self.Parent.HasNewData = True - + def GetValue(self, tick=None, raw=False): """ Return current value or value and forced flag for tick given @@ -329,30 +327,31 @@ """ # If tick given and stored data option enabled if tick is not None and self.Data is not None: - + # Return current value and forced flag if data empty if len(self.Data) == 0: return self.Value, self.IsForced() - + # Get index of nearest data from tick given idx = self.GetNearestData(tick, 0) - + # Get value and forced flag at given index - value, forced = self.RawData[int(self.Data[idx, 2])] \ - if self.VariableType in ["STRING", "WSTRING"] \ - else self.Data[idx, 1:3] - + value, forced = \ + self.RawData[int(self.Data[idx, 2])] \ + if self.VariableType in ["STRING", "WSTRING"] \ + else self.Data[idx, 1:3] + # Get raw value if asked if not raw: value = TYPE_TRANSLATOR.get( - self.VariableType, str)(value) - + self.VariableType, str)(value) + return value, forced - + # Return raw value if asked if not raw and self.VariableType in ["STRING", "WSTRING"]: return TYPE_TRANSLATOR.get( - self.VariableType, str)(self.Value) + self.VariableType, str)(self.Value) return self.Value def GetNearestData(self, tick, adjust): @@ -368,16 +367,16 @@ # Return immediately if data is empty if self.Data is None: return None - + # Extract data ticks ticks = self.Data[:, 0] - + # Get nearest data from tick idx = numpy.argmin(abs(ticks - tick)) - + # Adjust data index according to constraint - if (adjust < 0 and ticks[idx] > tick and idx > 0 or - adjust > 0 and ticks[idx] < tick and idx < len(ticks)): + if adjust < 0 and ticks[idx] > tick and idx > 0 or \ + adjust > 0 and ticks[idx] < tick and idx < len(ticks): idx += adjust - + return idx diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/DebugVariablePanel.py --- a/controls/DebugVariablePanel/DebugVariablePanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariablePanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,30 +22,35 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from types import TupleType -import math import numpy import wx import wx.lib.buttons + +# pylint: disable=wrong-import-position import matplotlib -matplotlib.use('WX') +matplotlib.use('WX') # noqa import matplotlib.pyplot from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap + from editors.DebugViewer import DebugViewer from util.BitmapLibrary import GetBitmap -from DebugVariableItem import DebugVariableItem -from DebugVariableTextViewer import DebugVariableTextViewer -from DebugVariableGraphicViewer import * - -MILLISECOND = 1000000 # Number of nanosecond in a millisecond -SECOND = 1000 * MILLISECOND # Number of nanosecond in a second -MINUTE = 60 * SECOND # Number of nanosecond in a minute -HOUR = 60 * MINUTE # Number of nanosecond in a hour -DAY = 24 * HOUR # Number of nanosecond in a day +from controls.DebugVariablePanel.DebugVariableItem import DebugVariableItem +from controls.DebugVariablePanel.DebugVariableTextViewer import DebugVariableTextViewer +from controls.DebugVariablePanel.DebugVariableGraphicViewer import * + + +MILLISECOND = 1000000 # Number of nanosecond in a millisecond +SECOND = 1000 * MILLISECOND # Number of nanosecond in a second +MINUTE = 60 * SECOND # Number of nanosecond in a minute +HOUR = 60 * MINUTE # Number of nanosecond in a hour +DAY = 24 * HOUR # Number of nanosecond in a day # List of values possible for graph range # Format is [(time_in_plain_text, value_in_nanosecond),...] @@ -58,33 +63,35 @@ # Scrollbar increment in pixel SCROLLBAR_UNIT = 10 + def compute_mask(x, y): return [(xp if xp == yp else "*") for xp, yp in zip(x, y)] + def NextTick(variables): next_tick = None - for item, data in variables: + for _item, data in variables: if len(data) == 0: continue - + next_tick = (data[0][0] if next_tick is None else min(next_tick, data[0][0])) - + return next_tick -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Graphic Panel Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Graphic -Panel -""" +# ------------------------------------------------------------------------------- + class DebugVariableDropTarget(wx.TextDropTarget): - + """ + Class that implements a custom drop target class for Debug Variable Graphic + Panel + """ + def __init__(self, window): """ Constructor @@ -92,14 +99,14 @@ """ wx.TextDropTarget.__init__(self) self.ParentWindow = window - + def __del__(self): """ Destructor """ # Remove reference to Debug Variable Panel self.ParentWindow = None - + def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -107,10 +114,10 @@ @param y: Y coordinate of mouse pointer @param d: Suggested default for return value """ - # Signal Debug Variable Panel to refresh highlight giving mouse position + # Signal Debug Variable Panel to refresh highlight giving mouse position self.ParentWindow.RefreshHighlight(x, y) return wx.TextDropTarget.OnDragOver(self, x, y, d) - + def OnDropText(self, x, y, data): """ Function called when mouse is released in Drop Target @@ -120,34 +127,34 @@ """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + message = None - + # Check that data is valid regarding DebugVariablePanel try: values = eval(data) if not isinstance(values, TupleType): raise ValueError - except: - message = _("Invalid value \"%s\" for debug variable")%data + except Exception: + message = _("Invalid value \"%s\" for debug variable") % data values = None - + # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) - + # Data contain a reference to a variable to debug elif values[1] == "debug": - + # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel + # Variable Panel if len(values) > 2 and values[2] == "move": self.ParentWindow.MoveValue(values[0]) - + # Drag'n Drop was initiated by another control of Beremiz else: self.ParentWindow.InsertValue(values[0], force=True) - + def OnLeave(self): """ Function called when mouse is leave Drop Target @@ -155,30 +162,25 @@ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() return wx.TextDropTarget.OnLeave(self) - + def ShowMessage(self, message): """ Show error message in Error Dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self.ParentWindow, + message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() -#------------------------------------------------------------------------------- -# Debug Variable Graphic Panel Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a graphs -""" - class DebugVariablePanel(wx.Panel, DebugViewer): - + """ + Class that implements a Viewer that display variable values as a graphs + """ + def __init__(self, parent, producer, window): """ Constructor @@ -187,112 +189,113 @@ consumers @param window: Reference to Beremiz frame """ - wx.Panel.__init__(self, parent, style=wx.SP_3D|wx.TAB_TRAVERSAL) - + wx.Panel.__init__(self, parent, style=wx.SP_3D | wx.TAB_TRAVERSAL) + # Save Reference to Beremiz frame self.ParentWindow = window - + # Variable storing flag indicating that variable displayed in table # received new value and then table need to be refreshed self.HasNewData = False - + # Variable storing flag indicating that refresh has been forced, and # that next time refresh is possible, it will be done even if no new # data is available self.Force = False - + self.SetBackgroundColour(wx.WHITE) - + main_sizer = wx.BoxSizer(wx.VERTICAL) - - self.Ticks = numpy.array([]) # List of tick received - self.StartTick = 0 # Tick starting range of data displayed - self.Fixed = False # Flag that range of data is fixed - self.CursorTick = None # Tick of cursor for displaying values - + + self.Ticks = numpy.array([]) # List of tick received + self.StartTick = 0 # Tick starting range of data displayed + self.Fixed = False # Flag that range of data is fixed + self.CursorTick = None # Tick of cursor for displaying values + self.DraggingAxesPanel = None self.DraggingAxesBoundingBox = None self.DraggingAxesMousePos = None self.VetoScrollEvent = False - + self.VariableNameMask = [] - + self.GraphicPanels = [] - + graphics_button_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW|wx.ALL) - + main_sizer.AddSizer(graphics_button_sizer, border=5, flag=wx.GROW | wx.ALL) + range_label = wx.StaticText(self, label=_('Range:')) graphics_button_sizer.AddWindow(range_label, flag=wx.ALIGN_CENTER_VERTICAL) - + self.CanvasRange = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnRangeChanged, self.CanvasRange) - graphics_button_sizer.AddWindow(self.CanvasRange, 1, - border=5, flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - + graphics_button_sizer.AddWindow(self.CanvasRange, 1, + border=5, + flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) + self.CanvasRange.Clear() default_range_idx = 0 - for idx, (text, value) in enumerate(RANGE_VALUES): + for idx, (text, _value) in enumerate(RANGE_VALUES): self.CanvasRange.Append(text) if text == "1s": default_range_idx = idx self.CanvasRange.SetSelection(default_range_idx) - + for name, bitmap, help in [ - ("CurrentButton", "current", _("Go to current value")), - ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: - button = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + ("CurrentButton", "current", _("Go to current value")), + ("ExportGraphButton", "export_graph", _("Export graph values to clipboard"))]: + button = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) graphics_button_sizer.AddWindow(button, border=5, flag=wx.LEFT) - - self.CanvasPosition = wx.ScrollBar(self, - size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) - self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, - self.OnPositionChanging, self.CanvasPosition) - self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, - self.OnPositionChanging, self.CanvasPosition) - main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW|wx.LEFT|wx.RIGHT|wx.BOTTOM) - + + self.CanvasPosition = wx.ScrollBar( + self, size=wx.Size(0, 16), style=wx.SB_HORIZONTAL) + self.CanvasPosition.Bind(wx.EVT_SCROLL_THUMBTRACK, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEUP, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_LINEDOWN, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEUP, + self.OnPositionChanging, self.CanvasPosition) + self.CanvasPosition.Bind(wx.EVT_SCROLL_PAGEDOWN, + self.OnPositionChanging, self.CanvasPosition) + main_sizer.AddWindow(self.CanvasPosition, border=5, flag=wx.GROW | wx.LEFT | wx.RIGHT | wx.BOTTOM) + self.TickSizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL|wx.GROW) - + main_sizer.AddSizer(self.TickSizer, border=5, flag=wx.ALL | wx.GROW) + self.TickLabel = wx.StaticText(self) self.TickSizer.AddWindow(self.TickLabel, border=5, flag=wx.RIGHT) - - self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY|wx.TE_CENTER|wx.NO_BORDER) - self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT|wx.GROW) - + + self.MaskLabel = wx.TextCtrl(self, style=wx.TE_READONLY | wx.TE_CENTER | wx.NO_BORDER) + self.TickSizer.AddWindow(self.MaskLabel, 1, border=5, flag=wx.RIGHT | wx.GROW) + self.TickTimeLabel = wx.StaticText(self) self.TickSizer.AddWindow(self.TickTimeLabel) - - self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL|wx.VSCROLL) + + self.GraphicsWindow = wx.ScrolledWindow(self, style=wx.HSCROLL | wx.VSCROLL) self.GraphicsWindow.SetBackgroundColour(wx.WHITE) self.GraphicsWindow.SetDropTarget(DebugVariableDropTarget(self)) self.GraphicsWindow.Bind(wx.EVT_ERASE_BACKGROUND, self.OnGraphicsWindowEraseBackground) self.GraphicsWindow.Bind(wx.EVT_PAINT, self.OnGraphicsWindowPaint) self.GraphicsWindow.Bind(wx.EVT_SIZE, self.OnGraphicsWindowResize) self.GraphicsWindow.Bind(wx.EVT_MOUSEWHEEL, self.OnGraphicsWindowMouseWheel) - + main_sizer.AddWindow(self.GraphicsWindow, 1, flag=wx.GROW) - + self.GraphicsSizer = wx.BoxSizer(wx.VERTICAL) self.GraphicsWindow.SetSizer(self.GraphicsSizer) - + DebugViewer.__init__(self, producer, True) - + self.SetSizer(main_sizer) self.SetTickTime() - + def SetTickTime(self, ticktime=0): """ Set Ticktime for calculate data range according to time range selected @@ -300,26 +303,26 @@ """ # Save ticktime self.Ticktime = ticktime - + # Set ticktime to millisecond if undefined if self.Ticktime == 0: self.Ticktime = MILLISECOND - + # Calculate range to apply to data self.CurrentRange = RANGE_VALUES[ self.CanvasRange.GetSelection()][1] / self.Ticktime - + def SetDataProducer(self, producer): """ Set Data Producer @param producer: Data Producer """ DebugViewer.SetDataProducer(self, producer) - + # Set ticktime if data producer is available if self.DataProducer is not None: self.SetTickTime(self.DataProducer.GetTicktime()) - + def RefreshNewData(self): """ Called to refresh Panel according to values received by variables @@ -329,50 +332,50 @@ if self.HasNewData or self.Force: self.HasNewData = False self.RefreshView() - + DebugViewer.RefreshNewData(self) - + def NewDataAvailable(self, ticks): """ Called by DataProducer for each tick captured or by panel to refresh graphs @param tick: PLC tick captured - All other parameters are passed to refresh function + All other parameters are passed to refresh function """ # If tick given if ticks is not None: tick = ticks[-1] - + # Save tick as start tick for range if data is still empty if len(self.Ticks) == 0: self.StartTick = ticks[0] - + # Add tick to list of ticks received self.Ticks = numpy.append(self.Ticks, ticks) - + # Update start tick for range if range follow ticks received if not self.Fixed or tick < self.StartTick + self.CurrentRange: self.StartTick = max(self.StartTick, tick - self.CurrentRange) - + # Force refresh if graph is fixed because range of data received # is too small to fill data range selected if self.Fixed and \ self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: self.Force = True - + self.HasNewData = False self.RefreshView() - + else: DebugViewer.NewDataAvailable(self, ticks) - + def ForceRefresh(self): """ Called to force refresh of graphs """ self.Force = True wx.CallAfter(self.NewDataAvailable, None) - + def SetCursorTick(self, cursor_tick): """ Set Cursor for displaying values of items at a tick given @@ -381,37 +384,36 @@ # Save cursor tick self.CursorTick = cursor_tick self.Fixed = cursor_tick is not None - self.UpdateCursorTick() - + self.UpdateCursorTick() + def MoveCursorTick(self, move): if self.CursorTick is not None: - cursor_tick = max(self.Ticks[0], - min(self.CursorTick + move, - self.Ticks[-1])) + cursor_tick = max(self.Ticks[0], + min(self.CursorTick + move, self.Ticks[-1])) cursor_tick_idx = numpy.argmin(numpy.abs(self.Ticks - cursor_tick)) if self.Ticks[cursor_tick_idx] == self.CursorTick: - cursor_tick_idx = max(0, - min(cursor_tick_idx + abs(move) / move, - len(self.Ticks) - 1)) + cursor_tick_idx = max(0, + min(cursor_tick_idx + abs(move) / move, + len(self.Ticks) - 1)) self.CursorTick = self.Ticks[cursor_tick_idx] - self.StartTick = max(self.Ticks[ - numpy.argmin(numpy.abs(self.Ticks - - self.CursorTick + self.CurrentRange))], - min(self.StartTick, self.CursorTick)) + self.StartTick = max( + self.Ticks[numpy.argmin( + numpy.abs(self.Ticks - self.CursorTick + self.CurrentRange))], + min(self.StartTick, self.CursorTick)) self.RefreshCanvasPosition() - self.UpdateCursorTick() - + self.UpdateCursorTick() + def ResetCursorTick(self): self.CursorTick = None self.Fixed = False self.UpdateCursorTick() - + def UpdateCursorTick(self): for panel in self.GraphicPanels: if isinstance(panel, DebugVariableGraphicViewer): panel.SetCursorTick(self.CursorTick) self.ForceRefresh() - + def StartDragNDrop(self, panel, item, x_mouse, y_mouse, x_mouse_start, y_mouse_start): if len(panel.GetItems()) > 1: self.DraggingAxesPanel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) @@ -424,23 +426,23 @@ self.DraggingAxesPanel = panel self.DraggingAxesBoundingBox = panel.GetAxesBoundingBox(parent_coordinate=True) self.DraggingAxesMousePos = wx.Point( - x_mouse_start - self.DraggingAxesBoundingBox.x, + x_mouse_start - self.DraggingAxesBoundingBox.x, y_mouse_start - self.DraggingAxesBoundingBox.y) self.MoveDragNDrop(x_mouse, y_mouse) - + def MoveDragNDrop(self, x_mouse, y_mouse): self.DraggingAxesBoundingBox.x = x_mouse - self.DraggingAxesMousePos.x self.DraggingAxesBoundingBox.y = y_mouse - self.DraggingAxesMousePos.y self.RefreshHighlight(x_mouse, y_mouse) - + def RefreshHighlight(self, x_mouse, y_mouse): for idx, panel in enumerate(self.GraphicPanels): x, y = panel.GetPosition() width, height = panel.GetSize() rect = wx.Rect(x, y, width, height) - if (rect.InsideXY(x_mouse, y_mouse) or - idx == 0 and y_mouse < 0 or - idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]): + if rect.InsideXY(x_mouse, y_mouse) or \ + idx == 0 and y_mouse < 0 or \ + idx == len(self.GraphicPanels) - 1 and y_mouse > panel.GetPosition()[1]: panel.RefreshHighlight(x_mouse - x, y_mouse - y) else: panel.SetHighlight(HIGHLIGHT_NONE) @@ -448,7 +450,7 @@ self.RefreshView() else: self.ForceRefresh() - + def ResetHighlight(self): for panel in self.GraphicPanels: panel.SetHighlight(HIGHLIGHT_NONE) @@ -456,10 +458,10 @@ self.RefreshView() else: self.ForceRefresh() - + def IsDragging(self): return self.DraggingAxesPanel is not None - + def GetDraggingAxesClippingRegion(self, panel): x, y = panel.GetPosition() width, height = panel.GetSize() @@ -468,12 +470,12 @@ bbox.x -= x bbox.y -= y return bbox - + def GetDraggingAxesPosition(self, panel): x, y = panel.GetPosition() return wx.Point(self.DraggingAxesBoundingBox.x - x, self.DraggingAxesBoundingBox.y - y) - + def StopDragNDrop(self, variable, x_mouse, y_mouse): if self.DraggingAxesPanel not in self.GraphicPanels: self.DraggingAxesPanel.Destroy() @@ -504,25 +506,25 @@ idx += 1 wx.CallAfter(self.MoveValue, variable, idx, True) self.ForceRefresh() - return + return width, height = self.GraphicsWindow.GetVirtualSize() rect = wx.Rect(0, 0, width, height) if rect.InsideXY(x_mouse, y_mouse): wx.CallAfter(self.MoveValue, variable, len(self.GraphicPanels), True) self.ForceRefresh() - + def RefreshGraphicsSizer(self): self.GraphicsSizer.Clear() - + for panel in self.GraphicPanels: self.GraphicsSizer.AddWindow(panel, flag=wx.GROW) - + self.GraphicsSizer.Layout() self.RefreshGraphicsWindowScrollbars() - + def RefreshView(self): self.RefreshCanvasPosition() - + width, height = self.GraphicsWindow.GetVirtualSize() bitmap = wx.EmptyBitmap(width, height) dc = wx.BufferedDC(wx.ClientDC(self.GraphicsWindow), bitmap) @@ -531,22 +533,22 @@ if self.DraggingAxesPanel is not None: destBBox = self.DraggingAxesBoundingBox srcBBox = self.DraggingAxesPanel.GetAxesBoundingBox() - + srcBmp = _convert_agg_to_wx_bitmap(self.DraggingAxesPanel.get_renderer(), None) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - - dc.Blit(destBBox.x, destBBox.y, - int(destBBox.width), int(destBBox.height), + + dc.Blit(destBBox.x, destBBox.y, + int(destBBox.width), int(destBBox.height), srcDC, srcBBox.x, srcBBox.y) dc.EndDrawing() - + if not self.Fixed or self.Force: self.Force = False refresh_graphics = True else: refresh_graphics = False - + if self.DraggingAxesPanel is not None and self.DraggingAxesPanel not in self.GraphicPanels: self.DraggingAxesPanel.RefreshViewer(refresh_graphics) for panel in self.GraphicPanels: @@ -554,7 +556,7 @@ panel.RefreshViewer(refresh_graphics) else: panel.RefreshViewer() - + if self.CursorTick is not None: tick = self.CursorTick elif len(self.Ticks) > 0: @@ -570,27 +572,27 @@ ((tick_duration % DAY) / HOUR, _("%dh")), ((tick_duration % HOUR) / MINUTE, _("%dm")), ((tick_duration % MINUTE) / SECOND, _("%ds"))]: - + if value > 0 or not_null: duration += format % value not_null = True - - duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) + + duration += _("%03gms") % (float(tick_duration % SECOND) / MILLISECOND) self.TickTimeLabel.SetLabel("t: %s" % duration) else: self.TickLabel.SetLabel("") self.TickTimeLabel.SetLabel("") self.TickSizer.Layout() - + def SubscribeAllDataConsumers(self): DebugViewer.SubscribeAllDataConsumers(self) - + if self.DataProducer is not None: if self.DataProducer is not None: self.SetTickTime(self.DataProducer.GetTicktime()) - + self.ResetCursorTick() - + for panel in self.GraphicPanels[:]: panel.SubscribeAllDataConsumers() if panel.ItemsIsEmpty(): @@ -598,28 +600,28 @@ panel.ReleaseMouse() self.GraphicPanels.remove(panel) panel.Destroy() - + self.ResetVariableNameMask() self.RefreshGraphicsSizer() self.ForceRefresh() - + def ResetView(self): self.UnsubscribeAllDataConsumers() - + self.Fixed = False for panel in self.GraphicPanels: panel.Destroy() self.GraphicPanels = [] self.ResetVariableNameMask() self.RefreshGraphicsSizer() - + def SetCanvasPosition(self, tick): tick = max(self.Ticks[0], min(tick, self.Ticks[-1] - self.CurrentRange)) self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - tick))] self.Fixed = True self.RefreshCanvasPosition() self.ForceRefresh() - + def RefreshCanvasPosition(self): if len(self.Ticks) > 0: pos = int(self.StartTick - self.Ticks[0]) @@ -628,7 +630,7 @@ pos = 0 range = 0 self.CanvasPosition.SetScrollbar(pos, self.CurrentRange, range, self.CurrentRange) - + def ChangeRange(self, dir, tick=None): current_range = self.CurrentRange current_range_idx = self.CanvasRange.GetSelection() @@ -644,7 +646,7 @@ self.StartTick = self.Ticks[numpy.argmin(numpy.abs(self.Ticks - new_start_tick))] self.Fixed = new_start_tick < self.Ticks[-1] - self.CurrentRange self.ForceRefresh() - + def RefreshRange(self): if len(self.Ticks) > 0: if self.Fixed and self.Ticks[-1] - self.Ticks[0] < self.CurrentRange: @@ -654,21 +656,21 @@ else: self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) self.ForceRefresh() - + def OnRangeChanged(self, event): try: self.CurrentRange = RANGE_VALUES[self.CanvasRange.GetSelection()][1] / self.Ticktime - except ValueError, e: + except ValueError: self.CanvasRange.SetValue(str(self.CurrentRange)) wx.CallAfter(self.RefreshRange) event.Skip() - + def OnCurrentButton(self, event): if len(self.Ticks) > 0: self.StartTick = max(self.Ticks[0], self.Ticks[-1] - self.CurrentRange) self.ResetCursorTick() event.Skip() - + def CopyDataToClipboard(self, variables): text = "tick;%s;\n" % ";".join([item.GetVariable() for item, data in variables]) next_tick = NextTick(variables) @@ -693,7 +695,7 @@ text += "%d;%s;\n" % (next_tick, ";".join(values)) next_tick = NextTick(variables) self.ParentWindow.SetCopyBuffer(text) - + def OnExportGraphButton(self, event): items = reduce(lambda x, y: x + y, [panel.GetItems() for panel in self.GraphicPanels], @@ -703,25 +705,25 @@ if item.IsNumVariable()] wx.CallAfter(self.CopyDataToClipboard, variables) event.Skip() - + def OnPositionChanging(self, event): if len(self.Ticks) > 0: self.StartTick = self.Ticks[0] + event.GetPosition() self.Fixed = True self.ForceRefresh() event.Skip() - + def GetRange(self): return self.StartTick, self.StartTick + self.CurrentRange - + def GetViewerIndex(self, viewer): if viewer in self.GraphicPanels: return self.GraphicPanels.index(viewer) return None - + def IsViewerFirst(self, viewer): return viewer == self.GraphicPanels[0] - + def HighlightPreviousViewer(self, viewer): if self.IsViewerFirst(viewer): return @@ -729,25 +731,25 @@ if idx is None: return self.GraphicPanels[idx-1].SetHighlight(HIGHLIGHT_AFTER) - + def ResetVariableNameMask(self): items = [] for panel in self.GraphicPanels: items.extend(panel.GetItems()) if len(items) > 1: - self.VariableNameMask = reduce(compute_mask, - [item.GetVariable().split('.') for item in items]) + self.VariableNameMask = reduce( + compute_mask, [item.GetVariable().split('.') for item in items]) elif len(items) > 0: self.VariableNameMask = items[0].GetVariable().split('.')[:-1] + ['*'] else: self.VariableNameMask = [] self.MaskLabel.ChangeValue(".".join(self.VariableNameMask)) self.MaskLabel.SetInsertionPoint(self.MaskLabel.GetLastPosition()) - + def GetVariableNameMask(self): return self.VariableNameMask - - def InsertValue(self, iec_path, idx = None, force=False, graph=False): + + def InsertValue(self, iec_path, idx=None, force=False, graph=False): for panel in self.GraphicPanels: if panel.GetItem(iec_path) is not None: if graph and isinstance(panel, DebugVariableTextViewer): @@ -758,7 +760,7 @@ item = DebugVariableItem(self, iec_path, True) result = self.AddDataConsumer(iec_path.upper(), item, True) if result is not None or force: - + self.Freeze() if item.IsNumVariable() and graph: panel = DebugVariableGraphicViewer(self.GraphicsWindow, self, [item], GRAPH_PARALLEL) @@ -774,8 +776,8 @@ self.RefreshGraphicsSizer() self.Thaw() self.ForceRefresh() - - def MoveValue(self, iec_path, idx = None, graph=False): + + def MoveValue(self, iec_path, idx=None, graph=False): if idx is None: idx = len(self.GraphicPanels) source_panel = None @@ -787,9 +789,9 @@ break if source_panel is not None: source_panel_idx = self.GraphicPanels.index(source_panel) - - if (len(source_panel.GetItems()) == 1): - + + if len(source_panel.GetItems()) == 1: + if source_panel_idx < idx: self.GraphicPanels.insert(idx, source_panel) self.GraphicPanels.pop(source_panel_idx) @@ -798,7 +800,7 @@ self.GraphicPanels.insert(idx, source_panel) else: return - + else: source_panel.RemoveItem(item) source_size = source_panel.GetSize() @@ -807,22 +809,22 @@ panel.SetCanvasHeight(source_size.height) if self.CursorTick is not None: panel.SetCursorTick(self.CursorTick) - + else: panel = DebugVariableTextViewer(self.GraphicsWindow, self, [item]) - + self.GraphicPanels.insert(idx, panel) - + if source_panel.ItemsIsEmpty(): if source_panel.HasCapture(): source_panel.ReleaseMouse() source_panel.Destroy() self.GraphicPanels.remove(source_panel) - + self.ResetVariableNameMask() self.RefreshGraphicsSizer() self.ForceRefresh() - + def MergeGraphs(self, source, target_idx, merge_type, force=False): source_item = None source_panel = None @@ -845,12 +847,12 @@ target_panel = self.GraphicPanels[target_idx] graph_type = target_panel.GraphType if target_panel != source_panel: - if (merge_type == GRAPH_PARALLEL and graph_type != merge_type or - merge_type == GRAPH_ORTHOGONAL and - (graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or - graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3)): + if merge_type == GRAPH_PARALLEL and graph_type != merge_type or \ + merge_type == GRAPH_ORTHOGONAL and ( + graph_type == GRAPH_PARALLEL and len(target_panel.Items) > 1 or + graph_type == GRAPH_ORTHOGONAL and len(target_panel.Items) >= 3): return - + if source_panel is not None: source_panel.RemoveItem(source_item) if source_panel.ItemsIsEmpty(): @@ -858,11 +860,11 @@ source_panel.ReleaseMouse() source_panel.Destroy() self.GraphicPanels.remove(source_panel) - elif (merge_type != graph_type and len(target_panel.Items) == 2): + elif merge_type != graph_type and len(target_panel.Items) == 2: target_panel.RemoveItem(source_item) else: target_panel = None - + if target_panel is not None: target_panel.AddItem(source_item) target_panel.GraphType = merge_type @@ -874,15 +876,15 @@ else: target_panel.SetCanvasHeight(size.height) target_panel.ResetGraphics() - + self.ResetVariableNameMask() self.RefreshGraphicsSizer() self.ForceRefresh() - + def DeleteValue(self, source_panel, item=None): source_idx = self.GetViewerIndex(source_panel) if source_idx is not None: - + if item is None: source_panel.ClearItems() source_panel.Destroy() @@ -900,7 +902,7 @@ self.Fixed = False self.ResetCursorTick() self.ForceRefresh() - + def ToggleViewerType(self, panel): panel_idx = self.GetViewerIndex(panel) if panel_idx is not None: @@ -916,7 +918,7 @@ panel.Destroy() self.RefreshGraphicsSizer() self.ForceRefresh() - + def ResetGraphicsValues(self): self.Ticks = numpy.array([]) self.StartTick = 0 @@ -931,23 +933,24 @@ posx = max(0, min(xstart, (vwidth - window_size[0]) / SCROLLBAR_UNIT)) posy = max(0, min(ystart, (vheight - window_size[1]) / SCROLLBAR_UNIT)) self.GraphicsWindow.Scroll(posx, posy) - self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, - vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, posx, posy) - + self.GraphicsWindow.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + vwidth / SCROLLBAR_UNIT, vheight / SCROLLBAR_UNIT, + posx, posy) + def OnGraphicsWindowEraseBackground(self, event): pass - + def OnGraphicsWindowPaint(self, event): self.RefreshView() event.Skip() - + def OnGraphicsWindowResize(self, event): size = self.GetSize() for panel in self.GraphicPanels: panel_size = panel.GetSize() - if (isinstance(panel, DebugVariableGraphicViewer) and - panel.GraphType == GRAPH_ORTHOGONAL and - panel_size.width == panel_size.height): + if isinstance(panel, DebugVariableGraphicViewer) and \ + panel.GraphType == GRAPH_ORTHOGONAL and \ + panel_size.width == panel_size.height: panel.SetCanvasHeight(size.width) self.RefreshGraphicsWindowScrollbars() self.GraphicsSizer.Layout() diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/DebugVariableTextViewer.py --- a/controls/DebugVariablePanel/DebugVariableTextViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableTextViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,24 +22,25 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from types import TupleType import wx -from DebugVariableItem import DebugVariableItem -from DebugVariableViewer import DebugVariableViewer -from GraphButton import GraphButton - -#------------------------------------------------------------------------------- +from controls.DebugVariablePanel.DebugVariableViewer import DebugVariableViewer +from controls.DebugVariablePanel.GraphButton import GraphButton + +# ------------------------------------------------------------------------------- # Debug Variable Text Viewer Drop Target -#------------------------------------------------------------------------------- - -""" -Class that implements a custom drop target class for Debug Variable Text Viewer -""" +# ------------------------------------------------------------------------------- + class DebugVariableTextDropTarget(wx.TextDropTarget): - + """ + Class that implements a custom drop target class for Debug Variable Text Viewer + """ + def __init__(self, parent, window): """ Constructor @@ -49,7 +50,7 @@ wx.TextDropTarget.__init__(self) self.ParentControl = parent self.ParentWindow = window - + def __del__(self): """ Destructor @@ -58,7 +59,7 @@ # Panel self.ParentControl = None self.ParentWindow = None - + def OnDragOver(self, x, y, d): """ Function called when mouse is dragged over Drop Target @@ -68,9 +69,9 @@ """ # Signal parent that mouse is dragged over self.ParentControl.OnMouseDragging(x, y) - + return wx.TextDropTarget.OnDragOver(self, x, y, d) - + def OnDropText(self, x, y, data): """ Function called when mouse is released in Drop Target @@ -80,77 +81,76 @@ """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + message = None - + # Check that data is valid regarding DebugVariablePanel try: values = eval(data) if not isinstance(values, TupleType): raise ValueError - except: + except Exception: message = _("Invalid value \"%s\" for debug variable") % data values = None - + # Display message if data is invalid if message is not None: wx.CallAfter(self.ShowMessage, message) - + # Data contain a reference to a variable to debug elif values[1] == "debug": - + # Get Before which Viewer the variable has to be moved or added # according to the position of mouse in Viewer. - width, height = self.ParentControl.GetSize() + _width, height = self.ParentControl.GetSize() target_idx = self.ParentControl.GetIndex() if y > height / 2: target_idx += 1 - + # Drag'n Drop is an internal is an internal move inside Debug - # Variable Panel + # Variable Panel if len(values) > 2 and values[2] == "move": - self.ParentWindow.MoveValue(values[0], + self.ParentWindow.MoveValue(values[0], target_idx) - + # Drag'n Drop was initiated by another control of Beremiz else: - self.ParentWindow.InsertValue(values[0], - target_idx, + self.ParentWindow.InsertValue(values[0], + target_idx, force=True) - + def OnLeave(self): """ Function called when mouse is leave Drop Target """ # Signal Debug Variable Panel to reset highlight self.ParentWindow.ResetHighlight() - + return wx.TextDropTarget.OnLeave(self) - + def ShowMessage(self, message): """ Show error message in Error Dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self.ParentWindow, - message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self.ParentWindow, + message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Debug Variable Text Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements a Viewer that display variable values as a text -""" +# ------------------------------------------------------------------------------- class DebugVariableTextViewer(DebugVariableViewer, wx.Panel): - - def __init__(self, parent, window, items=[]): + """ + Class that implements a Viewer that display variable values as a text + """ + + def __init__(self, parent, window, items=None): """ Constructor @param parent: Parent wx.Window of DebugVariableText @@ -158,13 +158,13 @@ @param items: List of DebugVariableItem displayed by Viewer """ DebugVariableViewer.__init__(self, window, items) - + wx.Panel.__init__(self, parent) # Set panel background colour self.SetBackgroundColour(wx.WHITE) # Define panel drop target self.SetDropTarget(DebugVariableTextDropTarget(self, window)) - + # Bind events self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp) @@ -174,16 +174,16 @@ self.Bind(wx.EVT_SIZE, self.OnResize) self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnEraseBackground) self.Bind(wx.EVT_PAINT, self.OnPaint) - + # Define panel min size for parent sizer layout self.SetMinSize(wx.Size(0, 25)) - + # Add buttons to Viewer for bitmap, callback in [("force", self.OnForceButton), ("release", self.OnReleaseButton), ("delete_graph", self.OnCloseButton)]: self.Buttons.append(GraphButton(0, 0, bitmap, callback)) - + def RefreshViewer(self): """ Method that refresh the content displayed by Viewer @@ -193,24 +193,24 @@ bitmap = wx.EmptyBitmap(width, height) dc = wx.BufferedDC(wx.ClientDC(self), bitmap) dc.Clear() - + # Get Graphics Context for DC, for anti-aliased and transparent # rendering gc = wx.GCDC(dc) - + gc.BeginDrawing() - + # Get first item item = self.ItemsDict.values()[0] - + # Get item variable path masked according Debug Variable Panel mask item_path = item.GetVariable( - self.ParentWindow.GetVariableNameMask()) - + self.ParentWindow.GetVariableNameMask()) + # Draw item variable path at Viewer left side w, h = gc.GetTextExtent(item_path) gc.DrawText(item_path, 20, (height - h) / 2) - + # Update 'Release' button state and text color according to item forced # flag value item_forced = item.IsForced() @@ -218,17 +218,17 @@ self.RefreshButtonsPosition() if item_forced: gc.SetTextForeground(wx.BLUE) - + # Draw item current value at right side of Viewer item_value = item.GetValue() w, h = gc.GetTextExtent(item_value) gc.DrawText(item_value, width - 40 - w, (height - h) / 2) - + # Draw other Viewer common elements self.DrawCommonElements(gc) - + gc.EndDrawing() - + def OnLeftDown(self, event): """ Function called when mouse left button is pressed @@ -236,15 +236,15 @@ """ # Get first item item = self.ItemsDict.values()[0] - + # Calculate item path bounding box - width, height = self.GetSize() + _width, height = self.GetSize() item_path = item.GetVariable( - self.ParentWindow.GetVariableNameMask()) + self.ParentWindow.GetVariableNameMask()) w, h = self.GetTextExtent(item_path) - + # Test if mouse has been pressed in this bounding box. In that case - # start a move drag'n drop of item variable + # start a move drag'n drop of item variable x, y = event.GetPosition() item_path_bbox = wx.Rect(20, (height - h) / 2, w, h) if item_path_bbox.InsideXY(x, y): @@ -253,11 +253,11 @@ dragSource = wx.DropSource(self) dragSource.SetData(data) dragSource.DoDragDrop() - + # In other case handle event normally else: event.Skip() - + def OnLeftUp(self, event): """ Function called when mouse left button is released @@ -267,7 +267,7 @@ x, y = event.GetPosition() wx.CallAfter(self.HandleButton, x, y) event.Skip() - + def OnLeftDClick(self, event): """ Function called when mouse left button is double clicked @@ -276,7 +276,7 @@ # Only numeric variables can be toggled to graph canvas if self.ItemsDict.values()[0].IsNumVariable(): self.ParentWindow.ToggleViewerType(self) - + def OnPaint(self, event): """ Function called when redrawing Viewer content is needed diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/DebugVariableViewer.py --- a/controls/DebugVariablePanel/DebugVariableViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/DebugVariableViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,12 +22,11 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from collections import OrderedDict import wx - -import matplotlib -import matplotlib.pyplot from matplotlib.backends.backend_wxagg import _convert_agg_to_wx_bitmap from dialogs.ForceVariableDialog import ForceVariableDialog @@ -41,52 +40,62 @@ HIGHLIGHT_RESIZE] = range(6) # Viewer highlight styles -HIGHLIGHT_DROP_PEN = wx.Pen(wx.Colour(0, 128, 255)) -HIGHLIGHT_DROP_BRUSH = wx.Brush(wx.Colour(0, 128, 255, 128)) -HIGHLIGHT_RESIZE_PEN = wx.Pen(wx.Colour(200, 200, 200)) -HIGHLIGHT_RESIZE_BRUSH = wx.Brush(wx.Colour(200, 200, 200)) - -#------------------------------------------------------------------------------- +HIGHLIGHT = { +} + +# ------------------------------------------------------------------------------- # Base Debug Variable Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements a generic viewer that display a list of variable values -This class has to be inherited to effectively display variable values -""" - -class DebugVariableViewer: - - def __init__(self, window, items=[]): +# ------------------------------------------------------------------------------- + + +class DebugVariableViewer(object): + """ + Class that implements a generic viewer that display a list of variable values + This class has to be inherited to effectively display variable values + """ + + def __init__(self, window, items=None): """ Constructor @param window: Reference to the Debug Variable Panel @param items: List of DebugVariableItem displayed by Viewer """ self.ParentWindow = window + items = [] if items is None else items self.ItemsDict = OrderedDict([(item.GetVariable(), item) for item in items]) self.Items = self.ItemsDict.viewvalues() - + # Variable storing current highlight displayed in Viewer self.Highlight = HIGHLIGHT_NONE # List of buttons self.Buttons = [] - + self.InitHighlightPensBrushes() + def __del__(self): """ Destructor """ # Remove reference to Debug Variable Panel self.ParentWindow = None - + + def InitHighlightPensBrushes(self): + """ + Init global pens and brushes + """ + if not HIGHLIGHT: + HIGHLIGHT['DROP_PEN'] = wx.Pen(wx.Colour(0, 128, 255)) + HIGHLIGHT['DROP_BRUSH'] = wx.Brush(wx.Colour(0, 128, 255, 128)) + HIGHLIGHT['RESIZE_PEN'] = wx.Pen(wx.Colour(200, 200, 200)) + HIGHLIGHT['RESIZE_BRUSH'] = wx.Brush(wx.Colour(200, 200, 200)) + def GetIndex(self): """ Return position of Viewer in Debug Variable Panel @return: Position of Viewer """ return self.ParentWindow.GetViewerIndex(self) - + def GetItem(self, variable): """ Return item storing values of a variable @@ -94,28 +103,28 @@ @return: Item storing values of this variable """ return self.ItemsDict.get(variable, None) - + def GetItems(self): """ Return items displayed by Viewer @return: List of items displayed in Viewer """ return self.ItemsDict.values() - + def AddItem(self, item): """ Add an item to the list of items displayed by Viewer @param item: Item to add to the list """ self.ItemsDict[item.GetVariable()] = item - + def RemoveItem(self, item): """ Remove an item from the list of items displayed by Viewer @param item: Item to remove from the list """ self.ItemsDict.pop(item.GetVariable(), None) - + def ClearItems(self): """ Clear list of items displayed by Viewer @@ -123,17 +132,17 @@ # Unsubscribe every items of the list for item in self.Items: self.ParentWindow.RemoveDataConsumer(item) - + # Clear list self.ItemsDict.clear() - + def ItemsIsEmpty(self): """ Return if list of items displayed by Viewer is empty @return: True if list is empty """ return len(self.Items) == 0 - + def SubscribeAllDataConsumers(self): """ Function that unsubscribe and remove every item that store values of @@ -141,7 +150,7 @@ """ for item in self.ItemsDict.values()[:]: iec_path = item.GetVariable() - + # Check that variablepath exist in PLC if self.ParentWindow.GetDataType(iec_path) is None: # If not, unsubscribe and remove it @@ -151,30 +160,30 @@ # If it exist, resubscribe and refresh data type self.ParentWindow.AddDataConsumer(iec_path.upper(), item, True) item.RefreshVariableType() - + def ResetItemsData(self): """ Reset data stored in every items displayed in Viewer """ for item in self.Items: item.ResetData() - + def GetItemsMinCommonTick(self): """ Return the minimum tick common to all iems displayed in Viewer @return: Minimum common tick between items """ - return reduce(max, [item.GetData()[0, 0] + return reduce(max, [item.GetData()[0, 0] for item in self.Items if len(item.GetData()) > 0], 0) - + def RefreshViewer(self): """ Method that refresh the content displayed by Viewer Need to be overridden by inherited classes """ pass - + def SetHighlight(self, highlight): """ Set Highlight type displayed in Viewer @@ -183,17 +192,17 @@ # Return immediately if highlight don't change if self.Highlight == highlight: return False - + self.Highlight = highlight return True - + def GetButtons(self): """ Return list of buttons defined in Viewer @return: List of buttons """ return self.Buttons - + def IsOverButton(self, x, y): """ Return if point is over one button of Viewer @@ -205,7 +214,7 @@ if button.HitTest(x, y): return button return None - + def HandleButton(self, x, y): """ Search for the button under point and if found execute associated @@ -217,10 +226,10 @@ button = self.IsOverButton(x, y) if button is None: return False - + button.ProcessCallback() return True - + def ShowButtons(self, show): """ Set display state of buttons in Viewer @@ -229,35 +238,35 @@ # Change display of every buttons for button in self.Buttons: button.Show(show) - + # Refresh button positions self.RefreshButtonsPosition() self.RefreshViewer() - + def RefreshButtonsPosition(self): """ Function that refresh buttons position in Viewer """ # Get Viewer size - width, height = self.GetSize() - + width, _height = self.GetSize() + # Buttons are align right so we calculate buttons positions in # reverse order buttons = self.Buttons[:] buttons.reverse() - + # Position offset on x coordinate x_offset = 0 for button in buttons: - # Buttons are stacked right, removing those that are not active + # Buttons are stacked right, removing those that are not active if button.IsEnabled(): # Update button position according to button width and offset # on x coordinate - w, h = button.GetSize() + w, _h = button.GetSize() button.SetPosition(width - 5 - w - x_offset, 5) # Update offset on x coordinate x_offset += w + 2 - + def DrawCommonElements(self, dc, buttons=None): """ Function that draw common graphics for every Viewers @@ -268,26 +277,26 @@ """ # Get Viewer size width, height = self.GetSize() - + # Set dc styling for drop before or drop after highlight - dc.SetPen(HIGHLIGHT_DROP_PEN) - dc.SetBrush(HIGHLIGHT_DROP_BRUSH) - + dc.SetPen(HIGHLIGHT['DROP_PEN']) + dc.SetBrush(HIGHLIGHT['DROP_BRUSH']) + # Draw line at upper side of Viewer if highlight is drop before if self.Highlight == HIGHLIGHT_BEFORE: dc.DrawLine(0, 1, width - 1, 1) - + # Draw line at lower side of Viewer if highlight is drop before elif self.Highlight == HIGHLIGHT_AFTER: dc.DrawLine(0, height - 1, width - 1, height - 1) - + # If no specific buttons are defined, get default buttons if buttons is None: buttons = self.Buttons # Draw buttons for button in buttons: button.Draw(dc) - + # If graph dragging is processing if self.ParentWindow.IsDragging(): destBBox = self.ParentWindow.GetDraggingAxesClippingRegion(self) @@ -295,55 +304,55 @@ if destBBox.width > 0 and destBBox.height > 0: srcPanel = self.ParentWindow.DraggingAxesPanel srcBBox = srcPanel.GetAxesBoundingBox() - + srcX = srcBBox.x - (srcPos.x if destBBox.x == 0 else 0) srcY = srcBBox.y - (srcPos.y if destBBox.y == 0 else 0) - + srcBmp = _convert_agg_to_wx_bitmap( - srcPanel.get_renderer(), None) + srcPanel.get_renderer(), None) srcDC = wx.MemoryDC() srcDC.SelectObject(srcBmp) - - dc.Blit(destBBox.x, destBBox.y, - int(destBBox.width), int(destBBox.height), + + dc.Blit(destBBox.x, destBBox.y, + int(destBBox.width), int(destBBox.height), srcDC, srcX, srcY) - + def OnEnter(self, event): """ Function called when entering Viewer - @param event: wx.MouseEvent + @param event: wx.MouseEvent """ # Display buttons self.ShowButtons(True) event.Skip() - + def OnLeave(self, event): """ Function called when leaving Viewer - @param event: wx.MouseEvent + @param event: wx.MouseEvent """ # Hide buttons self.ShowButtons(False) event.Skip() - + def OnCloseButton(self): """ Function called when Close button is pressed """ wx.CallAfter(self.ParentWindow.DeleteValue, self) - + def OnForceButton(self): """ Function called when Force button is pressed """ self.ForceValue(self.ItemsDict.values()[0]) - + def OnReleaseButton(self): """ Function called when Release button is pressed """ self.ReleaseValue(self.ItemsDict.values()[0]) - + def OnMouseDragging(self, x, y): """ Function called when mouse is dragged over Viewer @@ -354,7 +363,7 @@ # Refresh highlight in Debug Variable Panel (highlight can be displayed # in another Viewer self.ParentWindow.RefreshHighlight(x + xw, y + yw) - + def RefreshHighlight(self, x, y): """ Function called by Debug Variable Panel asking Viewer to refresh @@ -363,23 +372,23 @@ @param y: Y coordinate of mouse pointer """ # Get Viewer size - width, height = self.GetSize() - + _width, height = self.GetSize() + # Mouse is in the first half of Viewer if y < height / 2: # If Viewer is the upper one, draw drop before highlight if self.ParentWindow.IsViewerFirst(self): self.SetHighlight(HIGHLIGHT_BEFORE) - + # Else draw drop after highlight in previous Viewer else: self.SetHighlight(HIGHLIGHT_NONE) self.ParentWindow.HighlightPreviousViewer(self) - - # Mouse is in the second half of Viewer, draw drop after highlight + + # Mouse is in the second half of Viewer, draw drop after highlight else: self.SetHighlight(HIGHLIGHT_AFTER) - + def OnEraseBackground(self, event): """ Function called when Viewer background is going to be erase @@ -387,7 +396,7 @@ """ # Prevent flicker on Windows pass - + def OnResize(self, event): """ Function called when Viewer size changed @@ -397,7 +406,7 @@ self.RefreshButtonsPosition() self.ParentWindow.ForceRefresh() event.Skip() - + def ForceValue(self, item): """ Force value of item given @@ -409,17 +418,17 @@ # Return immediately if not found if iec_type is None: return - + # Open a dialog to enter varaible forced value dialog = ForceVariableDialog(self, iec_type, str(item.GetValue())) if dialog.ShowModal() == wx.ID_OK: self.ParentWindow.ForceDataValue(iec_path.upper(), dialog.GetValue()) - + def ReleaseValue(self, item): """ Release value of item given @param item: Item to release value """ self.ParentWindow.ReleaseDataValue( - item.GetVariable().upper()) + item.GetVariable().upper()) diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/GraphButton.py --- a/controls/DebugVariablePanel/GraphButton.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/GraphButton.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,20 +22,22 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from util.BitmapLibrary import GetBitmap -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Custom button for Graphic Viewer Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a custom button for graphic Viewer -""" -class GraphButton(): - +class GraphButton(object): + """ + Class that implements a custom button for graphic Viewer + """ + def __init__(self, x, y, bitmap, callback): """ Constructor @@ -48,21 +50,21 @@ self.SetPosition(x, y) # Set button bitmap self.SetBitmap(bitmap) - + # By default button is hide and enabled self.Shown = False self.Enabled = True - + # Save reference to callback function self.Callback = callback - + def __del__(self): """ Destructor """ # Remove reference to callback function self.callback = None - + def SetBitmap(self, bitmap): """ Set bitmap to use for button @@ -70,7 +72,7 @@ """ # Get wx.Bitmap object corresponding to bitmap self.Bitmap = GetBitmap(bitmap) - + def GetSize(self): """ Return size of button @@ -78,7 +80,7 @@ """ # Button size is size of bitmap return self.Bitmap.GetSize() - + def SetPosition(self, x, y): """ Set button position @@ -86,7 +88,7 @@ @param y: Y coordinate of Button in Graphic Viewer """ self.Position = wx.Point(x, y) - + def Show(self, show=True): """ Mark if button to be displayed in Graphic Viewer @@ -94,20 +96,20 @@ (default True) """ self.Shown = show - + def Hide(self): """ Hide button from Graphic Viewer """ self.Show(False) - + def IsShown(self): """ Return if button is displayed in Graphic Viewer @return: True if button is displayed in Graphic Viewer """ return self.Shown - + def Enable(self, enable=True): """ Mark if button is active in Graphic Viewer @@ -115,44 +117,44 @@ (default True) """ self.Enabled = enable - + def Disable(self): """ Deactivate button in Graphic Viewer """ self.Enabled = False - + def IsEnabled(self): """ Return if button is active in Graphic Viewer @return: True if button is active in Graphic Viewer """ return self.Enabled - + def HitTest(self, x, y): """ Test if point is inside button @param x: X coordinate of point @param y: Y coordinate of point @return: True if button is active and displayed and point is inside - button + button """ # Return immediately if button is hidden or inactive if not (self.IsShown() and self.IsEnabled()): return False - + # Test if point is inside button w, h = self.Bitmap.GetSize() rect = wx.Rect(self.Position.x, self.Position.y, w, h) return rect.InsideXY(x, y) - + def ProcessCallback(self): """ Call callback function if defined """ if self.Callback is not None: self.Callback() - + def Draw(self, dc): """ Draw button in Graphic Viewer diff -r c1298e7ffe3a -r 8391c11477f4 controls/DebugVariablePanel/__init__.py --- a/controls/DebugVariablePanel/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DebugVariablePanel/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,5 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -from DebugVariablePanel import DebugVariablePanel +from __future__ import absolute_import +from controls.DebugVariablePanel.DebugVariablePanel import DebugVariablePanel diff -r c1298e7ffe3a -r 8391c11477f4 controls/DurationCellEditor.py --- a/controls/DurationCellEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/DurationCellEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,44 +22,47 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from dialogs.DurationEditorDialog import DurationEditorDialog + class DurationCellControl(wx.PyControl): - + ''' Custom cell editor control with a text box and a button that launches the DurationEditorDialog. ''' def __init__(self, parent): - wx.Control.__init__(self, parent) - + wx.PyControl.__init__(self, parent) + main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + # create location text control - self.Duration = wx.TextCtrl(self, size=wx.Size(0, -1), - style=wx.TE_PROCESS_ENTER) + self.Duration = wx.TextCtrl(self, size=wx.Size(0, -1), + style=wx.TE_PROCESS_ENTER) self.Duration.Bind(wx.EVT_KEY_DOWN, self.OnDurationChar) main_sizer.AddWindow(self.Duration, flag=wx.GROW) - + # create browse button self.EditButton = wx.Button(self, label='...', size=wx.Size(30, -1)) self.Bind(wx.EVT_BUTTON, self.OnEditButtonClick, self.EditButton) main_sizer.AddWindow(self.EditButton, flag=wx.GROW) - + self.Bind(wx.EVT_SIZE, self.OnSize) - + self.SetSizer(main_sizer) - + self.Default = None - + def SetValue(self, value): self.Default = value self.Duration.SetValue(value) - + def GetValue(self): return self.Duration.GetValue() @@ -90,23 +93,24 @@ def SetInsertionPoint(self, i): self.Duration.SetInsertionPoint(i) - + def SetFocus(self): self.Duration.SetFocus() + class DurationCellEditor(wx.grid.PyGridCellEditor): ''' Grid cell editor that uses DurationCellControl to display an edit button. ''' def __init__(self, table, colname): wx.grid.PyGridCellEditor.__init__(self) - + self.Table = table self.Colname = colname - + def __del__(self): self.CellControl = None - + def Create(self, parent, id, evt_handler): self.CellControl = DurationCellControl(parent) self.SetControl(self.CellControl) @@ -131,13 +135,13 @@ return self.EndEditInternal(row, col, grid, oldval) else: def EndEdit(self, row, col, grid): - oldval = self.Table.GetValueByName(row, self.Colname) - return self.EndEditInternal(row, col, grid, oldval) + oldval = self.Table.GetValueByName(row, self.Colname) + return self.EndEditInternal(row, col, grid, oldval) def SetSize(self, rect): self.CellControl.SetDimensions(rect.x + 1, rect.y, - rect.width, rect.height, - wx.SIZE_ALLOW_MINUS_ONE) + rect.width, rect.height, + wx.SIZE_ALLOW_MINUS_ONE) def Clone(self): - return DurationCellEditor(self.Table) + return DurationCellEditor(self.Table, self.Colname) diff -r c1298e7ffe3a -r 8391c11477f4 controls/EnhancedStatusBar.py --- a/controls/EnhancedStatusBar.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/EnhancedStatusBar.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,248 +1,246 @@ -# --------------------------------------------------------------------------- # -# ENHANCEDSTATUSBAR wxPython IMPLEMENTATION -# Python Code By: -# -# Andrea Gavana, @ 31 May 2005 -# Nitro, @ 21 September 2005 -# Latest Revision: 21 September 2005, 19.57.20 GMT+2 -# Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 -# Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET -# -# -# TODO List/Caveats -# -# 1. Some Requests/Features To Add? -# -# -# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please -# Write To Me At: -# -# -# andrea.gavana@agip.it -# andrea_gavan@tin.it -# -# Or, Obviously, To The wxPython Mailing List!!! -# -# -# licensed under wxWidgets License (GPL compatible) -# End Of Comments -# --------------------------------------------------------------------------- # - -""" Description: - -EnhancedStatusBar Is A Slight Modification (Actually A Subclassing) Of wx.StatusBar. -It Allows You To Add Almost Any Widget You Like To The wx.StatusBar Of Your Main -Frame Application And Also To Layout Them Properly. - - -What It Can Do: - -1) Almost All The Functionalities Of wx.StatusBar Are Still Present; -2) You Can Add Different Kind Of Widgets Into Every Field Of The EnhancedStatusBar; -3) The AddWidget() Methods Accepts 2 Layout Inputs: - - horizontalalignment: This Specify The Horizontal Alignment For Your Widget, - And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_HORIZONTAL, ESB_ALIGN_LEFT And - ESB_ALIGN_RIGHT; - - varticalalignment: This Specify The Vertical Alignment For Your Widget, - And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_VERTICAL, ESB_ALIGN_BOTTOM And - ESB_ALIGN_LEFT; - -EnhancedStatusBar Is Freeware And Distributed Under The wxPython License. - -Latest Revision: 21 September 2005, 19.57.20 GMT+2 -Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 -Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET - -""" - -import wx - -# Horizontal Alignment Constants -ESB_ALIGN_CENTER_VERTICAL = 1 -ESB_ALIGN_TOP = 2 -ESB_ALIGN_BOTTOM = 3 - -# Vertical Alignment Constants -ESB_ALIGN_CENTER_HORIZONTAL = 11 -ESB_ALIGN_LEFT = 12 -ESB_ALIGN_RIGHT = 13 - -# Exact Fit (Either Horizontal Or Vertical Or Both) Constant -ESB_EXACT_FIT = 20 - - -# --------------------------------------------------------------- -# Class EnhancedStatusBar -# --------------------------------------------------------------- -# This Is The Main Class Implementation. See The Demo For Details -# --------------------------------------------------------------- -class EnhancedStatusBarItem(object): - def __init__(self, widget, pos, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, verticalalignment=ESB_ALIGN_CENTER_VERTICAL): - self.__dict__.update( locals() ) - -class EnhancedStatusBar(wx.StatusBar): - - def __init__(self, parent, id=wx.ID_ANY, style=wx.ST_SIZEGRIP, - name="EnhancedStatusBar"): - """Default Class Constructor. - - EnhancedStatusBar.__init__(self, parent, id=wx.ID_ANY, - style=wx.ST_SIZEGRIP, - name="EnhancedStatusBar") - """ - - wx.StatusBar.__init__(self, parent, id, style, name) - - self._items = {} - self._curPos = 0 - self._parent = parent - - wx.EVT_SIZE(self, self.OnSize) - wx.CallAfter(self.OnSize, None) - - - def OnSize(self, event): - """Handles The wx.EVT_SIZE Events For The StatusBar. - - Actually, All The Calculations Linked To HorizontalAlignment And - VerticalAlignment Are Done In This Function.""" - - for pos, item in self._items.items(): - widget, horizontalalignment, verticalalignment = item.widget, item.horizontalalignment, item.verticalalignment - - rect = self.GetFieldRect(pos) - widgetpos = widget.GetPosition() - widgetsize = widget.GetSize() - - rect = self.GetFieldRect(pos) - - if horizontalalignment == ESB_EXACT_FIT: - - if verticalalignment == ESB_EXACT_FIT: - """ 1 September 2015 Fix fit align """ - widget.SetSize((rect.width-4, rect.height-4)) - widget.SetPosition((rect.x+2, rect.y+2)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.width - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.y+diffs)) - else: - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.y-1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetSize((rect.width-2, widgetsize[1])) - widget.SetPosition((rect.x-1, rect.height-widgetsize[1])) - - elif horizontalalignment == ESB_ALIGN_LEFT: - - xpos = rect.x - 1 - if verticalalignment == ESB_EXACT_FIT: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.height - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetPosition((xpos, rect.y+diffs)) - else: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetPosition((xpos, rect.height-widgetsize[1])) - - elif horizontalalignment == ESB_ALIGN_RIGHT: - - xpos = rect.x + rect.width - widgetsize[0] - 1 - if verticalalignment == ESB_EXACT_FIT: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.height - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetPosition((xpos, rect.y+diffs)) - else: - widget.SetSize((widgetsize[0], rect.height-2)) - widget.SetPosition((xpos, rect.y-1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetPosition((xpos, rect.height-widgetsize[1])) - - elif horizontalalignment == ESB_ALIGN_CENTER_HORIZONTAL: - - xpos = rect.x + (rect.width - widgetsize[0])/2 - 1 - if verticalalignment == ESB_EXACT_FIT: - widget.SetSize((widgetsize[0], rect.height)) - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: - if widgetsize[1] < rect.height - 1: - diffs = (rect.height - widgetsize[1])/2 - widget.SetPosition((xpos, rect.y+diffs)) - else: - widget.SetSize((widgetsize[0], rect.height-1)) - widget.SetPosition((xpos, rect.y+1)) - elif verticalalignment == ESB_ALIGN_TOP: - widget.SetPosition((xpos, rect.y)) - elif verticalalignment == ESB_ALIGN_BOTTOM: - widget.SetPosition((xpos, rect.height-widgetsize[1])) - - - if event is not None: - event.Skip() - - - def AddWidget(self, widget, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, - verticalalignment=ESB_ALIGN_CENTER_VERTICAL, pos = -1): - """Add A Widget To The EnhancedStatusBar. - - Parameters: - - - horizontalalignment: This Can Be One Of: - a) ESB_EXACT_FIT: The Widget Will Fit Horizontally The StatusBar Field Width; - b) ESB_ALIGN_CENTER_HORIZONTAL: The Widget Will Be Centered Horizontally In - The StatusBar Field; - c) ESB_ALIGN_LEFT: The Widget Will Be Left Aligned In The StatusBar Field; - d) ESB_ALIGN_RIGHT: The Widget Will Be Right Aligned In The StatusBar Field; - - - verticalalignment: - a) ESB_EXACT_FIT: The Widget Will Fit Vertically The StatusBar Field Height; - b) ESB_ALIGN_CENTER_VERTICAL: The Widget Will Be Centered Vertically In - The StatusBar Field; - c) ESB_ALIGN_BOTTOM: The Widget Will Be Bottom Aligned In The StatusBar Field; - d) ESB_ALIGN_TOP: The Widget Will Be TOP Aligned In The StatusBar Field; - - """ - - if pos == -1: - pos = self._curPos - self._curPos += 1 - - if self.GetFieldsCount() <= pos: - raise "\nERROR: EnhancedStatusBar has a max of %d items, you tried to set item #%d" % (self.GetFieldsCount(), pos) - - if horizontalalignment not in [ESB_ALIGN_CENTER_HORIZONTAL, ESB_EXACT_FIT, - ESB_ALIGN_LEFT, ESB_ALIGN_RIGHT]: - raise '\nERROR: Parameter "horizontalalignment" Should Be One Of '\ - '"ESB_ALIGN_CENTER_HORIZONTAL", "ESB_ALIGN_LEFT", "ESB_ALIGN_RIGHT"' \ - '"ESB_EXACT_FIT"' - - if verticalalignment not in [ESB_ALIGN_CENTER_VERTICAL, ESB_EXACT_FIT, - ESB_ALIGN_TOP, ESB_ALIGN_BOTTOM]: - raise '\nERROR: Parameter "verticalalignment" Should Be One Of '\ - '"ESB_ALIGN_CENTER_VERTICAL", "ESB_ALIGN_TOP", "ESB_ALIGN_BOTTOM"' \ - '"ESB_EXACT_FIT"' - - - try: - self.RemoveChild(self._items[pos].widget) - self._items[pos].widget.Destroy() - except KeyError: pass - - self._items[pos] = EnhancedStatusBarItem(widget, pos, horizontalalignment, verticalalignment) - - wx.CallAfter(self.OnSize, None) +# --------------------------------------------------------------------------- # +# ENHANCEDSTATUSBAR wxPython IMPLEMENTATION +# Python Code By: +# +# Andrea Gavana, @ 31 May 2005 +# Nitro, @ 21 September 2005 +# Latest Revision: 21 September 2005, 19.57.20 GMT+2 +# Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 +# Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET +# +# +# TODO List/Caveats +# +# 1. Some Requests/Features To Add? +# +# +# For All Kind Of Problems, Requests Of Enhancements And Bug Reports, Please +# Write To Me At: +# +# +# andrea.gavana@agip.it +# andrea_gavan@tin.it +# +# Or, Obviously, To The wxPython Mailing List!!! +# +# +# licensed under wxWidgets License (GPL compatible) +# End Of Comments +# --------------------------------------------------------------------------- # + +""" Description: + +EnhancedStatusBar Is A Slight Modification (Actually A Subclassing) Of wx.StatusBar. +It Allows You To Add Almost Any Widget You Like To The wx.StatusBar Of Your Main +Frame Application And Also To Layout Them Properly. + + +What It Can Do: + +1) Almost All The Functionalities Of wx.StatusBar Are Still Present; +2) You Can Add Different Kind Of Widgets Into Every Field Of The EnhancedStatusBar; +3) The AddWidget() Methods Accepts 2 Layout Inputs: + - horizontalalignment: This Specify The Horizontal Alignment For Your Widget, + And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_HORIZONTAL, ESB_ALIGN_LEFT And + ESB_ALIGN_RIGHT; + - varticalalignment: This Specify The Vertical Alignment For Your Widget, + And Can Be ESB_EXACT_FIT, ESB_ALIGN_CENTER_VERTICAL, ESB_ALIGN_BOTTOM And + ESB_ALIGN_LEFT; + +EnhancedStatusBar Is Freeware And Distributed Under The wxPython License. + +Latest Revision: 21 September 2005, 19.57.20 GMT+2 +Latest Revision before Latest Revision: 21 September 2005, 18.29.35 GMT+2 +Latest Revision before Latest Revision before Latest Revision: 31 May 2005, 23.17 CET + +""" + +from __future__ import absolute_import +import wx + +# Horizontal Alignment Constants +ESB_ALIGN_CENTER_VERTICAL = 1 +ESB_ALIGN_TOP = 2 +ESB_ALIGN_BOTTOM = 3 + +# Vertical Alignment Constants +ESB_ALIGN_CENTER_HORIZONTAL = 11 +ESB_ALIGN_LEFT = 12 +ESB_ALIGN_RIGHT = 13 + +# Exact Fit (Either Horizontal Or Vertical Or Both) Constant +ESB_EXACT_FIT = 20 + + +# --------------------------------------------------------------- +# Class EnhancedStatusBar +# --------------------------------------------------------------- +# This Is The Main Class Implementation. See The Demo For Details +# --------------------------------------------------------------- +class EnhancedStatusBarItem(object): + def __init__(self, widget, pos, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, verticalalignment=ESB_ALIGN_CENTER_VERTICAL): + self.__dict__.update(locals()) + + +class EnhancedStatusBar(wx.StatusBar): + + def __init__(self, parent, id=wx.ID_ANY, style=wx.ST_SIZEGRIP, + name="EnhancedStatusBar"): + """Default Class Constructor. + + EnhancedStatusBar.__init__(self, parent, id=wx.ID_ANY, + style=wx.ST_SIZEGRIP, + name="EnhancedStatusBar") + """ + + wx.StatusBar.__init__(self, parent, id, style, name) + + self._items = {} + self._curPos = 0 + self._parent = parent + + wx.EVT_SIZE(self, self.OnSize) + wx.CallAfter(self.OnSize, None) + + def OnSize(self, event): + """Handles The wx.EVT_SIZE Events For The StatusBar. + + Actually, All The Calculations Linked To HorizontalAlignment And + VerticalAlignment Are Done In This Function.""" + + for pos, item in self._items.items(): + widget, horizontalalignment, verticalalignment = item.widget, item.horizontalalignment, item.verticalalignment + + rect = self.GetFieldRect(pos) + widgetsize = widget.GetSize() + + rect = self.GetFieldRect(pos) + + if horizontalalignment == ESB_EXACT_FIT: + + if verticalalignment == ESB_EXACT_FIT: + # 1 September 2015 Fix fit align + widget.SetSize((rect.width-4, rect.height-4)) + widget.SetPosition((rect.x+2, rect.y+2)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.width - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.y+diffs)) + else: + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.y-1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetSize((rect.width-2, widgetsize[1])) + widget.SetPosition((rect.x-1, rect.height-widgetsize[1])) + + elif horizontalalignment == ESB_ALIGN_LEFT: + + xpos = rect.x - 1 + if verticalalignment == ESB_EXACT_FIT: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.height - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetPosition((xpos, rect.y+diffs)) + else: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetPosition((xpos, rect.height-widgetsize[1])) + + elif horizontalalignment == ESB_ALIGN_RIGHT: + + xpos = rect.x + rect.width - widgetsize[0] - 1 + if verticalalignment == ESB_EXACT_FIT: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.height - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetPosition((xpos, rect.y+diffs)) + else: + widget.SetSize((widgetsize[0], rect.height-2)) + widget.SetPosition((xpos, rect.y-1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetPosition((xpos, rect.height-widgetsize[1])) + + elif horizontalalignment == ESB_ALIGN_CENTER_HORIZONTAL: + + xpos = rect.x + (rect.width - widgetsize[0])/2 - 1 + if verticalalignment == ESB_EXACT_FIT: + widget.SetSize((widgetsize[0], rect.height)) + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_CENTER_VERTICAL: + if widgetsize[1] < rect.height - 1: + diffs = (rect.height - widgetsize[1])/2 + widget.SetPosition((xpos, rect.y+diffs)) + else: + widget.SetSize((widgetsize[0], rect.height-1)) + widget.SetPosition((xpos, rect.y+1)) + elif verticalalignment == ESB_ALIGN_TOP: + widget.SetPosition((xpos, rect.y)) + elif verticalalignment == ESB_ALIGN_BOTTOM: + widget.SetPosition((xpos, rect.height-widgetsize[1])) + + if event is not None: + event.Skip() + + def AddWidget(self, widget, horizontalalignment=ESB_ALIGN_CENTER_HORIZONTAL, + verticalalignment=ESB_ALIGN_CENTER_VERTICAL, pos=-1): + """Add A Widget To The EnhancedStatusBar. + + Parameters: + + - horizontalalignment: This Can Be One Of: + a) ESB_EXACT_FIT: The Widget Will Fit Horizontally The StatusBar Field Width; + b) ESB_ALIGN_CENTER_HORIZONTAL: The Widget Will Be Centered Horizontally In + The StatusBar Field; + c) ESB_ALIGN_LEFT: The Widget Will Be Left Aligned In The StatusBar Field; + d) ESB_ALIGN_RIGHT: The Widget Will Be Right Aligned In The StatusBar Field; + + - verticalalignment: + a) ESB_EXACT_FIT: The Widget Will Fit Vertically The StatusBar Field Height; + b) ESB_ALIGN_CENTER_VERTICAL: The Widget Will Be Centered Vertically In + The StatusBar Field; + c) ESB_ALIGN_BOTTOM: The Widget Will Be Bottom Aligned In The StatusBar Field; + d) ESB_ALIGN_TOP: The Widget Will Be TOP Aligned In The StatusBar Field; + + """ + + if pos == -1: + pos = self._curPos + self._curPos += 1 + + if self.GetFieldsCount() <= pos: + raise "\nERROR: EnhancedStatusBar has a max of %d items, you tried to set item #%d" % (self.GetFieldsCount(), pos) + + if horizontalalignment not in [ESB_ALIGN_CENTER_HORIZONTAL, ESB_EXACT_FIT, + ESB_ALIGN_LEFT, ESB_ALIGN_RIGHT]: + raise '\nERROR: Parameter "horizontalalignment" Should Be One Of '\ + '"ESB_ALIGN_CENTER_HORIZONTAL", "ESB_ALIGN_LEFT", "ESB_ALIGN_RIGHT"' \ + '"ESB_EXACT_FIT"' + + if verticalalignment not in [ESB_ALIGN_CENTER_VERTICAL, ESB_EXACT_FIT, + ESB_ALIGN_TOP, ESB_ALIGN_BOTTOM]: + raise '\nERROR: Parameter "verticalalignment" Should Be One Of '\ + '"ESB_ALIGN_CENTER_VERTICAL", "ESB_ALIGN_TOP", "ESB_ALIGN_BOTTOM"' \ + '"ESB_EXACT_FIT"' + + try: + self.RemoveChild(self._items[pos].widget) + self._items[pos].widget.Destroy() + except KeyError: + pass + + self._items[pos] = EnhancedStatusBarItem(widget, pos, horizontalalignment, verticalalignment) + + wx.CallAfter(self.OnSize, None) diff -r c1298e7ffe3a -r 8391c11477f4 controls/FolderTree.py --- a/controls/FolderTree.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/FolderTree.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import os import wx @@ -30,6 +32,7 @@ DRIVE, FOLDER, FILE = range(3) + def sort_folder(x, y): if x[1] == y[1]: return cmp(x[0], y[0]) @@ -38,6 +41,7 @@ else: return 1 + def splitpath(path): head, tail = os.path.split(path) if head == "": @@ -46,20 +50,21 @@ return splitpath(head) return splitpath(head) + [tail] + class FolderTree(wx.Panel): - + def __init__(self, parent, folder, filter=None, editable=True): wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - + main_sizer = wx.BoxSizer(wx.VERTICAL) - - self.Tree = wx.TreeCtrl(self, - style=wx.TR_HAS_BUTTONS| - wx.TR_SINGLE| - wx.SUNKEN_BORDER| - wx.TR_HIDE_ROOT| - wx.TR_LINES_AT_ROOT| - wx.TR_EDIT_LABELS) + + self.Tree = wx.TreeCtrl(self, + style=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.SUNKEN_BORDER | + wx.TR_HIDE_ROOT | + wx.TR_LINES_AT_ROOT | + wx.TR_EDIT_LABELS)) if wx.Platform == '__WXMSW__': self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnTreeItemExpanded, self.Tree) self.Tree.Bind(wx.EVT_LEFT_DOWN, self.OnTreeLeftDown) @@ -69,19 +74,19 @@ self.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT, self.OnTreeBeginLabelEdit, self.Tree) self.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.OnTreeEndLabelEdit, self.Tree) main_sizer.AddWindow(self.Tree, 1, flag=wx.GROW) - + if filter is not None: self.Filter = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnFilterChanged, self.Filter) main_sizer.AddWindow(self.Filter, flag=wx.GROW) else: self.Filter = None - + self.SetSizer(main_sizer) - + self.Folder = folder self.Editable = editable - + self.TreeImageList = wx.ImageList(16, 16) self.TreeImageDict = {} for item_type, bitmap in [(DRIVE, "tree_drive"), @@ -89,7 +94,7 @@ (FILE, "tree_file")]: self.TreeImageDict[item_type] = self.TreeImageList.Add(GetBitmap(bitmap)) self.Tree.SetImageList(self.TreeImageList) - + self.Filters = {} if self.Filter is not None: filter_parts = filter.split("|") @@ -101,11 +106,11 @@ self.Filter.Append(filter_parts[idx]) if idx == 0: self.Filter.SetStringSelection(filter_parts[idx]) - + self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()] else: self.CurrentFilter = "" - + def _GetFolderChildren(self, folderpath, recursive=True): items = [] if wx.Platform == '__WXMSW__' and folderpath == "/": @@ -116,7 +121,7 @@ else: try: files = os.listdir(folderpath) - except: + except Exception: return [] for filename in files: if not filename.startswith("."): @@ -127,25 +132,25 @@ else: children = 0 items.append((filename, FOLDER, children)) - elif (self.CurrentFilter == "" or + elif (self.CurrentFilter == "" or os.path.splitext(filename)[1] == self.CurrentFilter): items.append((filename, FILE, None)) if recursive: items.sort(sort_folder) return items - + def SetFilter(self, filter): self.CurrentFilter = filter - + def GetTreeCtrl(self): return self.Tree - + def RefreshTree(self): root = self.Tree.GetRootItem() if not root.IsOk(): root = self.Tree.AddRoot("") self.GenerateTreeBranch(root, self.Folder) - + def GenerateTreeBranch(self, root, folderpath): item, item_cookie = self.Tree.GetFirstChild(root) for idx, (filename, item_type, children) in enumerate(self._GetFolderChildren(folderpath)): @@ -172,18 +177,18 @@ def ExpandItem(self, item): self.GenerateTreeBranch(item, self.GetPath(item)) self.Tree.Expand(item) - + def OnTreeItemActivated(self, event): self.ExpandItem(event.GetItem()) event.Skip() - + def OnTreeLeftDown(self, event): item, flags = self.Tree.HitTest(event.GetPosition()) if flags & wx.TREE_HITTEST_ONITEMBUTTON and not self.Tree.IsExpanded(item): self.ExpandItem(item) else: event.Skip() - + def OnTreeItemExpanded(self, event): item = event.GetItem() self.GenerateTreeBranch(item, self.GetPath(item)) @@ -201,7 +206,7 @@ event.Skip() else: event.Veto() - + def OnTreeEndLabelEdit(self, event): new_name = event.GetLabel() if new_name != "": @@ -212,20 +217,20 @@ os.rename(old_filepath, new_filepath) event.Skip() else: - message = wx.MessageDialog(self, - _("File '%s' already exists!") % new_name, - _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, + _("File '%s' already exists!") % new_name, + _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() event.Veto() else: event.Skip() - + def OnFilterChanged(self, event): self.CurrentFilter = self.Filters[self.Filter.GetStringSelection()] self.RefreshTree() event.Skip() - + def _SelectItem(self, root, parts): if len(parts) == 0: self.Tree.SelectItem(root) @@ -233,22 +238,22 @@ item, item_cookie = self.Tree.GetFirstChild(root) while item.IsOk(): if self.Tree.GetItemText(item) == parts[0]: - if (self.Tree.ItemHasChildren(item) and - not self.Tree.IsExpanded(item)): + if self.Tree.ItemHasChildren(item) and \ + not self.Tree.IsExpanded(item): self.Tree.Expand(item) wx.CallAfter(self._SelectItem, item, parts[1:]) else: self._SelectItem(item, parts[1:]) return item, item_cookie = self.Tree.GetNextChild(root, item_cookie) - + def SetPath(self, path): if path.startswith(self.Folder): root = self.Tree.GetRootItem() if root.IsOk(): relative_path = path.replace(os.path.join(self.Folder, ""), "") self._SelectItem(root, splitpath(relative_path)) - + def GetPath(self, item=None): if item is None: item = self.Tree.GetSelection() diff -r c1298e7ffe3a -r 8391c11477f4 controls/LibraryPanel.py --- a/controls/LibraryPanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/LibraryPanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,27 +22,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + [CATEGORY, BLOCK] = range(2) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Library Panel -#------------------------------------------------------------------------------- - -""" -Class that implements a panel displaying a tree containing an hierarchical list -of functions and function blocks available in project an a search control for -quickly find one functions or function blocks in this list and a text control -displaying informations about selected functions or function blocks -""" +# ------------------------------------------------------------------------------- + class LibraryPanel(wx.Panel): - + """ + Class that implements a panel displaying a tree containing an hierarchical list + of functions and function blocks available in project an a search control for + quickly find one functions or function blocks in this list and a text control + displaying informations about selected functions or function blocks + """ + def __init__(self, parent, enable_drag=False): """ Constructor @@ -51,19 +54,19 @@ be drag'n drop from LibraryPanel (default: False) """ wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - + # Define LibraryPanel main sizer main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + # Add SearchCtrl to main sizer self.SearchCtrl = wx.SearchCtrl(self) # Add a button with a magnifying glass, essentially to show that this # control is for searching in tree self.SearchCtrl.ShowSearchButton(True) self.Bind(wx.EVT_TEXT, self.OnSearchCtrlChanged, self.SearchCtrl) - self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, + self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnSearchButtonClick, self.SearchCtrl) # Bind keyboard event on SearchCtrl text control to catch UP and DOWN # for search previous and next occurrence @@ -74,56 +77,56 @@ search_textctrl.Bind(wx.EVT_CHAR, self.OnKeyDown) main_sizer.AddWindow(self.SearchCtrl, flag=wx.GROW) - + # Add Splitter window for tree and block comment to main sizer splitter_window = wx.SplitterWindow(self) splitter_window.SetSashGravity(1.0) main_sizer.AddWindow(splitter_window, flag=wx.GROW) - + # Add TreeCtrl for functions and function blocks library in splitter # window self.Tree = wx.TreeCtrl(splitter_window, - size=wx.Size(0, 0), - style=wx.TR_HAS_BUTTONS| - wx.TR_SINGLE| - wx.SUNKEN_BORDER| - wx.TR_HIDE_ROOT| - wx.TR_LINES_AT_ROOT) + size=wx.Size(0, 0), + style=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.SUNKEN_BORDER | + wx.TR_HIDE_ROOT | + wx.TR_LINES_AT_ROOT)) self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemSelected, self.Tree) self.Tree.Bind(wx.EVT_CHAR, self.OnKeyDown) # If drag'n drop is enabled, bind event generated when a drag begins on # tree to start a drag'n drop if enable_drag: self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, self.Tree) - + # Add TextCtrl for function and function block informations - self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), - style=wx.TE_READONLY|wx.TE_MULTILINE) - + self.Comment = wx.TextCtrl(splitter_window, size=wx.Size(0, 80), + style=wx.TE_READONLY | wx.TE_MULTILINE) + splitter_window.SplitHorizontally(self.Tree, self.Comment, -80) - + self.SetSizer(main_sizer) - + # Reference to the project controller self.Controller = None - + # Variable storing functions and function blocks library to display self.BlockList = None - + def __del__(self): """ Destructor """ # Remove reference to project controller self.Controller = None - + def SetController(self, controller): """ Set reference to project controller @param controller: Reference to project controller """ self.Controller = controller - + def SetBlockList(self, blocklist): """ Set function and function block library to display in TreeCtrl @@ -133,15 +136,15 @@ self.BlockList = blocklist # Refresh TreeCtrl values self.RefreshTree() - + def SetFocus(self): """ Called to give focus to LibraryPanel - Override wx.Window SetFocus method + Override wx.Window SetFocus method """ # Give focus to SearchCtrl self.SearchCtrl.SetFocus() - + def ResetTree(self): """ Reset LibraryPanel values displayed in controls @@ -150,7 +153,7 @@ self.SearchCtrl.SetValue("") self.Tree.DeleteAllItems() self.Comment.SetValue("") - + def RefreshTree(self): """ Refresh LibraryPanel values displayed in controls @@ -160,35 +163,35 @@ if blocktypes is None and self.Controller is not None: # Get library from project controller if not defined blocktypes = self.Controller.GetBlockTypes() - + # Refresh TreeCtrl values if a library is defined if blocktypes is not None: # List that will contain tree items to be deleted when TreeCtrl # will be refreshed items_to_delete = [] - + # Get current selected item for selected it when values refreshed selected_item = self.Tree.GetSelection() selected_pydata = (self.Tree.GetPyData(selected_item) - if selected_item.IsOk() and - selected_item != self.Tree.GetRootItem() + if (selected_item.IsOk() and + selected_item != self.Tree.GetRootItem()) else None) # Don't save selected item if it is a category selected_infos = ((self.Tree.GetItemText(selected_item), selected_pydata["inputs"]) - if selected_pydata is not None and - selected_pydata["type"] == BLOCK - else (None, None)) - + if (selected_pydata is not None and + selected_pydata["type"] == BLOCK) + else (None, None)) + # Get TreeCtrl root item (hidden) root = self.Tree.GetRootItem() if not root.IsOk(): # Create root if not present root = self.Tree.AddRoot("") - + # Iterate over functions and function blocks library categories and # add a tree item to root item for each of them - + # Get first child under root item category_item, root_cookie = self.Tree.GetFirstChild(root) for category in blocktypes: @@ -196,11 +199,11 @@ # extracting translated strings for gettext to consider "name" # to be translated category_name = category["name"] - + # Tree item already exists, set item label if category_item.IsOk(): self.Tree.SetItemText(category_item, _(category_name)) - + # Tree item doesn't exist, add new one to root else: category_item = self.Tree.AppendItem(root, _(category_name)) @@ -209,83 +212,83 @@ if wx.Platform != '__WXMSW__': category_item, root_cookie = \ self.Tree.GetNextChild(root, root_cookie) - - # Set data associated to tree item (only save that item is a + + # Set data associated to tree item (only save that item is a # category) - self.Tree.SetPyData(category_item, {"type" : CATEGORY}) - + self.Tree.SetPyData(category_item, {"type": CATEGORY}) + # Iterate over functions and function blocks defined in library - # category add a tree item to category tree item for each of + # category add a tree item to category tree item for each of # them - + # Get first child under category tree item blocktype_item, category_cookie = \ self.Tree.GetFirstChild(category_item) for blocktype in category["list"]: - + # Tree item already exists, set item label if blocktype_item.IsOk(): self.Tree.SetItemText(blocktype_item, blocktype["name"]) - + # Tree item doesn't exist, add new one to category item else: blocktype_item = self.Tree.AppendItem( - category_item, blocktype["name"]) + category_item, blocktype["name"]) # See comment when adding category if wx.Platform != '__WXMSW__': blocktype_item, category_cookie = \ - self.Tree.GetNextChild(category_item, + self.Tree.GetNextChild(category_item, category_cookie) - + # Define data to associate to block tree item comment = blocktype["comment"] - block_data = {"type" : BLOCK, - "block_type" : blocktype["type"], - "inputs" : tuple([type - for name, type, modifier - in blocktype["inputs"]]), - "extension" : (len(blocktype["inputs"]) - if blocktype["extensible"] - else None), - "comment": _(comment) + - blocktype.get("usage", "")} + block_data = { + "type": BLOCK, + "block_type": blocktype["type"], + "inputs": tuple([type + for _name, type, _modifier + in blocktype["inputs"]]), + "extension": (len(blocktype["inputs"]) + if blocktype["extensible"] else None), + "comment": _(comment) + blocktype.get("usage", "") + } self.Tree.SetPyData(blocktype_item, block_data) - + # Select block tree item in tree if it corresponds to # previously selected one - if selected_infos == (blocktype["name"], + if selected_infos == (blocktype["name"], blocktype["inputs"]): self.Tree.SelectItem(blocktype_item) - + # Update TextCtrl value self.Comment.SetValue(block_data["comment"]) - + # Get next block tree item under category tree item blocktype_item, category_cookie = \ self.Tree.GetNextChild(category_item, category_cookie) - + # Add every remaining tree item under category tree item after # updating all block items to the list of items to delete while blocktype_item.IsOk(): items_to_delete.append(blocktype_item) blocktype_item, category_cookie = \ self.Tree.GetNextChild(category_item, category_cookie) - + # Get next category tree item under root item category_item, root_cookie = \ self.Tree.GetNextChild(root, root_cookie) - - # Add every remaining tree item under root item after updating all + + # Add every remaining tree item under root item after updating all # category items to the list of items to delete while category_item.IsOk(): items_to_delete.append(category_item) category_item, root_cookie = \ self.Tree.GetNextChild(root, root_cookie) - + # Remove all items in list of items to delete from TreeCtrl for item in items_to_delete: self.Tree.Delete(item) - + def GetSelectedBlock(self): """ Get selected block informations @@ -295,20 +298,20 @@ # Get selected item associated data in tree selected_item = self.Tree.GetSelection() selected_pydata = (self.Tree.GetPyData(selected_item) - if selected_item.IsOk() and - selected_item != self.Tree.GetRootItem() + if (selected_item.IsOk() and + selected_item != self.Tree.GetRootItem()) else None) - + # Return value is None if selected tree item is root or a category - return ({"type": self.Tree.GetItemText(selected_item), + return ({"type": self.Tree.GetItemText(selected_item), "inputs": selected_pydata["inputs"]} - if selected_pydata is not None and - selected_pydata["type"] == BLOCK + if (selected_pydata is not None and + selected_pydata["type"] == BLOCK) else None) - + def SelectTreeItem(self, name, inputs): """ - Select Tree item corresponding to block informations given + Select Tree item corresponding to block informations given @param name: Block type name @param inputs: List of block inputs type [input_type,...] """ @@ -318,8 +321,8 @@ # Select tree item found self.Tree.SelectItem(item) self.Tree.EnsureVisible(item) - - def FindTreeItem(self, item, name, inputs = None): + + def FindTreeItem(self, item, name, inputs=None): """ Find Tree item corresponding to block informations given Function is recursive @@ -330,12 +333,12 @@ # Return immediately if item isn't valid if not item.IsOk(): return None - + # Get data associated to item to test item_pydata = self.Tree.GetPyData(item) if item_pydata is not None and item_pydata["type"] == BLOCK: # Only test item corresponding to block - + # Test if block inputs type are the same than those given type_inputs = item_pydata.get("inputs", None) type_extension = item_pydata.get("extension", None) @@ -343,7 +346,7 @@ same_inputs = reduce( lambda x, y: x and y, map( - lambda x: x[0]==x[1] or x[0]=='ANY' or x[1]=='ANY', + lambda x: x[0] == x[1] or x[0] == 'ANY' or x[1] == 'ANY', zip(type_inputs, (inputs[:type_extension] if type_extension is not None @@ -351,11 +354,11 @@ True) else: same_inputs = True - + # Return item if block data corresponds to informations given if self.Tree.GetItemText(item) == name and same_inputs: return item - + # Test item children if item doesn't correspond child, child_cookie = self.Tree.GetFirstChild(item) while child.IsOk(): @@ -363,9 +366,9 @@ if result: return result child, child_cookie = self.Tree.GetNextChild(item, child_cookie) - + return None - + def SearchInTree(self, value, mode="first"): """ Search in Tree and select item that name contains string given @@ -378,43 +381,43 @@ root = self.Tree.GetRootItem() if not root.IsOk(): return False - + # Set function to navigate in Tree item sibling according to search - # mode defined + # mode defined sibling_function = (self.Tree.GetPrevSibling if mode == "previous" else self.Tree.GetNextSibling) - + # Get current selected item (for next and previous mode) item = self.Tree.GetSelection() if not item.IsOk() or mode == "first": - item, item_cookie = self.Tree.GetFirstChild(root) + item, _item_cookie = self.Tree.GetFirstChild(root) selected = None else: selected = item - + # Navigate through tree items until one matching found or reach tree # starting or ending while item.IsOk(): - + # Get item data to get item type item_pydata = self.Tree.GetPyData(item) - + # Item is a block category if (item == root) or item_pydata["type"] == CATEGORY: - - # Get category first or last child according to search mode + + # Get category first or last child according to search mode # defined child = (self.Tree.GetLastChild(item) if mode == "previous" else self.Tree.GetFirstChild(item)[0]) - + # If category has no child, go to sibling category item = (child if child.IsOk() else sibling_function(item)) - + # Item is a block else: - + # Extract item block name name = self.Tree.GetItemText(item) # Test if block name contains string given @@ -428,17 +431,17 @@ self.Tree.SelectItem(item) self.Tree.EnsureVisible(item) return True - + # Go to next item sibling if block not found next = sibling_function(item) - + # If category has no other child, go to next category sibling item = (next if next.IsOk() else sibling_function(self.Tree.GetItemParent(item))) - + return False - + def OnSearchCtrlChanged(self, event): """ Called when SearchCtrl text control value changed @@ -447,7 +450,7 @@ # Search for block containing SearchCtrl value in 'first' mode self.SearchInTree(self.SearchCtrl.GetValue()) event.Skip() - + def OnSearchButtonClick(self, event): """ Called when SearchCtrl search button was clicked @@ -456,7 +459,7 @@ # Search for block containing SearchCtrl value in 'next' mode self.SearchInTree(self.SearchCtrl.GetValue(), "next") event.Skip() - + def OnTreeItemSelected(self, event): """ Called when tree item is selected @@ -468,13 +471,13 @@ item_pydata["comment"] if item_pydata is not None and item_pydata["type"] == BLOCK else "") - + # Call extra function defined when tree item is selected if getattr(self, "_OnTreeItemSelected", None) is not None: self._OnTreeItemSelected(event) - + event.Skip() - + def OnTreeBeginDrag(self, event): """ Called when a drag is started in tree @@ -482,19 +485,19 @@ """ selected_item = event.GetItem() item_pydata = self.Tree.GetPyData(selected_item) - + # Item dragged is a block if item_pydata is not None and item_pydata["type"] == BLOCK: # Start a drag'n drop data = wx.TextDataObject(str( - (self.Tree.GetItemText(selected_item), - item_pydata["block_type"], - "", + (self.Tree.GetItemText(selected_item), + item_pydata["block_type"], + "", item_pydata["inputs"]))) dragSource = wx.DropSource(self.Tree) dragSource.SetData(data) dragSource.DoDragDrop() - + def OnKeyDown(self, event): """ Called when key is pressed in SearchCtrl text control @@ -503,17 +506,17 @@ # Get event keycode and value in SearchCtrl keycode = event.GetKeyCode() search_value = self.SearchCtrl.GetValue() - + # Up key was pressed and SearchCtrl isn't empty, search for block in - # 'previous' mode + # 'previous' mode if keycode == wx.WXK_UP and search_value != "": self.SearchInTree(search_value, "previous") - + # Down key was pressed and SearchCtrl isn't empty, search for block in - # 'next' mode + # 'next' mode elif keycode == wx.WXK_DOWN and search_value != "": self.SearchInTree(search_value, "next") - + # Handle key normally else: event.Skip() diff -r c1298e7ffe3a -r 8391c11477f4 controls/LocationCellEditor.py --- a/controls/LocationCellEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/LocationCellEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,36 +22,39 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from dialogs.BrowseLocationsDialog import BrowseLocationsDialog + class LocationCellControl(wx.PyControl): - + ''' Custom cell editor control with a text box and a button that launches the BrowseLocationsDialog. ''' def __init__(self, parent): - wx.Control.__init__(self, parent) - + wx.PyControl.__init__(self, parent) + main_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + # create location text control - self.Location = wx.TextCtrl(self, size=wx.Size(0, -1), - style=wx.TE_PROCESS_ENTER) + self.Location = wx.TextCtrl(self, size=wx.Size(0, -1), + style=wx.TE_PROCESS_ENTER) self.Location.Bind(wx.EVT_KEY_DOWN, self.OnLocationChar) main_sizer.AddWindow(self.Location, flag=wx.GROW) - + # create browse button self.BrowseButton = wx.Button(self, label='...', size=wx.Size(30, -1)) self.BrowseButton.Bind(wx.EVT_BUTTON, self.OnBrowseButtonClick) main_sizer.AddWindow(self.BrowseButton, flag=wx.GROW) - + self.Bind(wx.EVT_SIZE, self.OnSize) - + self.SetSizer(main_sizer) self.Controller = None @@ -73,7 +76,7 @@ def SetValue(self, value): self.Default = value self.Location.SetValue(value) - + def GetValue(self): return self.Location.GetValue() @@ -88,15 +91,17 @@ else: infos = None dialog.Destroy() - + if infos is not None: location = infos["location"] # set the location if not infos["location"].startswith("%"): - dialog = wx.SingleChoiceDialog(self, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog( + self, + _("Select a variable class:"), + _("Variable class"), + [_("Input"), _("Output"), _("Memory")], + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: selected = dialog.GetSelection() else: @@ -111,7 +116,7 @@ location = "%Q" + location else: location = "%M" + location - + self.Location.SetValue(location) self.VarType = infos["IEC_type"] @@ -129,24 +134,25 @@ def SetInsertionPoint(self, i): self.Location.SetInsertionPoint(i) - + def SetFocus(self): self.Location.SetFocus() + class LocationCellEditor(wx.grid.PyGridCellEditor): ''' Grid cell editor that uses LocationCellControl to display a browse button. ''' def __init__(self, table, controller): wx.grid.PyGridCellEditor.__init__(self) - + self.Table = table self.Controller = controller def __del__(self): self.CellControl = None self.Controller = None - + def Create(self, parent, id, evt_handler): self.CellControl = LocationCellControl(parent) self.SetControl(self.CellControl) @@ -169,20 +175,19 @@ self.Table.SetValueByName(row, 'Type', self.CellControl.GetVarType()) self.CellControl.Disable() return changed - + if wx.VERSION >= (3, 0, 0): def EndEdit(self, row, col, grid, oldval): return self.EndEditInternal(row, col, grid, oldval) else: def EndEdit(self, row, col, grid): - old_loc = self.Table.GetValueByName(row, 'Location') + old_loc = self.Table.GetValueByName(row, 'Location') return self.EndEditInternal(row, col, grid, old_loc) - + def SetSize(self, rect): self.CellControl.SetDimensions(rect.x + 1, rect.y, - rect.width, rect.height, - wx.SIZE_ALLOW_MINUS_ONE) + rect.width, rect.height, + wx.SIZE_ALLOW_MINUS_ONE) def Clone(self): return LocationCellEditor(self.Table, self.Controller) - diff -r c1298e7ffe3a -r 8391c11477f4 controls/LogViewer.py --- a/controls/LogViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/LogViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -23,20 +23,23 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import from datetime import datetime from time import time as gettime +from weakref import proxy + import numpy - import wx from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD from editors.DebugViewer import DebugViewer, REFRESH_PERIOD -from targets.typemapping import LogLevelsCount, LogLevels +from runtime.loglevels import LogLevelsCount, LogLevels from util.BitmapLibrary import GetBitmap -from weakref import proxy + THUMB_SIZE_RATIO = 1. / 8. + def ArrowPoints(direction, width, height, xoffset, yoffset): if direction == wx.TOP: return [wx.Point(xoffset + 1, yoffset + height - 2), @@ -47,6 +50,7 @@ wx.Point(xoffset + width / 2, yoffset - 2), wx.Point(xoffset + width - 1, yoffset - height + 1)] + class LogScrollBar(wx.Panel): def __init__(self, parent, size): @@ -58,7 +62,7 @@ self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_SIZE, self.OnResize) - self.ThumbPosition = 0. # -1 <= ThumbPosition <= 1 + self.ThumbPosition = 0. # -1 <= ThumbPosition <= 1 self.ThumbScrollingStartPos = None def GetRangeRect(self): @@ -66,7 +70,7 @@ return wx.Rect(0, width, width, height - 2 * width) def GetThumbRect(self): - width, height = self.GetClientSize() + width, _height = self.GetClientSize() range_rect = self.GetRangeRect() thumb_size = range_rect.height * THUMB_SIZE_RATIO thumb_range = range_rect.height - thumb_size @@ -115,8 +119,7 @@ def OnMotion(self, event): if event.Dragging() and self.ThumbScrollingStartPos is not None: - posx, posy = event.GetPosition() - width, height = self.GetClientSize() + _posx, posy = event.GetPosition() range_rect = self.GetRangeRect() thumb_size = range_rect.height * THUMB_SIZE_RATIO thumb_range = range_rect.height - thumb_size @@ -177,9 +180,11 @@ dc.EndDrawing() event.Skip() + BUTTON_SIZE = (70, 15) -class LogButton(): + +class LogButton(object): def __init__(self, label, callback): self.Position = wx.Point(0, 0) @@ -217,13 +222,15 @@ w, h = dc.GetTextExtent(self.Label) dc.DrawText(self.Label, - self.Position.x + (self.Size.width - w) / 2, - self.Position.y + (self.Size.height - h) / 2) + self.Position.x + (self.Size.width - w) / 2, + self.Position.y + (self.Size.height - h) / 2) + DATE_INFO_SIZE = 10 MESSAGE_INFO_SIZE = 18 -class LogMessage: + +class LogMessage(object): def __init__(self, tv_sec, tv_nsec, level, level_bitmap, msg): self.Date = datetime.utcfromtimestamp(tv_sec) @@ -263,7 +270,7 @@ dc.DrawBitmap(self.LevelBitmap, 10 + sw, offset + (MESSAGE_INFO_SIZE - bh) / 2) text = self.Message.replace("\n", " ") - mw, mh = dc.GetTextExtent(text) + _mw, mh = dc.GetTextExtent(text) dc.DrawText(text, 15 + sw + bw, offset + (MESSAGE_INFO_SIZE - mh) / 2) def GetHeight(self, draw_date): @@ -271,6 +278,7 @@ return DATE_INFO_SIZE + MESSAGE_INFO_SIZE return MESSAGE_INFO_SIZE + SECOND = 1 MINUTE = 60 * SECOND HOUR = 60 * MINUTE @@ -281,10 +289,11 @@ (_("1m"), MINUTE), (_("1s"), SECOND)] + class LogViewer(DebugViewer, wx.Panel): def __init__(self, parent, window): - wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER) + wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER) DebugViewer.__init__(self, None, False, False) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) @@ -292,7 +301,7 @@ main_sizer.AddGrowableRow(1) filter_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) + main_sizer.AddSizer(filter_sizer, border=5, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) self.MessageFilter = wx.ComboBox(self, style=wx.CB_READONLY) self.MessageFilter.Append(_("All")) @@ -301,20 +310,20 @@ for level in levels: self.MessageFilter.Append(_(level)) self.Bind(wx.EVT_COMBOBOX, self.OnMessageFilterChanged, self.MessageFilter) - filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + filter_sizer.AddWindow(self.MessageFilter, 1, border=5, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) self.SearchMessage = wx.SearchCtrl(self, style=wx.TE_PROCESS_ENTER) self.SearchMessage.ShowSearchButton(True) self.SearchMessage.ShowCancelButton(True) self.Bind(wx.EVT_TEXT_ENTER, self.OnSearchMessageChanged, self.SearchMessage) self.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, - self.OnSearchMessageSearchButtonClick, self.SearchMessage) + self.OnSearchMessageSearchButtonClick, self.SearchMessage) self.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, - self.OnSearchMessageCancelButtonClick, self.SearchMessage) - filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + self.OnSearchMessageCancelButtonClick, self.SearchMessage) + filter_sizer.AddWindow(self.SearchMessage, 3, border=5, flag=wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) self.CleanButton = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap("Clean"), - size=wx.Size(28, 28), style=wx.NO_BORDER) + size=wx.Size(28, 28), style=wx.NO_BORDER) self.CleanButton.SetToolTipString(_("Clean log messages")) self.Bind(wx.EVT_BUTTON, self.OnCleanButton, self.CleanButton) filter_sizer.AddWindow(self.CleanButton) @@ -322,7 +331,7 @@ message_panel_sizer = wx.FlexGridSizer(cols=2, hgap=0, rows=1, vgap=0) message_panel_sizer.AddGrowableCol(0) message_panel_sizer.AddGrowableRow(0) - main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) + main_sizer.AddSizer(message_panel_sizer, border=5, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) self.MessagePanel = wx.Panel(self) if wx.Platform == '__WXMSW__': @@ -379,7 +388,7 @@ self.ScrollTimer.Stop() def ResetLogMessages(self): - self.previous_log_count = [None]*LogLevelsCount + self.ResetLogCounters() self.OldestMessages = [] self.LogMessages = [] self.LogMessagesTimestamp = numpy.array([]) @@ -391,16 +400,19 @@ self.CleanButton.Enable(self.LogSource is not None) if log_source is not None: self.ResetLogMessages() - self.RefreshView() + wx.CallAfter(self.RefreshView) def GetLogMessageFromSource(self, msgidx, level): if self.LogSource is not None: answer = self.LogSource.GetLogMessage(level, msgidx) if answer is not None: - msg, tick, tv_sec, tv_nsec = answer + msg, _tick, tv_sec, tv_nsec = answer return LogMessage(tv_sec, tv_nsec, level, self.LevelIcons[level], msg) return None + def ResetLogCounters(self): + self.previous_log_count = [None]*LogLevelsCount + def SetLogCounters(self, log_count): new_messages = [] for level, count, prev in zip(xrange(LogLevelsCount), log_count, self.previous_log_count): @@ -410,7 +422,7 @@ oldest_message = (-1, None) else: dump_end = prev - 1 - for msgidx in xrange(count-1, dump_end,-1): + for msgidx in xrange(count-1, dump_end, -1): new_message = self.GetLogMessageFromSource(msgidx, level) if new_message is None: if prev is None: @@ -475,12 +487,12 @@ msgidx -= 1 if len(self.LogMessages) > 0: message = self.LogMessages[0] - for idx, msg in self.OldestMessages: + for _idx, msg in self.OldestMessages: if msg is not None and msg > message: message = msg while message is not None: level = message.Level - oldest_msgidx, oldest_message = self.OldestMessages[level] + oldest_msgidx, _oldest_message = self.OldestMessages[level] if oldest_msgidx > 0: message = self.GetLogMessageFromSource(oldest_msgidx - 1, level) if message is not None: @@ -501,13 +513,13 @@ current_message = message self.LogMessages.insert(message_idx, message) self.LogMessagesTimestamp = numpy.insert( - self.LogMessagesTimestamp, - [message_idx], - [message.Timestamp]) + self.LogMessagesTimestamp, + [message_idx], + [message.Timestamp]) self.CurrentMessage = self.LogMessages.index(current_message) if message_idx == 0 and self.FilterLogMessage(message, timestamp): return message, 0 - for idx, msg in self.OldestMessages: + for _idx, msg in self.OldestMessages: if msg is not None and (message is None or msg > message): message = msg return None, None @@ -549,6 +561,14 @@ self.MessageScrollBar.RefreshThumbPosition() + def IsPLCLogEmpty(self): + empty = True + for _level, prev in zip(xrange(LogLevelsCount), self.previous_log_count): + if prev is not None: + empty = False + break + return empty + def IsMessagePanelTop(self, message_idx=None): if message_idx is None: message_idx = self.CurrentMessage @@ -560,7 +580,7 @@ if message_idx is None: message_idx = self.CurrentMessage if message_idx is not None: - width, height = self.MessagePanel.GetClientSize() + _width, height = self.MessagePanel.GetClientSize() offset = 5 message = self.LogMessages[message_idx] draw_date = True @@ -590,7 +610,7 @@ def ScrollMessagePanelByPage(self, page): if self.CurrentMessage is not None: - width, height = self.MessagePanel.GetClientSize() + _width, height = self.MessagePanel.GetClientSize() message_per_page = max(1, (height - DATE_INFO_SIZE) / MESSAGE_INFO_SIZE - 1) self.ScrollMessagePanel(page * message_per_page) @@ -636,7 +656,7 @@ event.Skip() def OnCleanButton(self, event): - if self.LogSource is not None: + if self.LogSource is not None and not self.IsPLCLogEmpty(): self.LogSource.ResetLogCount() self.ResetLogMessages() self.RefreshView() @@ -654,7 +674,7 @@ def GetMessageByScreenPos(self, posx, posy): if self.CurrentMessage is not None: - width, height = self.MessagePanel.GetClientSize() + _width, height = self.MessagePanel.GetClientSize() message_idx = self.CurrentMessage message = self.LogMessages[message_idx] draw_date = True @@ -747,15 +767,15 @@ event.Skip() def OnMessagePanelResize(self, event): - width, height = self.MessagePanel.GetClientSize() + width, _height = self.MessagePanel.GetClientSize() offset = 2 for button in self.LeftButtons: button.SetPosition(offset, 2) - w, h = button.GetSize() + w, _h = button.GetSize() offset += w + 2 offset = width - 2 for button in self.RightButtons: - w, h = button.GetSize() + w, _h = button.GetSize() button.SetPosition(offset - w, 2) offset -= w + 2 if self.IsMessagePanelBottom(): diff -r c1298e7ffe3a -r 8391c11477f4 controls/PouInstanceVariablesPanel.py --- a/controls/PouInstanceVariablesPanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/PouInstanceVariablesPanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,21 +22,40 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from collections import namedtuple import wx import wx.lib.agw.customtreectrl as CT import wx.lib.buttons +from PLCControler import \ + ITEMS_VARIABLE, \ + ITEM_CONFIGURATION, \ + ITEM_RESOURCE, \ + ITEM_POU, \ + ITEM_TRANSITION, \ + ITEM_ACTION + +from util.BitmapLibrary import GetBitmap + + # Customize CustomTreeItem for adding icon on item right CT.GenericTreeItem._rightimages = [] + def SetRightImages(self, images): self._rightimages = images + + CT.GenericTreeItem.SetRightImages = SetRightImages + def GetRightImages(self): return self._rightimages + + CT.GenericTreeItem.GetRightImages = GetRightImages @@ -49,24 +68,24 @@ height = CT.CustomTreeCtrl.GetLineHeight(self, item) rightimages = item.GetRightImages() if len(rightimages) > 0: - r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) + _r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) return max(height, r_image_h + 8) return height def GetItemRightImagesBBox(self, item): rightimages = item.GetRightImages() if len(rightimages) > 0: - w, h = self.GetClientSize() + w, _h = self.GetClientSize() total_h = self.GetLineHeight(item) r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) - + bbox_width = (r_image_w + 4) * len(rightimages) + 4 bbox_height = r_image_h + 8 bbox_x = w - bbox_width bbox_y = item.GetY() + ((total_h > r_image_h) and [(total_h-r_image_h)/2] or [0])[0] - + return wx.Rect(bbox_x, bbox_y, bbox_width, bbox_height) - + return None def IsOverItemRightImage(self, item, point): @@ -75,30 +94,28 @@ point = self.CalcUnscrolledPosition(point) r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) images_bbx = self.GetItemRightImagesBBox(item) - + rect = wx.Rect(images_bbx.x + 4, images_bbx.y + 4, r_image_w, r_image_h) for r_image in rightimages: if rect.Inside(point): return r_image rect.x += r_image_w + 4 - + return None - + def PaintItem(self, item, dc, level, align): CT.CustomTreeCtrl.PaintItem(self, item, dc, level, align) - + rightimages = item.GetRightImages() if len(rightimages) > 0: images_bbx = self.GetItemRightImagesBBox(item) - r_image_w, r_image_h = self._imageListRight.GetSize(rightimages[0]) - + r_image_w, _r_image_h = self._imageListRight.GetSize(rightimages[0]) + dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetPen(wx.TRANSPARENT_PEN) - - bg_width = (r_image_w + 4) * len(rightimages) + 4 - bg_height = r_image_h + 8 - dc.DrawRectangle(images_bbx.x, images_bbx.y, + + dc.DrawRectangle(images_bbx.x, images_bbx.y, images_bbx.width, images_bbx.height) x_pos = images_bbx.x + 4 for r_image in rightimages: @@ -106,98 +123,98 @@ r_image, dc, x_pos, images_bbx.y + 4, wx.IMAGELIST_DRAW_TRANSPARENT) x_pos += r_image_w + 4 - + + _ButtonCallbacks = namedtuple("ButtonCallbacks", ["leftdown", "dclick"]) -from PLCControler import ITEMS_VARIABLE, ITEM_CONFIGURATION, ITEM_RESOURCE, ITEM_POU, ITEM_TRANSITION, ITEM_ACTION -from util.BitmapLibrary import GetBitmap class PouInstanceVariablesPanel(wx.Panel): def __init__(self, parent, window, controller, debug): - wx.Panel.__init__(self, name='PouInstanceTreePanel', - parent=parent, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) - - self.ParentButton = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap("top"), size=wx.Size(28, 28), style=wx.NO_BORDER) + wx.Panel.__init__(self, name='PouInstanceTreePanel', + parent=parent, pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + + self.ParentButton = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap("top"), size=wx.Size(28, 28), style=wx.NO_BORDER) self.ParentButton.SetToolTipString(_("Parent instance")) - self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick, - self.ParentButton) - + self.Bind(wx.EVT_BUTTON, self.OnParentButtonClick, + self.ParentButton) + self.InstanceChoice = wx.ComboBox(self, size=wx.Size(0, 0), style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInstanceChoiceChanged, - self.InstanceChoice) - - self.DebugButton = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap("debug_instance"), size=wx.Size(28, 28), style=wx.NO_BORDER) + self.InstanceChoice) + + self.DebugButton = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap("debug_instance"), size=wx.Size(28, 28), style=wx.NO_BORDER) self.DebugButton.SetToolTipString(_("Debug instance")) - self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick, - self.DebugButton) - - self.VariablesList = CustomTreeCtrlWithRightImage(self, - style=wx.SUNKEN_BORDER, - agwStyle=CT.TR_NO_BUTTONS| - CT.TR_SINGLE| - CT.TR_HAS_VARIABLE_ROW_HEIGHT| - CT.TR_HIDE_ROOT| - CT.TR_NO_LINES| - getattr(CT, "TR_ALIGN_WINDOWS_RIGHT", CT.TR_ALIGN_WINDOWS)) + self.Bind(wx.EVT_BUTTON, self.OnDebugButtonClick, + self.DebugButton) + + self.VariablesList = CustomTreeCtrlWithRightImage( + self, + style=wx.SUNKEN_BORDER, + agwStyle=(CT.TR_NO_BUTTONS | + CT.TR_SINGLE | + CT.TR_HAS_VARIABLE_ROW_HEIGHT | + CT.TR_HIDE_ROOT | + CT.TR_NO_LINES | + getattr(CT, "TR_ALIGN_WINDOWS_RIGHT", CT.TR_ALIGN_WINDOWS))) self.VariablesList.SetIndent(0) self.VariablesList.SetSpacing(5) - self.VariablesList.DoSelectItem = lambda *x,**y:True + self.VariablesList.DoSelectItem = lambda *x, **y: True self.VariablesList.Bind(CT.EVT_TREE_ITEM_ACTIVATED, - self.OnVariablesListItemActivated) + self.OnVariablesListItemActivated) self.VariablesList.Bind(wx.EVT_LEFT_DOWN, self.OnVariablesListLeftDown) self.VariablesList.Bind(wx.EVT_KEY_DOWN, self.OnVariablesListKeyDown) - + self.TreeRightImageList = wx.ImageList(24, 24) self.EditImage = self.TreeRightImageList.Add(GetBitmap("edit")) self.DebugInstanceImage = self.TreeRightImageList.Add(GetBitmap("debug_instance")) self.VariablesList.SetRightImageList(self.TreeRightImageList) - + self.ButtonCallBacks = { self.EditImage: _ButtonCallbacks( self.EditButtonCallback, None), self.DebugInstanceImage: _ButtonCallbacks( self.DebugButtonCallback, self.DebugButtonDClickCallback)} - + buttons_sizer = wx.FlexGridSizer(cols=3, hgap=0, rows=1, vgap=0) buttons_sizer.AddWindow(self.ParentButton) buttons_sizer.AddWindow(self.InstanceChoice, flag=wx.GROW) buttons_sizer.AddWindow(self.DebugButton) buttons_sizer.AddGrowableCol(1) buttons_sizer.AddGrowableRow(0) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) main_sizer.AddSizer(buttons_sizer, flag=wx.GROW) main_sizer.AddWindow(self.VariablesList, flag=wx.GROW) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + self.SetSizer(main_sizer) - + self.ParentWindow = window self.Controller = controller self.Debug = debug if not self.Debug: self.DebugButton.Hide() - + self.PouTagName = None self.PouInfos = None self.PouInstance = None - + def __del__(self): self.Controller = None - + def SetTreeImageList(self, tree_image_list): self.VariablesList.SetImageList(tree_image_list) - + def SetController(self, controller): self.Controller = controller - + self.RefreshView() - + def SetPouType(self, tagname, pou_instance=None): if self.Controller is not None: if tagname == "Project": @@ -206,7 +223,7 @@ tagname = self.Controller.ComputeConfigurationName(config_name) if pou_instance is not None: self.PouInstance = pou_instance - + if self.PouTagName != tagname: self.PouTagName = tagname self.RefreshView() @@ -214,49 +231,54 @@ self.RefreshInstanceChoice() else: self.RefreshView() - + def ResetView(self): self.Controller = None - + self.PouTagName = None self.PouInfos = None self.PouInstance = None - + self.RefreshView() - + def RefreshView(self): self.Freeze() self.VariablesList.DeleteAllItems() - + if self.Controller is not None and self.PouTagName is not None: - self.PouInfos = self.Controller.GetPouVariables(self.PouTagName, self.Debug) + if self.PouTagName.split('::')[0] in ['A', 'T']: + self.PouInfos = self.Controller.GetPouVariables('P::%s' % self.PouTagName.split('::')[1], self.Debug) + else: + self.PouInfos = self.Controller.GetPouVariables(self.PouTagName, self.Debug) + if None in self.Controller.GetEditedElementType(self.PouTagName, self.Debug) and self.PouInfos is not None: + self.PouInfos.debug = False else: self.PouInfos = None if self.PouInfos is not None: - root = self.VariablesList.AddRoot("") + root = self.VariablesList.AddRoot("", data=self.PouInfos) for var_infos in self.PouInfos.variables: if var_infos.type is not None: text = "%s (%s)" % (var_infos.name, var_infos.type) else: text = var_infos.name - + right_images = [] if var_infos.edit: right_images.append(self.EditImage) - + if var_infos.debug and self.Debug: right_images.append(self.DebugInstanceImage) - + item = self.VariablesList.AppendItem(root, text) item.SetRightImages(right_images) self.VariablesList.SetItemImage(item, self.ParentWindow.GetTreeImage(var_infos.var_class)) self.VariablesList.SetPyData(item, var_infos) - + self.RefreshInstanceChoice() self.RefreshButtons() - + self.Thaw() - + def RefreshInstanceChoice(self): self.InstanceChoice.Clear() self.InstanceChoice.SetValue("") @@ -274,12 +296,12 @@ else: self.PouInstance = None self.InstanceChoice.SetValue(_("Select an instance")) - + def RefreshButtons(self): enabled = self.InstanceChoice.GetSelection() != -1 self.ParentButton.Enable(enabled and self.PouInfos.var_class != ITEM_CONFIGURATION) self.DebugButton.Enable(enabled and self.PouInfos.debug and self.Debug) - + root = self.VariablesList.GetRootItem() if root is not None and root.IsOk(): item, item_cookie = self.VariablesList.GetFirstChild(root) @@ -290,12 +312,12 @@ if child.GetName() != "edit": child.Enable(enabled) item, item_cookie = self.VariablesList.GetNextChild(root, item_cookie) - + def EditButtonCallback(self, infos): var_class = infos.var_class if var_class == ITEM_RESOURCE: tagname = self.Controller.ComputeConfigurationResourceName( - self.InstanceChoice.GetStringSelection(), + self.InstanceChoice.GetStringSelection(), infos.name) elif var_class == ITEM_TRANSITION: tagname = self.Controller.ComputePouTransitionName( @@ -309,12 +331,15 @@ var_class = ITEM_POU tagname = self.Controller.ComputePouName(infos.type) self.ParentWindow.EditProjectElement(var_class, tagname) - + def DebugButtonCallback(self, infos): if self.InstanceChoice.GetSelection() != -1: var_class = infos.var_class - var_path = "%s.%s" % (self.InstanceChoice.GetStringSelection(), - infos.name) + instance_path = self.InstanceChoice.GetStringSelection() + if self.PouTagName.split("::")[0] in ["A", "T"]: + pos = instance_path.rfind('.') + instance_path = instance_path[0:pos] + var_path = "%s.%s" % (instance_path, infos.name) if var_class in ITEMS_VARIABLE: self.ParentWindow.AddDebugVariable(var_path, force=True) elif var_class == ITEM_TRANSITION: @@ -336,16 +361,16 @@ var_class, var_path, self.Controller.ComputePouName(infos.type)) - + def DebugButtonDClickCallback(self, infos): if self.InstanceChoice.GetSelection() != -1: if infos.var_class in ITEMS_VARIABLE: self.ParentWindow.AddDebugVariable( - "%s.%s" % (self.InstanceChoice.GetStringSelection(), - infos.name), + "%s.%s" % (self.InstanceChoice.GetStringSelection(), + infos.name), force=True, graph=True) - + def ShowInstanceChoicePopup(self): self.InstanceChoice.SetFocusFromKbd() size = self.InstanceChoice.GetSize() @@ -353,10 +378,10 @@ event.x = size.width / 2 event.y = size.height / 2 event.SetEventObject(self.InstanceChoice) - #event = wx.KeyEvent(wx.EVT_KEY_DOWN._getEvtType()) - #event.m_keyCode = wx.WXK_SPACE + # event = wx.KeyEvent(wx.EVT_KEY_DOWN._getEvtType()) + # event.m_keyCode = wx.WXK_SPACE self.InstanceChoice.GetEventHandler().ProcessEvent(event) - + def OnParentButtonClick(self, event): if self.InstanceChoice.GetSelection() != -1: parent_path = self.InstanceChoice.GetStringSelection().rsplit(".", 1)[0] @@ -365,11 +390,11 @@ wx.CallAfter(self.SetPouType, tagname, parent_path) wx.CallAfter(self.ParentWindow.SelectProjectTreeItem, tagname) event.Skip() - + def OnInstanceChoiceChanged(self, event): self.RefreshButtons() event.Skip() - + def OnDebugButtonClick(self, event): if self.InstanceChoice.GetSelection() != -1: self.ParentWindow.OpenDebugViewer( @@ -377,31 +402,37 @@ self.InstanceChoice.GetStringSelection(), self.PouTagName) event.Skip() - + def OnVariablesListItemActivated(self, event): selected_item = event.GetItem() if selected_item is not None and selected_item.IsOk(): item_infos = self.VariablesList.GetPyData(selected_item) if item_infos is not None: - + item_button = self.VariablesList.IsOverItemRightImage( selected_item, event.GetPoint()) if item_button is not None: callback = self.ButtonCallBacks[item_button].dclick if callback is not None: callback(item_infos) - + elif item_infos.var_class not in ITEMS_VARIABLE: instance_path = self.InstanceChoice.GetStringSelection() if item_infos.var_class == ITEM_RESOURCE: if instance_path != "": tagname = self.Controller.ComputeConfigurationResourceName( - instance_path, - item_infos.name) + instance_path, + item_infos.name) else: tagname = None else: - tagname = self.Controller.ComputePouName(item_infos.type) + parent_infos = self.VariablesList.GetPyData(selected_item.GetParent()) + if item_infos.var_class == ITEM_ACTION: + tagname = self.Controller.ComputePouActionName(parent_infos.type, item_infos.name) + elif item_infos.var_class == ITEM_TRANSITION: + tagname = self.Controller.ComputePouTransitionName(parent_infos.type, item_infos.name) + else: + tagname = self.Controller.ComputePouName(item_infos.type) if tagname is not None: if instance_path != "": item_path = "%s.%s" % (instance_path, item_infos.name) @@ -410,7 +441,7 @@ self.SetPouType(tagname, item_path) self.ParentWindow.SelectProjectTreeItem(tagname) event.Skip() - + def OnVariablesListLeftDown(self, event): if self.InstanceChoice.GetSelection() == -1: wx.CallAfter(self.ShowInstanceChoicePopup) @@ -420,15 +451,15 @@ if item is not None: item_infos = self.VariablesList.GetPyData(item) if item_infos is not None: - + item_button = self.VariablesList.IsOverItemRightImage( item, event.GetPosition()) if item_button is not None: callback = self.ButtonCallBacks[item_button].leftdown if callback is not None: callback(item_infos) - - elif (flags & CT.TREE_HITTEST_ONITEMLABEL and + + elif (flags & CT.TREE_HITTEST_ONITEMLABEL and item_infos.var_class in ITEMS_VARIABLE): self.ParentWindow.EnsureTabVisible( self.ParentWindow.DebugVariablePanel) @@ -443,4 +474,3 @@ keycode = event.GetKeyCode() if keycode != wx.WXK_LEFT: event.Skip() - diff -r c1298e7ffe3a -r 8391c11477f4 controls/ProjectPropertiesPanel.py --- a/controls/ProjectPropertiesPanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/ProjectPropertiesPanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,24 +23,29 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx - -#------------------------------------------------------------------------------- +from wx.lib.scrolledpanel import ScrolledPanel + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- REQUIRED_PARAMS = ["projectName", "productName", "productVersion", "companyName"] -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, - POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES +[ + TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES ] = range(10) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Project Properties Panel -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ProjectPropertiesPanel(wx.Notebook): - + def AddSizerParams(self, parent, sizer, params): for idx, (name, label) in enumerate(params): border = 0 @@ -47,105 +53,112 @@ border |= wx.TOP elif idx == len(params) - 1: border |= wx.BOTTOM - + st = wx.StaticText(parent, label=label) - sizer.AddWindow(st, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|border|wx.LEFT) - + sizer.AddWindow(st, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT) + tc = wx.TextCtrl(parent, style=wx.TE_PROCESS_ENTER) setattr(self, name, tc) callback = self.GetTextCtrlChangedFunction(tc, name) self.Bind(wx.EVT_TEXT_ENTER, callback, tc) tc.Bind(wx.EVT_KILL_FOCUS, callback) - sizer.AddWindow(tc, border=10, - flag=wx.GROW|border|wx.RIGHT) + sizer.AddWindow(tc, border=10, + flag=wx.GROW | border | wx.RIGHT) def __init__(self, parent, controller=None, window=None, enable_required=True): - wx.Notebook.__init__(self, parent, size=wx.Size(500, 300)) + wx.Notebook.__init__(self, parent) self.Controller = controller self.ParentWindow = window self.Values = None - + # Project Panel elements - self.ProjectPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) + self.ProjectPanel = ScrolledPanel(self, -1, style=wx.TAB_TRAVERSAL) + self.ProjectPanel.SetAutoLayout(1) + self.ProjectPanel.SetupScrolling() projectpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=5, vgap=15) projectpanel_sizer.AddGrowableCol(1) self.ProjectPanel.SetSizer(projectpanel_sizer) - + self.AddSizerParams(self.ProjectPanel, projectpanel_sizer, - [("projectName", _('Project Name (required):')), - ("projectVersion", _('Project Version (optional):')), - ("productName", _('Product Name (required):')), - ("productVersion", _('Product Version (required):')), - ("productRelease", _('Product Release (optional):'))]) - + [("projectName", _('Project Name (required):')), + ("projectVersion", _('Project Version (optional):')), + ("productName", _('Product Name (required):')), + ("productVersion", _('Product Version (required):')), + ("productRelease", _('Product Release (optional):'))]) + self.AddPage(self.ProjectPanel, _("Project")) - + # Author Panel elements - self.AuthorPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) + self.AuthorPanel = ScrolledPanel(self, -1, style=wx.TAB_TRAVERSAL) + self.AuthorPanel.SetAutoLayout(1) + self.AuthorPanel.SetupScrolling() authorpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=4, vgap=15) authorpanel_sizer.AddGrowableCol(1) self.AuthorPanel.SetSizer(authorpanel_sizer) - + self.AddSizerParams(self.AuthorPanel, authorpanel_sizer, - [("companyName", _('Company Name (required):')), - ("companyURL", _('Company URL (optional):')), - ("authorName", _('Author Name (optional):')), - ("organization", _('Organization (optional):'))]) - + [("companyName", _('Company Name (required):')), + ("companyURL", _('Company URL (optional):')), + ("authorName", _('Author Name (optional):')), + ("organization", _('Organization (optional):'))]) + self.AddPage(self.AuthorPanel, _("Author")) # Graphics Panel elements - self.GraphicsPanel = wx.Panel(self, style=wx.TAB_TRAVERSAL) + self.GraphicsPanel = ScrolledPanel(self, -1, style=wx.TAB_TRAVERSAL) + self.GraphicsPanel.SetAutoLayout(1) + self.GraphicsPanel.SetupScrolling() graphicpanel_sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=4, vgap=5) graphicpanel_sizer.AddGrowableCol(0) graphicpanel_sizer.AddGrowableRow(3) self.GraphicsPanel.SetSizer(graphicpanel_sizer) - + pageSize_st = wx.StaticText(self.GraphicsPanel, - label=_('Page Size (optional):')) - graphicpanel_sizer.AddWindow(pageSize_st, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT|wx.RIGHT) - + label=_('Page Size (optional):')) + graphicpanel_sizer.AddWindow( + pageSize_st, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.LEFT | wx.RIGHT) + pageSize_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) pageSize_sizer.AddGrowableCol(1) - graphicpanel_sizer.AddSizer(pageSize_sizer, border=10, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + graphicpanel_sizer.AddSizer(pageSize_sizer, border=10, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + for name, label in [('PageWidth', _('Width:')), ('PageHeight', _('Height:'))]: st = wx.StaticText(self.GraphicsPanel, label=label) - pageSize_sizer.AddWindow(st, border=12, - flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT) - - sp = wx.SpinCtrl(self.GraphicsPanel, - min=0, max=2**16, style=wx.TE_PROCESS_ENTER) + pageSize_sizer.AddWindow(st, border=12, + flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT) + + sp = wx.SpinCtrl(self.GraphicsPanel, + min=0, max=2**16, style=wx.TE_PROCESS_ENTER) setattr(self, name, sp) callback = self.GetPageSizeChangedFunction(sp, name) self.Bind(wx.EVT_TEXT_ENTER, callback, sp) sp.Bind(wx.EVT_KILL_FOCUS, callback) pageSize_sizer.AddWindow(sp, flag=wx.GROW) - + scaling_st = wx.StaticText(self.GraphicsPanel, - label=_('Grid Resolution:')) - graphicpanel_sizer.AddWindow(scaling_st, border=10, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + label=_('Grid Resolution:')) + graphicpanel_sizer.AddWindow(scaling_st, border=10, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + scaling_nb = wx.Notebook(self.GraphicsPanel) - graphicpanel_sizer.AddWindow(scaling_nb, border=10, - flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + graphicpanel_sizer.AddWindow(scaling_nb, border=10, + flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.Scalings = {} - for language, translation in [("FBD",_("FBD")), ("LD",_("LD")), ("SFC",_("SFC"))]: + for language, translation in [("FBD", _("FBD")), ("LD", _("LD")), ("SFC", _("SFC"))]: scaling_panel = wx.Panel(scaling_nb, style=wx.TAB_TRAVERSAL) scalingpanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) scalingpanel_sizer.AddGrowableCol(1) scaling_panel.SetSizer(scalingpanel_sizer) - + scaling_controls = [] for idx, (name, label) in enumerate([('XScale', _('Horizontal:')), ('YScale', _('Vertical:'))]): @@ -153,67 +166,73 @@ border = wx.TOP else: border = wx.BOTTOM - + st = wx.StaticText(scaling_panel, label=label) - scalingpanel_sizer.AddWindow(st, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|border|wx.LEFT) - - sp = wx.SpinCtrl(scaling_panel, - min=0, max=2**16, style=wx.TE_PROCESS_ENTER) + scalingpanel_sizer.AddWindow( + st, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | border | wx.LEFT) + + sp = wx.SpinCtrl(scaling_panel, + min=0, max=2**16, style=wx.TE_PROCESS_ENTER) scaling_controls.append(sp) callback = self.GetScalingChangedFunction(sp, language, name) self.Bind(wx.EVT_TEXT_ENTER, callback, sp) sp.Bind(wx.EVT_KILL_FOCUS, callback) - scalingpanel_sizer.AddWindow(sp, border=10, - flag=wx.GROW|border|wx.RIGHT) - + scalingpanel_sizer.AddWindow(sp, border=10, + flag=wx.GROW | border | wx.RIGHT) + self.Scalings[language] = scaling_controls scaling_nb.AddPage(scaling_panel, translation) - + self.AddPage(self.GraphicsPanel, _("Graphics")) # Miscellaneous Panel elements - self.MiscellaneousPanel = wx.Panel(id=-1, parent=self, - name='MiscellaneousPanel', pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + self.MiscellaneousPanel = ScrolledPanel(id=-1, parent=self, + name='MiscellaneousPanel', + pos=wx.Point(0, 0), + size=wx.Size(0, 0), + style=wx.TAB_TRAVERSAL) + self.MiscellaneousPanel.SetAutoLayout(1) + self.MiscellaneousPanel.SetupScrolling() miscellaneouspanel_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=15) miscellaneouspanel_sizer.AddGrowableCol(1) miscellaneouspanel_sizer.AddGrowableRow(1) self.MiscellaneousPanel.SetSizer(miscellaneouspanel_sizer) - + language_label = wx.StaticText(self.MiscellaneousPanel, - label=_('Language (optional):')) - miscellaneouspanel_sizer.AddWindow(language_label, border=10, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP|wx.LEFT) - - self.Language = wx.ComboBox(self.MiscellaneousPanel, - style=wx.CB_READONLY) + label=_('Language (optional):')) + miscellaneouspanel_sizer.AddWindow(language_label, border=10, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP | wx.LEFT) + + self.Language = wx.ComboBox(self.MiscellaneousPanel, + style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnLanguageChanged, self.Language) - miscellaneouspanel_sizer.AddWindow(self.Language, border=10, - flag=wx.GROW|wx.TOP|wx.RIGHT) - - description_label = wx.StaticText(self.MiscellaneousPanel, - label=_('Content Description (optional):')) - miscellaneouspanel_sizer.AddWindow(description_label, border=10, - flag=wx.BOTTOM|wx.LEFT) - - self.ContentDescription = wx.TextCtrl(self.MiscellaneousPanel, - style=wx.TE_MULTILINE|wx.TE_PROCESS_ENTER) - self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged, - self.ContentDescription) - self.ContentDescription.Bind(wx.EVT_KILL_FOCUS, - self.OnContentDescriptionChanged) - miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10, - flag=wx.GROW|wx.BOTTOM|wx.RIGHT) - + miscellaneouspanel_sizer.AddWindow(self.Language, border=10, + flag=wx.GROW | wx.TOP | wx.RIGHT) + + description_label = wx.StaticText( + self.MiscellaneousPanel, label=_('Content Description (optional):')) + miscellaneouspanel_sizer.AddWindow(description_label, border=10, + flag=wx.BOTTOM | wx.LEFT) + + self.ContentDescription = wx.TextCtrl( + self.MiscellaneousPanel, size=wx.Size(240, 150), + style=wx.TE_MULTILINE | wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_TEXT_ENTER, self.OnContentDescriptionChanged, + self.ContentDescription) + self.ContentDescription.Bind(wx.EVT_KILL_FOCUS, + self.OnContentDescriptionChanged) + miscellaneouspanel_sizer.AddWindow(self.ContentDescription, border=10, + flag=wx.GROW | wx.BOTTOM | wx.RIGHT) + self.AddPage(self.MiscellaneousPanel, _("Miscellaneous")) - + for param in REQUIRED_PARAMS: getattr(self, param).Enable(enable_required) - + languages = ["", "en-US", "fr-FR", "zh-CN", "ru-RU"] - + for language in languages: self.Language.Append(language) @@ -240,13 +259,13 @@ tc = getattr(self, item, None) if tc is not None: tc.SetValue(value) - + def GetValues(self): values = {} for param in ["projectName", "projectVersion", "productName", "productVersion", "productRelease", "companyName", - "companyURL", "authorName", + "companyURL", "authorName", "organization"]: value = getattr(self, param).GetValue() if param in REQUIRED_PARAMS or value != "": @@ -269,21 +288,18 @@ values["scaling"][language] = (self.Scalings[language][0].GetValue(), self.Scalings[language][1].GetValue()) return values - + def GetTextCtrlChangedFunction(self, textctrl, name): def TextCtrlChangedFunction(event): - if self.Controller is not None: - if self.Values is not None: - old_value = self.Values.get(name) - else: - old_value = None + if self.Controller is not None and self.Values is not None: + old_value = self.Values.get(name) new_value = textctrl.GetValue() if name not in REQUIRED_PARAMS and new_value == "": new_value = None if old_value != new_value: self.Controller.SetProjectProperties(properties={name: new_value}) self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, - PROJECTTREE, PAGETITLES) + PROJECTTREE, PAGETITLES) wx.CallAfter(self.RefreshView) event.Skip() return TextCtrlChangedFunction @@ -302,11 +318,11 @@ if old_value != new_value: self.Controller.SetProjectProperties(properties={"pageSize": new_value}) self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, - PAGETITLES, SCALING) + PAGETITLES, SCALING) wx.CallAfter(self.RefreshView) event.Skip() return PageSizeChangedFunction - + def GetScalingChangedFunction(self, spinctrl, language, name): def ScalingChangedFunction(event): if self.Controller is not None: @@ -322,11 +338,11 @@ if old_value != new_value: self.Controller.SetProjectProperties(properties={"scaling": {language: new_value}}) self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, - PAGETITLES, SCALING) + PAGETITLES, SCALING) wx.CallAfter(self.RefreshView) event.Skip() return ScalingChangedFunction - + def OnLanguageChanged(self, event): if self.Controller is not None: if self.Values is not None: @@ -341,7 +357,7 @@ self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES) wx.CallAfter(self.RefreshView) event.Skip() - + def OnContentDescriptionChanged(self, event): if self.Controller is not None: if self.Values is not None: diff -r c1298e7ffe3a -r 8391c11477f4 controls/SearchResultPanel.py --- a/controls/SearchResultPanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/SearchResultPanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from types import TupleType import wx @@ -31,6 +33,7 @@ from PLCControler import * from util.BitmapLibrary import GetBitmap + def GenerateName(infos): if infos[0] in ["input", "output", "value"]: return "%s %d:" % (infos[0], infos[1]) @@ -40,18 +43,22 @@ return "element %d %s" % (infos[1], infos[2]) return "%s:" % infos[0] -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Search Result Panel -#------------------------------------------------------------------------------- - -[ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL, - ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, +# ------------------------------------------------------------------------------- + + +[ + ID_SEARCHRESULTPANEL, ID_SEARCHRESULTPANELHEADERLABEL, + ID_SEARCHRESULTPANELSEARCHRESULTSTREE, ID_SEARCHRESULTPANELRESETBUTTON, ] = [wx.NewId() for _init_ctrls in range(4)] + class SearchResultPanel(wx.Panel): if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -60,139 +67,145 @@ def _init_coll_MainSizer_Items(self, parent): parent.AddSizer(self.HeaderSizer, 0, border=0, flag=wx.GROW) parent.AddWindow(self.SearchResultsTree, 1, border=0, flag=wx.GROW) - + def _init_coll_MainSizer_Growables(self, parent): parent.AddGrowableCol(0) parent.AddGrowableRow(1) def _init_coll_HeaderSizer_Items(self, parent): - parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + parent.AddWindow(self.HeaderLabel, 1, border=5, flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) parent.AddWindow(self.ResetButton, 0, border=0, flag=0) - + def _init_coll_HeaderSizer_Growables(self, parent): parent.AddGrowableCol(0) - + def _init_sizers(self): self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=0) self.HeaderSizer = wx.BoxSizer(wx.HORIZONTAL) - + self._init_coll_MainSizer_Items(self.MainSizer) self._init_coll_MainSizer_Growables(self.MainSizer) self._init_coll_HeaderSizer_Items(self.HeaderSizer) - + self.SetSizer(self.MainSizer) def _init_ctrls(self, prnt): - wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL, - name='SearchResultPanel', parent=prnt, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) - self.HeaderLabel = wx.StaticText(id=ID_SEARCHRESULTPANELHEADERLABEL, - name='HeaderLabel', parent=self, - pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) - - search_results_tree_style = CT.TR_HAS_BUTTONS|CT.TR_NO_LINES|CT.TR_HAS_VARIABLE_ROW_HEIGHT + name='HeaderLabel', parent=self, + pos=wx.Point(0, 0), size=wx.Size(0, 17), style=0) + + search_results_tree_style = CT.TR_HAS_BUTTONS | CT.TR_NO_LINES | CT.TR_HAS_VARIABLE_ROW_HEIGHT self.SearchResultsTree = CT.CustomTreeCtrl(id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE, - name="SearchResultsTree", parent=self, - pos=wx.Point(0, 0), style=search_results_tree_style) + name="SearchResultsTree", parent=self, + pos=wx.Point(0, 0), style=search_results_tree_style) if wx.VERSION >= (2, 8, 11): self.SearchResultsTree.SetAGWWindowStyleFlag(search_results_tree_style) self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnSearchResultsTreeItemActivated, - id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE) - - self.ResetButton = wx.lib.buttons.GenBitmapButton(self, - bitmap=GetBitmap("reset"), size=wx.Size(28, 28), style=wx.NO_BORDER) + id=ID_SEARCHRESULTPANELSEARCHRESULTSTREE) + + self.ResetButton = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap("reset"), + size=wx.Size(28, 28), style=wx.NO_BORDER) self.ResetButton.SetToolTipString(_("Reset search result")) self.Bind(wx.EVT_BUTTON, self.OnResetButton, self.ResetButton) - + self._init_sizers() def __init__(self, parent, window): + wx.Panel.__init__(self, id=ID_SEARCHRESULTPANEL, + name='SearchResultPanel', parent=parent, + pos=wx.Point(0, 0), + size=wx.Size(0, 0), style=wx.TAB_TRAVERSAL) + self.ParentWindow = window - + self._init_ctrls(parent) - + # Define Tree item icon list self.TreeImageList = wx.ImageList(16, 16) self.TreeImageDict = {} - + # Icons for other items for imgname, itemtype in [ - #editables - ("PROJECT", ITEM_PROJECT), - ("TRANSITION", ITEM_TRANSITION), - ("ACTION", ITEM_ACTION), - ("CONFIGURATION", ITEM_CONFIGURATION), - ("RESOURCE", ITEM_RESOURCE), - ("DATATYPE", ITEM_DATATYPE), - ("ACTION", "action_block"), - ("IL", "IL"), - ("ST", "ST")]: + # editables + ("PROJECT", ITEM_PROJECT), + ("TRANSITION", ITEM_TRANSITION), + ("ACTION", ITEM_ACTION), + ("CONFIGURATION", ITEM_CONFIGURATION), + ("RESOURCE", ITEM_RESOURCE), + ("DATATYPE", ITEM_DATATYPE), + ("ACTION", "action_block"), + ("IL", "IL"), + ("ST", "ST")]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) - + for itemtype in ["function", "functionBlock", "program", "comment", "block", "io_variable", "connector", "contact", "coil", - "step", "transition", "jump", - "var_local", "var_input", + "step", "transition", "jump", + "var_local", "var_input", "var_inout", "var_output"]: self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(itemtype.upper())) - + # Assign icon list to TreeCtrl self.SearchResultsTree.SetImageList(self.TreeImageList) - + self.ResetSearchResults() def SetSearchResults(self, criteria, search_results): self.Criteria = criteria self.SearchResults = {} self.ElementsOrder = [] - + for infos, start, end, text in search_results: if infos[0] not in self.ElementsOrder: self.ElementsOrder.append(infos[0]) - + results = self.SearchResults.setdefault(infos[0], []) results.append((infos, start, end, text)) - + self.RefreshView() - + def ResetSearchResults(self): self.Criteria = None self.ElementsOrder = [] self.SearchResults = {} self.RefreshView() - + def RefreshView(self): self.SearchResultsTree.DeleteAllItems() if self.Criteria is None: + self.SearchResultsTree.AddRoot("") + root = self.SearchResultsTree.GetRootItem() + root.SetHilight(False) self.HeaderLabel.SetLabel(_("No search results available.")) self.ResetButton.Enable(False) else: matches_number = 0 - search_results_tree_infos = {"name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), - "type": ITEM_PROJECT, - "data": None, - "text": None, - "matches": None, - } + search_results_tree_infos = { + "name": _("Project '%s':") % self.ParentWindow.Controler.GetProjectName(), + "type": ITEM_PROJECT, + "data": None, + "text": None, + "matches": None, + } search_results_tree_children = search_results_tree_infos.setdefault("children", []) for tagname in self.ElementsOrder: results = self.SearchResults.get(tagname, []) matches_number += len(results) - + words = tagname.split("::") - + element_type = self.ParentWindow.Controler.GetElementType(tagname) if element_type == ITEM_POU: element_type = self.ParentWindow.Controler.GetPouType(words[1]) - + element_infos = {"name": words[-1], "type": element_type, "data": tagname, "text": None, "matches": len(results)} - + children = element_infos.setdefault("children", []) for infos, start, end, text in results: if infos[1] == "name" or element_type == ITEM_DATATYPE: @@ -215,15 +228,16 @@ child_type = self.ParentWindow.Controler.GetPouBodyType(words[1]) else: child_name = GenerateName(infos[3:]) - child_infos = {"name": child_name, - "type": child_type, - "data": (infos, start, end ,None), - "text": text, - "matches": 1, - "children": [], - } + child_infos = { + "name": child_name, + "type": child_type, + "data": (infos, start, end, None), + "text": text, + "matches": 1, + "children": [], + } children.append(child_infos) - + if len(words) > 2: for _element_infos in search_results_tree_children: if _element_infos["name"] == words[1]: @@ -234,41 +248,40 @@ search_results_tree_children.append(element_infos) else: search_results_tree_children.append(element_infos) - + if matches_number < 2: header_format = _("'{a1}' - {a2} match in project") else: header_format = _("'{a1}' - {a2} matches in project") - - self.HeaderLabel.SetLabel(header_format.format(a1 = self.Criteria["find_pattern"], a2 = matches_number)) + + self.HeaderLabel.SetLabel(header_format.format(a1=self.Criteria["find_pattern"], a2=matches_number)) self.ResetButton.Enable(True) - + if matches_number > 0: root = self.SearchResultsTree.GetRootItem() if root is None: root = self.SearchResultsTree.AddRoot(search_results_tree_infos["name"]) self.GenerateSearchResultsTreeBranch(root, search_results_tree_infos) self.SearchResultsTree.Expand(root) - + def GetTextCtrlClickFunction(self, item): def OnTextCtrlClick(event): self.SearchResultsTree.SelectItem(item) event.Skip() return OnTextCtrlClick - + def GetTextCtrlDClickFunction(self, item): def OnTextCtrlDClick(event): self.ShowSearchResults(item) event.Skip() return OnTextCtrlDClick - + def GenerateSearchResultsTreeBranch(self, root, infos): - to_delete = [] if infos["name"] == "body": item_name = "%d:" % infos["data"][1][0] else: item_name = infos["name"] - + self.SearchResultsTree.SetItemText(root, item_name) self.SearchResultsTree.SetPyData(root, infos["data"]) self.SearchResultsTree.SetItemBackgroundColour(root, wx.WHITE) @@ -278,7 +291,7 @@ self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[self.ParentWindow.Controler.GetPouType(infos["name"])]) else: self.SearchResultsTree.SetItemImage(root, self.TreeImageDict[infos["type"]]) - + text = None if infos["text"] is not None: text = infos["text"] @@ -291,13 +304,13 @@ text = _("(%d matches)") % infos["matches"] start_idx, end_idx = 0, len(text) style = wx.TextAttr(wx.Colour(0, 127, 174)) - + if text is not None: - text_ctrl_style = wx.BORDER_NONE|wx.TE_READONLY|wx.TE_RICH2 + text_ctrl_style = wx.BORDER_NONE | wx.TE_READONLY | wx.TE_RICH2 if wx.Platform != '__WXMSW__' or len(text.splitlines()) > 1: text_ctrl_style |= wx.TE_MULTILINE - text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), - value=text, style=text_ctrl_style) + text_ctrl = wx.TextCtrl(id=-1, parent=self.SearchResultsTree, pos=wx.Point(0, 0), + value=text, style=text_ctrl_style) width, height = text_ctrl.GetTextExtent(text) text_ctrl.SetClientSize(wx.Size(width + 1, height)) text_ctrl.SetBackgroundColour(self.SearchResultsTree.GetBackgroundColour()) @@ -306,18 +319,15 @@ text_ctrl.SetInsertionPoint(0) text_ctrl.SetStyle(start_idx, end_idx, style) self.SearchResultsTree.SetItemWindow(root, text_ctrl) - - if wx.VERSION >= (2, 6, 0): - item, root_cookie = self.SearchResultsTree.GetFirstChild(root) - else: - item, root_cookie = self.SearchResultsTree.GetFirstChild(root, 0) + + item, root_cookie = self.SearchResultsTree.GetFirstChild(root) for child in infos["children"]: if item is None: item = self.SearchResultsTree.AppendItem(root, "") item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) self.GenerateSearchResultsTreeBranch(item, child) item, root_cookie = self.SearchResultsTree.GetNextChild(root, root_cookie) - + def ShowSearchResults(self, item): data = self.SearchResultsTree.GetPyData(item) if isinstance(data, TupleType): @@ -325,13 +335,13 @@ else: search_results = self.SearchResults.get(data, []) self.ParentWindow.ClearHighlights(SEARCH_RESULT_HIGHLIGHT) - for infos, start, end, text in search_results: + for infos, start, end, _text in search_results: self.ParentWindow.ShowSearchResult(infos, start, end) - + def OnSearchResultsTreeItemActivated(self, event): self.ShowSearchResults(event.GetItem()) event.Skip() - + def OnResetButton(self, event): self.ResetSearchResults() self.ParentWindow.ClearSearchResults() diff -r c1298e7ffe3a -r 8391c11477f4 controls/TextCtrlAutoComplete.py --- a/controls/TextCtrlAutoComplete.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/TextCtrlAutoComplete.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,8 +22,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +import cPickle import wx -import cPickle MAX_ITEM_COUNT = 10 MAX_ITEM_SHOWN = 6 @@ -34,34 +36,37 @@ LISTBOX_BORDER_HEIGHT = 4 LISTBOX_INTERVAL_HEIGHT = 6 + class PopupWithListbox(wx.PopupWindow): - - def __init__(self, parent, choices=[]): + + def __init__(self, parent, choices=None): wx.PopupWindow.__init__(self, parent, wx.BORDER_SIMPLE) - - self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL|wx.LB_SINGLE|wx.LB_SORT) - + + self.ListBox = wx.ListBox(self, -1, style=wx.LB_HSCROLL | wx.LB_SINGLE | wx.LB_SORT) + + choices = [] if choices is None else choices self.SetChoices(choices) - + self.ListBox.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown) self.ListBox.Bind(wx.EVT_MOTION, self.OnMotion) - + def SetChoices(self, choices): max_text_width = 0 max_text_height = 0 - + self.ListBox.Clear() for choice in choices: self.ListBox.Append(choice) w, h = self.ListBox.GetTextExtent(choice) max_text_width = max(max_text_width, w) max_text_height = max(max_text_height, h) - + itemcount = min(len(choices), MAX_ITEM_SHOWN) width = self.Parent.GetSize()[0] - height = max_text_height * itemcount + \ - LISTBOX_INTERVAL_HEIGHT * max(0, itemcount - 1) + \ - 2 * LISTBOX_BORDER_HEIGHT + height = \ + max_text_height * itemcount + \ + LISTBOX_INTERVAL_HEIGHT * max(0, itemcount - 1) + \ + 2 * LISTBOX_BORDER_HEIGHT if max_text_width + 10 > width: height += 15 size = wx.Size(width, height) @@ -69,7 +74,7 @@ size.width -= 2 self.ListBox.SetSize(size) self.SetClientSize(size) - + def MoveSelection(self, direction): selected = self.ListBox.GetSelection() if selected == wx.NOT_FOUND: @@ -82,10 +87,10 @@ if selected == self.ListBox.GetCount(): selected = wx.NOT_FOUND self.ListBox.SetSelection(selected) - + def GetSelection(self): return self.ListBox.GetStringSelection() - + def OnLeftDown(self, event): selected = self.ListBox.HitTest(wx.Point(event.GetX(), event.GetY())) parent_size = self.Parent.GetSize() @@ -99,16 +104,17 @@ else: wx.CallAfter(self.Parent.DismissListBox) event.Skip() - + def OnMotion(self, event): self.ListBox.SetSelection( self.ListBox.HitTest(wx.Point(event.GetX(), event.GetY()))) event.Skip() - + + class TextCtrlAutoComplete(wx.TextCtrl): - def __init__ (self, parent, choices=None, dropDownClick=True, - element_path=None, **therest): + def __init__(self, parent, choices=None, dropDownClick=True, + element_path=None, **therest): """ Constructor works just like wx.TextCtrl except you can pass in a list of choices. You can also change the choice list at any time @@ -118,21 +124,21 @@ therest['style'] = wx.TE_PROCESS_ENTER | therest.get('style', 0) wx.TextCtrl.__init__(self, parent, **therest) - - #Some variables + + # Some variables self._dropDownClick = dropDownClick self._lastinsertionpoint = None self._hasfocus = False - + self._screenheight = wx.SystemSettings.GetMetric(wx.SYS_SCREEN_Y) self.element_path = element_path - + self.listbox = None - + self.SetChoices(choices) - #gp = self - #while ( gp != None ) : + # gp = self + # while ( gp != None ) : # gp.Bind ( wx.EVT_MOVE , self.onControlChanged, gp ) # gp.Bind ( wx.EVT_SIZE , self.onControlChanged, gp ) # gp = gp.GetParent() @@ -142,7 +148,7 @@ self.Bind(wx.EVT_TEXT, self.OnEnteredText) self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown) - #If need drop down on left click + # If need drop down on left click if dropDownClick: self.Bind(wx.EVT_LEFT_DOWN, self.OnClickToggleDown) self.Bind(wx.EVT_LEFT_UP, self.OnClickToggleUp) @@ -201,14 +207,14 @@ self.DismissListBox() self._hasfocus = False event.Skip() - + def SetChoices(self, choices): self._choices = choices self.RefreshListBoxChoices() - + def GetChoices(self): return self._choices - + def SetValueFromSelected(self, selected): """ Sets the wx.TextCtrl value from the selected wx.ListCtrl item. @@ -217,7 +223,7 @@ if selected != "": self.SetValue(selected) self.DismissListBox() - + def RefreshListBoxChoices(self): if self.listbox is not None: text = self.GetValue() @@ -227,7 +233,7 @@ def PopupListBox(self): if self.listbox is None: self.listbox = PopupWithListbox(self) - + # Show the popup right below or above the button # depending on available screen space... pos = self.ClientToScreen((0, 0)) @@ -236,9 +242,9 @@ pos.x -= 2 pos.y -= 2 self.listbox.Position(pos, (0, sz[1])) - + self.RefreshListBoxChoices() - + self.listbox.Show() def DismissListBox(self): diff -r c1298e7ffe3a -r 8391c11477f4 controls/VariablePanel.py --- a/controls/VariablePanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/VariablePanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,951 +1,1019 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import os -import re -from types import TupleType, StringType, UnicodeType - -import wx -import wx.grid -import wx.lib.buttons - -from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS, DefaultType -from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD, ERROR_HIGHLIGHT -from dialogs.ArrayTypeDialog import ArrayTypeDialog -from CustomGrid import CustomGrid -from CustomTable import CustomTable -from LocationCellEditor import LocationCellEditor -from util.BitmapLibrary import GetBitmap -from PLCControler import _VariableInfos - -#------------------------------------------------------------------------------- -# Helpers -#------------------------------------------------------------------------------- - -[TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, - POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES -] = range(10) - -def GetVariableTableColnames(location): - _ = lambda x : x - if location: - return ["#", _("Name"), _("Class"), _("Type"), _("Location"), _("Initial Value"), _("Option"), _("Documentation")] - return ["#", _("Name"), _("Class"), _("Type"), _("Initial Value"), _("Option"), _("Documentation")] - -def GetOptions(constant=True, retain=True, non_retain=True): - _ = lambda x : x - options = [""] - if constant: - options.append(_("Constant")) - if retain: - options.append(_("Retain")) - if non_retain: - options.append(_("Non-Retain")) - return options -OPTIONS_DICT = dict([(_(option), option) for option in GetOptions()]) - -def GetFilterChoiceTransfer(): - _ = lambda x : x - return {_("All"): _("All"), _("Interface"): _("Interface"), - _(" Input"): _("Input"), _(" Output"): _("Output"), _(" InOut"): _("InOut"), - _(" External"): _("External"), _("Variables"): _("Variables"), _(" Local"): _("Local"), - _(" Temp"): _("Temp"), _("Global"): _("Global")}#, _("Access") : _("Access")} -VARIABLE_CHOICES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().iterkeys()]) -VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()]) - -CheckOptionForClass = {"Local": lambda x: x, - "Temp": lambda x: "", - "Input": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), - "InOut": lambda x: "", - "Output": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), - "Global": lambda x: {"Constant": "Constant", "Retain": "Retain"}.get(x, ""), - "External": lambda x: {"Constant": "Constant"}.get(x, "") - } - -LOCATION_MODEL = re.compile("((?:%[IQM](?:\*|(?:[XBWLD]?[0-9]+(?:\.[0-9]+)*)))?)$") -VARIABLE_NAME_SUFFIX_MODEL = re.compile("([0-9]*)$") - -#------------------------------------------------------------------------------- -# Variables Panel Table -#------------------------------------------------------------------------------- - -class VariableTable(CustomTable): - - """ - A custom wx.grid.Grid Table using user supplied data - """ - def __init__(self, parent, data, colnames): - # The base class must be initialized *first* - CustomTable.__init__(self, parent, data, colnames) - self.old_value = None - - def GetValueByName(self, row, colname): - if row < self.GetNumberRows(): - return getattr(self.data[row], colname) - - def SetValueByName(self, row, colname, value): - if row < self.GetNumberRows(): - setattr(self.data[row], colname, value) - - def GetValue(self, row, col): - if row < self.GetNumberRows(): - if col == 0: - return self.data[row].Number - colname = self.GetColLabelValue(col, False) - if colname == "Initial Value": - colname = "InitialValue" - value = getattr(self.data[row], colname, "") - if colname == "Type" and isinstance(value, TupleType): - if value[0] == "array": - return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "..".join(x), value[2])), value[1]) - if not isinstance(value, (StringType, UnicodeType)): - value = str(value) - if colname in ["Class", "Option"]: - return _(value) - return value - - def SetValue(self, row, col, value): - if col < len(self.colnames): - colname = self.GetColLabelValue(col, False) - if colname == "Name": - self.old_value = getattr(self.data[row], colname) - elif colname == "Class": - value = VARIABLE_CLASSES_DICT[value] - self.SetValueByName(row, "Option", CheckOptionForClass[value](self.GetValueByName(row, "Option"))) - if value == "External": - self.SetValueByName(row, "InitialValue", "") - elif colname == "Option": - value = OPTIONS_DICT[value] - elif colname == "Initial Value": - colname = "InitialValue" - setattr(self.data[row], colname, value) - - def GetOldValue(self): - return self.old_value - - def _GetRowEdit(self, row): - row_edit = self.GetValueByName(row, "Edit") - var_type = self.Parent.GetTagName() - bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type) - if bodytype in ["ST", "IL"]: - row_edit = True; - return row_edit - - def _updateColAttrs(self, grid): - """ - wx.grid.Grid -> update the column attributes to add the - appropriate renderer given the column name. - - Otherwise default to the default renderer. - """ - for row in range(self.GetNumberRows()): - var_class = self.GetValueByName(row, "Class") - var_type = self.GetValueByName(row, "Type") - row_highlights = self.Highlights.get(row, {}) - for col in range(self.GetNumberCols()): - editor = None - renderer = None - colname = self.GetColLabelValue(col, False) - if self.Parent.Debug: - grid.SetReadOnly(row, col, True) - else: - if colname == "Option": - options = GetOptions(constant = var_class in ["Local", "External", "Global"], - retain = self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output", "Global"], - non_retain = self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output"]) - if len(options) > 1: - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(",".join(map(_, options))) - else: - grid.SetReadOnly(row, col, True) - elif col != 0 and self._GetRowEdit(row): - grid.SetReadOnly(row, col, False) - if colname == "Name": - editor = wx.grid.GridCellTextEditor() - renderer = wx.grid.GridCellStringRenderer() - elif colname == "Initial Value": - if var_class not in ["External", "InOut"]: - if self.Parent.Controler.IsEnumeratedType(var_type): - editor = wx.grid.GridCellChoiceEditor() - editor.SetParameters(",".join([""] + self.Parent.Controler.GetEnumeratedDataValues(var_type))) - else: - editor = wx.grid.GridCellTextEditor() - renderer = wx.grid.GridCellStringRenderer() - else: - grid.SetReadOnly(row, col, True) - elif colname == "Location": - if var_class in ["Local", "Global"] and self.Parent.Controler.IsLocatableType(var_type): - editor = LocationCellEditor(self, self.Parent.Controler) - renderer = wx.grid.GridCellStringRenderer() - else: - grid.SetReadOnly(row, col, True) - elif colname == "Class": - if len(self.Parent.ClassList) == 1: - grid.SetReadOnly(row, col, True) - else: - editor = wx.grid.GridCellChoiceEditor() - excluded = [] - if self.Parent.IsFunctionBlockType(var_type): - excluded.extend(["Local","Temp"]) - editor.SetParameters(",".join([_(choice) for choice in self.Parent.ClassList if choice not in excluded])) - elif colname != "Documentation": - grid.SetReadOnly(row, col, True) - - grid.SetCellEditor(row, col, editor) - grid.SetCellRenderer(row, col, renderer) - - if colname == "Location" and LOCATION_MODEL.match(self.GetValueByName(row, colname)) is None: - highlight_colours = ERROR_HIGHLIGHT - else: - highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] - grid.SetCellBackgroundColour(row, col, highlight_colours[0]) - grid.SetCellTextColour(row, col, highlight_colours[1]) - self.ResizeRow(grid, row) - -#------------------------------------------------------------------------------- -# Variable Panel Drop Target -#------------------------------------------------------------------------------- - -class VariableDropTarget(wx.TextDropTarget): - ''' - This allows dragging a variable location from somewhere to the Location - column of a variable row. - - The drag source should be a TextDataObject containing a Python tuple like: - ('%ID0.0.0', 'location', 'REAL') - - c_ext/CFileEditor.py has an example of this (you can drag a C extension - variable to the Location column of the variable panel). - ''' - def __init__(self, parent): - wx.TextDropTarget.__init__(self) - self.ParentWindow = parent - - def OnDropText(self, x, y, data): - self.ParentWindow.ParentWindow.Select() - x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y) - col = self.ParentWindow.VariablesGrid.XToCol(x) - row = self.ParentWindow.VariablesGrid.YToRow(y) - message = None - element_type = self.ParentWindow.ElementType - try: - values = eval(data) - except: - message = _("Invalid value \"%s\" for variable grid element")%data - values = None - if not isinstance(values, TupleType): - message = _("Invalid value \"%s\" for variable grid element")%data - values = None - if values is not None: - if col != wx.NOT_FOUND and row != wx.NOT_FOUND: - colname = self.ParentWindow.Table.GetColLabelValue(col, False) - if colname == "Location" and values[1] == "location": - if not self.ParentWindow.Table.GetValueByName(row, "Edit"): - message = _("Can't give a location to a function block instance") - elif self.ParentWindow.Table.GetValueByName(row, "Class") not in ["Local", "Global"]: - message = _("Can only give a location to local or global variables") - else: - location = values[0] - variable_type = self.ParentWindow.Table.GetValueByName(row, "Type") - base_type = self.ParentWindow.Controler.GetBaseType(variable_type) - - if values[2] is not None: - base_location_type = self.ParentWindow.Controler.GetBaseType(values[2]) - if values[2] != variable_type and base_type != base_location_type: - message = _("Incompatible data types between \"{a1}\" and \"{a2}\"").\ - format(a1 = values[2], a2 = variable_type) - - if message is None: - if not location.startswith("%"): - if location[0].isdigit() and base_type != "BOOL": - message = _("Incompatible size of data between \"%s\" and \"BOOL\"")%location - elif location[0] not in LOCATIONDATATYPES: - message = _("Unrecognized data size \"%s\"")%location[0] - elif base_type not in LOCATIONDATATYPES[location[0]]: - message = _("Incompatible size of data between \"{a1}\" and \"{a2}\"").\ - format(a1 = location, a2 = variable_type) - else: - dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow.ParentWindow, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) - if dialog.ShowModal() == wx.ID_OK: - selected = dialog.GetSelection() - else: - selected = None - dialog.Destroy() - if selected is None: - return - if selected == 0: - location = "%I" + location - elif selected == 1: - location = "%Q" + location - else: - location = "%M" + location - - if message is None: - self.ParentWindow.Table.SetValue(row, col, location) - self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid) - self.ParentWindow.SaveValues() - elif colname == "Initial Value" and values[1] == "Constant": - if not self.ParentWindow.Table.GetValueByName(row, "Edit"): - message = _("Can't set an initial value to a function block instance") - else: - self.ParentWindow.Table.SetValue(row, col, values[0]) - self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid) - self.ParentWindow.SaveValues() - elif (element_type not in ["config", "resource", "function"] and values[1] == "Global" and - self.ParentWindow.Filter in ["All", "Interface", "External"] or - element_type != "function" and values[1] in ["location", "NamedConstant"]): - if values[1] in ["location","NamedConstant"]: - var_name = values[3] - else: - var_name = values[0] - tagname = self.ParentWindow.GetTagName() - dlg = wx.TextEntryDialog( - self.ParentWindow.ParentWindow.ParentWindow, - _("Confirm or change variable name"), - _('Variable Drop'), var_name) - dlg.SetValue(var_name) - var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None - dlg.Destroy() - if var_name is None: - return - elif var_name.upper() in [name.upper() - for name in self.ParentWindow.Controler.\ - GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name - elif not var_name.upper() in [name.upper() - for name in self.ParentWindow.Controler.\ - GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: - var_infos = self.ParentWindow.DefaultValue.copy() - var_infos.Name = var_name - var_infos.Type = values[2] - var_infos.Documentation = values[4] - if values[1] == "location": - location = values[0] - if not location.startswith("%"): - dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow.ParentWindow, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) - if dialog.ShowModal() == wx.ID_OK: - selected = dialog.GetSelection() - else: - selected = None - dialog.Destroy() - if selected is None: - return - if selected == 0: - location = "%I" + location - elif selected == 1: - location = "%Q" + location - else: - location = "%M" + location - if element_type == "functionBlock": - configs = self.ParentWindow.Controler.GetProjectConfigNames( - self.ParentWindow.Debug) - if len(configs) == 0: - return - if not var_name.upper() in [name.upper() - for name in self.ParentWindow.Controler.\ - GetConfigurationVariableNames(configs[0])]: - self.ParentWindow.Controler.AddConfigurationGlobalVar( - configs[0], values[2], var_name, location, "") - var_infos.Class = "External" - else: - if element_type == "program": - var_infos.Class = "Local" - else: - var_infos.Class = "Global" - var_infos.Location = location - elif values[1] == "NamedConstant": - if element_type in ["functionBlock","program"]: - var_infos.Class = "Local" - var_infos.InitialValue = values[0] - else : - return - else: - var_infos.Class = "External" - var_infos.Number = len(self.ParentWindow.Values) - self.ParentWindow.Values.append(var_infos) - self.ParentWindow.SaveValues() - self.ParentWindow.RefreshValues() - else: - message = _("\"%s\" element for this pou already exists!")%var_name - - if message is not None: - wx.CallAfter(self.ShowMessage, message) - - def ShowMessage(self, message): - message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - -#------------------------------------------------------------------------------- -# Variable Panel -#------------------------------------------------------------------------------- - -class VariablePanel(wx.Panel): - - def __init__(self, parent, window, controler, element_type, debug=False): - wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) - - self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0) - self.MainSizer.AddGrowableCol(0) - self.MainSizer.AddGrowableRow(1) - - controls_sizer = wx.FlexGridSizer(cols=10, hgap=5, rows=1, vgap=5) - controls_sizer.AddGrowableCol(5) - controls_sizer.AddGrowableRow(0) - self.MainSizer.AddSizer(controls_sizer, border=5, flag=wx.GROW|wx.ALL) - - self.ReturnTypeLabel = wx.StaticText(self, label=_('Return Type:')) - controls_sizer.AddWindow(self.ReturnTypeLabel, flag=wx.ALIGN_CENTER_VERTICAL) - - self.ReturnType = wx.ComboBox(self, - size=wx.Size(145, -1), style=wx.CB_READONLY) - self.Bind(wx.EVT_COMBOBOX, self.OnReturnTypeChanged, self.ReturnType) - controls_sizer.AddWindow(self.ReturnType) - - self.DescriptionLabel = wx.StaticText(self, label=_('Description:')) - controls_sizer.AddWindow(self.DescriptionLabel, flag=wx.ALIGN_CENTER_VERTICAL) - - self.Description = wx.TextCtrl(self, - size=wx.Size(250, -1), style=wx.TE_PROCESS_ENTER) - self.Bind(wx.EVT_TEXT_ENTER, self.OnDescriptionChanged, self.Description) - self.Description.Bind(wx.EVT_KILL_FOCUS, self.OnDescriptionChanged) - controls_sizer.AddWindow(self.Description) - - class_filter_label = wx.StaticText(self, label=_('Class Filter:')) - controls_sizer.AddWindow(class_filter_label, flag=wx.ALIGN_CENTER_VERTICAL) - - self.ClassFilter = wx.ComboBox(self, - size=wx.Size(145, -1), style=wx.CB_READONLY) - self.Bind(wx.EVT_COMBOBOX, self.OnClassFilter, self.ClassFilter) - controls_sizer.AddWindow(self.ClassFilter) - - for name, bitmap, help in [ - ("AddButton", "add_element", _("Add variable")), - ("DeleteButton", "remove_element", _("Remove variable")), - ("UpButton", "up", _("Move variable up")), - ("DownButton", "down", _("Move variable down"))]: - button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) - button.SetToolTipString(help) - setattr(self, name, button) - controls_sizer.AddWindow(button) - - self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL) - self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) - self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, - self.OnVariablesGridCellChange) - self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, - self.OnVariablesGridCellLeftClick) - self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, - self.OnVariablesGridEditorShown) - self.MainSizer.AddWindow(self.VariablesGrid, flag=wx.GROW) - - self.SetSizer(self.MainSizer) - - self.ParentWindow = window - self.Controler = controler - self.ElementType = element_type - self.Debug = debug - - self.RefreshHighlightsTimer = wx.Timer(self, -1) - self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, - self.RefreshHighlightsTimer) - - self.Filter = "All" - self.FilterChoices = [] - self.FilterChoiceTransfer = GetFilterChoiceTransfer() - - self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", DefaultType, ([], []), 0) - - if element_type in ["config", "resource"]: - self.DefaultTypes = {"All" : "Global"} - else: - self.DefaultTypes = {"All" : "Local", "Interface" : "Input", "Variables" : "Local"} - - if element_type in ["config", "resource"] \ - or element_type in ["program", "transition", "action"]: - # this is an element that can have located variables - self.Table = VariableTable(self, [], GetVariableTableColnames(True)) - - if element_type in ["config", "resource"]: - self.FilterChoices = ["All", "Global"]#,"Access"] - else: - self.FilterChoices = ["All", - "Interface", " Input", " Output", " InOut", " External", - "Variables", " Local", " Temp"]#,"Access"] - - # these condense the ColAlignements list - l = wx.ALIGN_LEFT - c = wx.ALIGN_CENTER - - # Num Name Class Type Loc Init Option Doc - self.ColSizes = [40, 80, 70, 80, 80, 80, 100, 80] - self.ColAlignements = [c, l, l, l, l, l, l, l] - - else: - # this is an element that cannot have located variables - self.Table = VariableTable(self, [], GetVariableTableColnames(False)) - - if element_type == "function": - self.FilterChoices = ["All", - "Interface", " Input", " Output", " InOut", - "Variables", " Local"] - else: - self.FilterChoices = ["All", - "Interface", " Input", " Output", " InOut", " External", - "Variables", " Local", " Temp"] - - # these condense the ColAlignements list - l = wx.ALIGN_LEFT - c = wx.ALIGN_CENTER - - # Num Name Class Type Init Option Doc - self.ColSizes = [40, 80, 70, 80, 80, 100, 160] - self.ColAlignements = [c, l, l, l, l, l, l] - - self.ElementType = element_type - self.BodyType = None - - for choice in self.FilterChoices: - self.ClassFilter.Append(_(choice)) - - reverse_transfer = {} - for filter, choice in self.FilterChoiceTransfer.items(): - reverse_transfer[choice] = filter - self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter])) - self.RefreshTypeList() - - self.VariablesGrid.SetTable(self.Table) - self.VariablesGrid.SetButtons({"Add": self.AddButton, - "Delete": self.DeleteButton, - "Up": self.UpButton, - "Down": self.DownButton}) - self.VariablesGrid.SetEditable(not self.Debug) - - def _AddVariable(new_row): - if new_row > 0: - row_content = self.Values[new_row - 1].copy() - - result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name) - if result is not None: - name = row_content.Name[:result.start(1)] - suffix = result.group(1) - if suffix != "": - start_idx = int(suffix) - else: - start_idx = 0 - else: - name = row_content.Name - start_idx = 0 - else: - row_content = None - start_idx = 0 - name = "LocalVar" - - if row_content is not None and row_content.Edit: - row_content = self.Values[new_row - 1].copy() - else: - row_content = self.DefaultValue.copy() - if self.Filter in self.DefaultTypes: - row_content.Class = self.DefaultTypes[self.Filter] - else: - row_content.Class = self.Filter - - row_content.Name = self.Controler.GenerateNewName( - self.TagName, None, name + "%d", start_idx) - - if self.Filter == "All" and len(self.Values) > 0: - self.Values.insert(new_row, row_content) - else: - self.Values.append(row_content) - new_row = self.Table.GetNumberRows() - self.SaveValues() - if self.ElementType == "resource": - self.ParentWindow.RefreshView(variablepanel = False) - self.RefreshValues() - return new_row - setattr(self.VariablesGrid, "_AddRow", _AddVariable) - - def _DeleteVariable(row): - if _GetRowEdit(row): - self.Values.remove(self.Table.GetRow(row)) - self.SaveValues() - if self.ElementType == "resource": - self.ParentWindow.RefreshView(variablepanel = False) - self.RefreshValues() - setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) - - def _MoveVariable(row, move): - if self.Filter == "All": - new_row = max(0, min(row + move, len(self.Values) - 1)) - if new_row != row: - self.Values.insert(new_row, self.Values.pop(row)) - self.SaveValues() - self.RefreshValues() - return new_row - return row - setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) - - def _GetRowEdit(row): - row_edit = False - if self: - row_edit = self.Table.GetValueByName(row, "Edit") - bodytype = self.Controler.GetEditedElementBodyType(self.TagName) - row_edit = row_edit or (bodytype in ["ST", "IL"]) - return row_edit - - def _RefreshButtons(): - if self: - table_length = len(self.Table.data) - row_class = None - row_edit = True - row = 0 - if table_length > 0: - row = self.VariablesGrid.GetGridCursorRow() - row_edit = _GetRowEdit(row) - self.AddButton.Enable(not self.Debug) - self.DeleteButton.Enable(not self.Debug and (table_length > 0 and row_edit)) - self.UpButton.Enable(not self.Debug and (table_length > 0 and row > 0 and self.Filter == "All")) - self.DownButton.Enable(not self.Debug and (table_length > 0 and row < table_length - 1 and self.Filter == "All")) - setattr(self.VariablesGrid, "RefreshButtons", _RefreshButtons) - - self.VariablesGrid.SetRowLabelSize(0) - for col in range(self.Table.GetNumberCols()): - attr = wx.grid.GridCellAttr() - attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE) - self.VariablesGrid.SetColAttr(col, attr) - self.VariablesGrid.SetColMinimalWidth(col, self.ColSizes[col]) - self.VariablesGrid.AutoSizeColumn(col, False) - - def __del__(self): - self.RefreshHighlightsTimer.Stop() - - def SetTagName(self, tagname): - self.TagName = tagname - self.BodyType = self.Controler.GetEditedElementBodyType(self.TagName) - - def GetTagName(self): - return self.TagName - - def IsFunctionBlockType(self, name): - if (isinstance(name, TupleType) or - self.ElementType != "function" and self.BodyType in ["ST", "IL"]): - return False - else: - return self.Controler.GetBlockType(name, debug=self.Debug) is not None - - def RefreshView(self): - self.PouNames = self.Controler.GetProjectPouNames(self.Debug) - returnType = None - description = None - - words = self.TagName.split("::") - if self.ElementType == "config": - self.Values = self.Controler.GetConfigurationGlobalVars(words[1], self.Debug) - elif self.ElementType == "resource": - self.Values = self.Controler.GetConfigurationResourceGlobalVars(words[1], words[2], self.Debug) - else: - if self.ElementType == "function": - self.ReturnType.Clear() - for data_type in self.Controler.GetDataTypes(self.TagName, debug=self.Debug): - self.ReturnType.Append(data_type) - returnType = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, debug=self.Debug) - description = self.Controler.GetPouDescription(words[1]) - self.Values = self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug) - - if returnType is not None: - self.ReturnType.SetStringSelection(returnType) - self.ReturnType.Enable(not self.Debug) - self.ReturnTypeLabel.Show() - self.ReturnType.Show() - else: - self.ReturnType.Enable(False) - self.ReturnTypeLabel.Hide() - self.ReturnType.Hide() - - if description is not None: - self.Description.SetValue(description) - self.Description.Enable(not self.Debug) - self.DescriptionLabel.Show() - self.Description.Show() - else: - self.Description.Enable(False) - self.DescriptionLabel.Hide() - self.Description.Hide() - - self.RefreshValues() - self.VariablesGrid.RefreshButtons() - self.MainSizer.Layout() - - def OnReturnTypeChanged(self, event): - words = self.TagName.split("::") - self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) - self.Controler.BufferProject() - self.ParentWindow.RefreshView(variablepanel = False) - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - event.Skip() - - def OnDescriptionChanged(self, event): - words = self.TagName.split("::") - old_description = self.Controler.GetPouDescription(words[1]) - new_description = self.Description.GetValue() - if new_description != old_description: - self.Controler.SetPouDescription(words[1], new_description) - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - event.Skip() - - def OnClassFilter(self, event): - self.Filter = self.FilterChoiceTransfer[VARIABLE_CHOICES_DICT[self.ClassFilter.GetStringSelection()]] - self.RefreshTypeList() - self.RefreshValues() - self.VariablesGrid.RefreshButtons() - event.Skip() - - def RefreshTypeList(self): - if self.Filter == "All": - self.ClassList = [self.FilterChoiceTransfer[choice] for choice in self.FilterChoices if self.FilterChoiceTransfer[choice] not in ["All","Interface","Variables"]] - elif self.Filter == "Interface": - self.ClassList = ["Input","Output","InOut","External"] - elif self.Filter == "Variables": - self.ClassList = ["Local","Temp"] - else: - self.ClassList = [self.Filter] - - def OnVariablesGridCellChange(self, event): - row, col = event.GetRow(), event.GetCol() - colname = self.Table.GetColLabelValue(col, False) - value = self.Table.GetValue(row, col) - message = None - - if colname == "Name" and value != "": - if not TestIdentifier(value): - message = _("\"%s\" is not a valid identifier!") % value - elif value.upper() in IEC_KEYWORDS: - message = _("\"%s\" is a keyword. It can't be used!") % value - elif value.upper() in self.PouNames: - message = _("A POU named \"%s\" already exists!") % value - elif value.upper() in [var.Name.upper() for var in self.Values if var != self.Table.data[row]]: - message = _("A variable with \"%s\" as name already exists in this pou!") % value - else: - self.SaveValues(False) - old_value = self.Table.GetOldValue() - if old_value != "": - self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) - self.Controler.BufferProject() - wx.CallAfter(self.ParentWindow.RefreshView, False) - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - else: - self.SaveValues() - if colname == "Class": - wx.CallAfter(self.ParentWindow.RefreshView, False) - elif colname == "Location": - wx.CallAfter(self.ParentWindow.RefreshView) - - if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) - dialog.ShowModal() - dialog.Destroy() - event.Veto() - else: - event.Skip() - - def BuildStdIECTypesMenu(self,type_menu): - # build a submenu containing standard IEC types - base_menu = wx.Menu(title='') - for base_type in self.Controler.GetBaseTypes(): - new_id = wx.NewId() - base_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type) - self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id) - - type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu) - - def BuildUserTypesMenu(self,type_menu): - # build a submenu containing user-defined types - datatype_menu = wx.Menu(title='') - datatypes = self.Controler.GetDataTypes(basetypes = False, confnodetypes = False) - for datatype in datatypes: - new_id = wx.NewId() - datatype_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) - self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id) - - type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu) - - def BuildLibsTypesMenu(self, type_menu): - for category in self.Controler.GetConfNodeDataTypes(): - if len(category["list"]) > 0: - # build a submenu containing confnode types - confnode_datatype_menu = wx.Menu(title='') - for datatype in category["list"]: - new_id = wx.NewId() - confnode_datatype_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) - self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id) - - type_menu.AppendMenu(wx.NewId(), category["name"], confnode_datatype_menu) - - def BuildProjectTypesMenu(self, type_menu, classtype): - # build a submenu containing function block types - bodytype = self.Controler.GetEditedElementBodyType(self.TagName) - pouname, poutype = self.Controler.GetEditedElementType(self.TagName) - if classtype in ["Input", "Output", "InOut", "External", "Global"] or \ - poutype != "function" and bodytype in ["ST", "IL"]: - functionblock_menu = wx.Menu(title='') - fbtypes = self.Controler.GetFunctionBlockTypes(self.TagName) - for functionblock_type in fbtypes: - new_id = wx.NewId() - functionblock_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) - self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) - - type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) - - def BuildArrayTypesMenu(self, type_menu): - new_id = wx.NewId() - type_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array")) - self.Bind(wx.EVT_MENU, self.VariableArrayTypeFunction, id=new_id) - - def OnVariablesGridEditorShown(self, event): - row, col = event.GetRow(), event.GetCol() - - label_value = self.Table.GetColLabelValue(col, False) - if label_value == "Type": - classtype = self.Table.GetValueByName(row, "Class") - type_menu = wx.Menu(title='') # the root menu - - self.BuildStdIECTypesMenu(type_menu) - - self.BuildUserTypesMenu(type_menu) - - self.BuildLibsTypesMenu(type_menu) - - self.BuildProjectTypesMenu(type_menu,classtype) - - self.BuildArrayTypesMenu(type_menu) - - rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col)) - corner_x = rect.x + rect.width - corner_y = rect.y + self.VariablesGrid.GetColLabelSize() - - # pop up this new menu - self.VariablesGrid.PopupMenuXY(type_menu, corner_x, corner_y) - type_menu.Destroy() - event.Veto() - else: - event.Skip() - - def GetVariableTypeFunction(self, base_type): - def VariableTypeFunction(event): - row = self.VariablesGrid.GetGridCursorRow() - self.Table.SetValueByName(row, "Type", base_type) - self.Table.ResetView(self.VariablesGrid) - self.SaveValues(False) - self.ParentWindow.RefreshView(variablepanel = False) - self.Controler.BufferProject() - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - return VariableTypeFunction - - def VariableArrayTypeFunction(self, event): - row = self.VariablesGrid.GetGridCursorRow() - dialog = ArrayTypeDialog(self, - self.Controler.GetDataTypes(self.TagName), - self.Table.GetValueByName(row, "Type")) - if dialog.ShowModal() == wx.ID_OK: - self.Table.SetValueByName(row, "Type", dialog.GetValue()) - self.Table.ResetView(self.VariablesGrid) - self.SaveValues(False) - self.ParentWindow.RefreshView(variablepanel = False) - self.Controler.BufferProject() - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - dialog.Destroy() - - def OnVariablesGridCellLeftClick(self, event): - row = event.GetRow() - if not self.Debug and (event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit")): - var_name = self.Table.GetValueByName(row, "Name") - var_class = self.Table.GetValueByName(row, "Class") - var_type = self.Table.GetValueByName(row, "Type") - data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName))) - dragSource = wx.DropSource(self.VariablesGrid) - dragSource.SetData(data) - dragSource.DoDragDrop() - event.Skip() - - def RefreshValues(self): - data = [] - for num, variable in enumerate(self.Values): - if variable.Class in self.ClassList: - variable.Number = num + 1 - data.append(variable) - self.Table.SetData(data) - self.Table.ResetView(self.VariablesGrid) - - def SaveValues(self, buffer = True): - words = self.TagName.split("::") - if self.ElementType == "config": - self.Controler.SetConfigurationGlobalVars(words[1], self.Values) - elif self.ElementType == "resource": - self.Controler.SetConfigurationResourceGlobalVars(words[1], words[2], self.Values) - else: - if self.ReturnType.IsEnabled(): - self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) - self.Controler.SetPouInterfaceVars(words[1], self.Values) - if buffer: - self.Controler.BufferProject() - self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) - -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- - - def OnRefreshHighlightsTimer(self, event): - self.Table.ResetView(self.VariablesGrid) - event.Skip() - - def AddVariableHighlight(self, infos, highlight_type): - if isinstance(infos[0], TupleType): - for i in xrange(*infos[0]): - self.Table.AddHighlight((i,) + infos[1:], highlight_type) - cell_visible = infos[0][0] - else: - self.Table.AddHighlight(infos, highlight_type) - cell_visible = infos[0] - colnames = [colname.lower() for colname in self.Table.colnames] - self.VariablesGrid.MakeCellVisible(cell_visible, colnames.index(infos[1])) - self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) - - def RemoveVariableHighlight(self, infos, highlight_type): - if isinstance(infos[0], TupleType): - for i in xrange(*infos[0]): - self.Table.RemoveHighlight((i,) + infos[1:], highlight_type) - else: - self.Table.RemoveHighlight(infos, highlight_type) - self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) - - def ClearHighlights(self, highlight_type=None): - self.Table.ClearHighlights(highlight_type) - self.Table.ResetView(self.VariablesGrid) +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import re +from types import TupleType, StringType, UnicodeType + +import wx +import wx.grid +import wx.lib.buttons + +from plcopen.structures import LOCATIONDATATYPES, TestIdentifier, IEC_KEYWORDS, DefaultType +from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD, ERROR_HIGHLIGHT +from dialogs.ArrayTypeDialog import ArrayTypeDialog +from controls.CustomGrid import CustomGrid +from controls.CustomTable import CustomTable +from controls.LocationCellEditor import LocationCellEditor +from util.BitmapLibrary import GetBitmap +from util.TranslationCatalogs import NoTranslate +from PLCControler import _VariableInfos + + +# ------------------------------------------------------------------------------- +# Helpers +# ------------------------------------------------------------------------------- + + +[ + TITLE, EDITORTOOLBAR, FILEMENU, EDITMENU, DISPLAYMENU, PROJECTTREE, + POUINSTANCEVARIABLESPANEL, LIBRARYTREE, SCALING, PAGETITLES +] = range(10) + + +def GetVariableTableColnames(location): + _ = NoTranslate + cols = ["#", + _("Name"), + _("Class"), + _("Type"), + _("Location"), + _("Initial Value"), + _("Option"), + _("Documentation")] + if not location: + del cols[4] # remove 'Location' column + return cols + + +def GetOptions(constant=True, retain=True, non_retain=True): + _ = NoTranslate + options = [""] + if constant: + options.append(_("Constant")) + if retain: + options.append(_("Retain")) + if non_retain: + options.append(_("Non-Retain")) + return options + + +OPTIONS_DICT = dict([(_(option), option) for option in GetOptions()]) + + +def GetFilterChoiceTransfer(): + _ = NoTranslate + return {_("All"): _("All"), _("Interface"): _("Interface"), + _(" Input"): _("Input"), _(" Output"): _("Output"), _(" InOut"): _("InOut"), + _(" External"): _("External"), _("Variables"): _("Variables"), _(" Local"): _("Local"), + _(" Temp"): _("Temp"), _("Global"): _("Global")} # , _("Access") : _("Access")} + + +VARIABLE_CHOICES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().iterkeys()]) +VARIABLE_CLASSES_DICT = dict([(_(_class), _class) for _class in GetFilterChoiceTransfer().itervalues()]) + +CheckOptionForClass = { + "Local": lambda x: x, + "Temp": lambda x: "", + "Input": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), + "InOut": lambda x: "", + "Output": lambda x: {"Retain": "Retain", "Non-Retain": "Non-Retain"}.get(x, ""), + "Global": lambda x: {"Constant": "Constant", "Retain": "Retain"}.get(x, ""), + "External": lambda x: {"Constant": "Constant"}.get(x, "") +} + +LOCATION_MODEL = re.compile("((?:%[IQM](?:\*|(?:[XBWLD]?[0-9]+(?:\.[0-9]+)*)))?)$") +VARIABLE_NAME_SUFFIX_MODEL = re.compile("([0-9]*)$") + + +# ------------------------------------------------------------------------------- +# Variables Panel Table +# ------------------------------------------------------------------------------- + + +class VariableTable(CustomTable): + + """ + A custom wx.grid.Grid Table using user supplied data + """ + def __init__(self, parent, data, colnames): + # The base class must be initialized *first* + CustomTable.__init__(self, parent, data, colnames) + self.old_value = None + + def GetValueByName(self, row, colname): + if row < self.GetNumberRows(): + return getattr(self.data[row], colname) + + def SetValueByName(self, row, colname, value): + if row < self.GetNumberRows(): + setattr(self.data[row], colname, value) + + def GetValue(self, row, col): + if row < self.GetNumberRows(): + if col == 0: + return self.data[row].Number + colname = self.GetColLabelValue(col, False) + if colname == "Initial Value": + colname = "InitialValue" + value = getattr(self.data[row], colname, "") + if colname == "Type" and isinstance(value, TupleType): + if value[0] == "array": + return "ARRAY [%s] OF %s" % (",".join(map("..".join, value[2])), value[1]) + if not isinstance(value, (StringType, UnicodeType)): + value = str(value) + if colname in ["Class", "Option"]: + return _(value) + return value + + def SetValue(self, row, col, value): + if col < len(self.colnames): + colname = self.GetColLabelValue(col, False) + if colname == "Name": + self.old_value = getattr(self.data[row], colname) + elif colname == "Class": + value = VARIABLE_CLASSES_DICT[value] + self.SetValueByName(row, "Option", CheckOptionForClass[value](self.GetValueByName(row, "Option"))) + if value == "External": + self.SetValueByName(row, "InitialValue", "") + elif colname == "Option": + value = OPTIONS_DICT[value] + elif colname == "Initial Value": + colname = "InitialValue" + setattr(self.data[row], colname, value) + + def GetOldValue(self): + return self.old_value + + def _GetRowEdit(self, row): + row_edit = self.GetValueByName(row, "Edit") + var_type = self.Parent.GetTagName() + bodytype = self.Parent.Controler.GetEditedElementBodyType(var_type) + if bodytype in ["ST", "IL"]: + row_edit = True + return row_edit + + def _updateColAttrs(self, grid): + """ + wx.grid.Grid -> update the column attributes to add the + appropriate renderer given the column name. + + Otherwise default to the default renderer. + """ + for row in range(self.GetNumberRows()): + var_class = self.GetValueByName(row, "Class") + var_type = self.GetValueByName(row, "Type") + row_highlights = self.Highlights.get(row, {}) + for col in range(self.GetNumberCols()): + editor = None + renderer = None + colname = self.GetColLabelValue(col, False) + if self.Parent.Debug: + grid.SetReadOnly(row, col, True) + else: + if colname == "Option": + options = GetOptions(constant=var_class in ["Local", "External", "Global"], + retain=self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output", "Global"], + non_retain=self.Parent.ElementType != "function" and var_class in ["Local", "Input", "Output"]) + if len(options) > 1: + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(",".join(map(_, options))) + else: + grid.SetReadOnly(row, col, True) + elif col != 0 and self._GetRowEdit(row): + grid.SetReadOnly(row, col, False) + if colname == "Name": + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + elif colname == "Initial Value": + if var_class not in ["External", "InOut"]: + if self.Parent.Controler.IsEnumeratedType(var_type): + editor = wx.grid.GridCellChoiceEditor() + editor.SetParameters(",".join([""] + self.Parent.Controler.GetEnumeratedDataValues(var_type))) + else: + editor = wx.grid.GridCellTextEditor() + renderer = wx.grid.GridCellStringRenderer() + else: + grid.SetReadOnly(row, col, True) + elif colname == "Location": + if var_class in ["Local", "Global"] and self.Parent.Controler.IsLocatableType(var_type): + editor = LocationCellEditor(self, self.Parent.Controler) + renderer = wx.grid.GridCellStringRenderer() + else: + grid.SetReadOnly(row, col, True) + elif colname == "Class": + if len(self.Parent.ClassList) == 1: + grid.SetReadOnly(row, col, True) + else: + editor = wx.grid.GridCellChoiceEditor() + excluded = [] + if self.Parent.IsFunctionBlockType(var_type): + excluded.extend(["Local", "Temp"]) + editor.SetParameters(",".join([_(choice) for choice in self.Parent.ClassList if choice not in excluded])) + elif colname != "Documentation": + grid.SetReadOnly(row, col, True) + + grid.SetCellEditor(row, col, editor) + grid.SetCellRenderer(row, col, renderer) + + if colname == "Location" and LOCATION_MODEL.match(self.GetValueByName(row, colname)) is None: + highlight_colours = ERROR_HIGHLIGHT + else: + highlight_colours = row_highlights.get(colname.lower(), [(wx.WHITE, wx.BLACK)])[-1] + grid.SetCellBackgroundColour(row, col, highlight_colours[0]) + grid.SetCellTextColour(row, col, highlight_colours[1]) + self.ResizeRow(grid, row) + + +# ------------------------------------------------------------------------------- +# Variable Panel Drop Target +# ------------------------------------------------------------------------------- + + +class VariableDropTarget(wx.TextDropTarget): + ''' + This allows dragging a variable location from somewhere to the Location + column of a variable row. + + The drag source should be a TextDataObject containing a Python tuple like: + ('%ID0.0.0', 'location', 'REAL') + + c_ext/CFileEditor.py has an example of this (you can drag a C extension + variable to the Location column of the variable panel). + ''' + def __init__(self, parent): + wx.TextDropTarget.__init__(self) + self.ParentWindow = parent + + def OnDropText(self, x, y, data): + self.ParentWindow.ParentWindow.Select() + x, y = self.ParentWindow.VariablesGrid.CalcUnscrolledPosition(x, y) + col = self.ParentWindow.VariablesGrid.XToCol(x) + row = self.ParentWindow.VariablesGrid.YToRow(y) + message = None + element_type = self.ParentWindow.ElementType + try: + values = eval(data) + except Exception: + message = _("Invalid value \"%s\" for variable grid element") % data + values = None + if not isinstance(values, TupleType): + message = _("Invalid value \"%s\" for variable grid element") % data + values = None + if values is not None: + if col != wx.NOT_FOUND and row != wx.NOT_FOUND: + colname = self.ParentWindow.Table.GetColLabelValue(col, False) + if colname == "Location" and values[1] == "location": + if not self.ParentWindow.Table.GetValueByName(row, "Edit"): + message = _("Can't give a location to a function block instance") + elif self.ParentWindow.Table.GetValueByName(row, "Class") not in ["Local", "Global"]: + message = _("Can only give a location to local or global variables") + else: + location = values[0] + variable_type = self.ParentWindow.Table.GetValueByName(row, "Type") + base_type = self.ParentWindow.Controler.GetBaseType(variable_type) + + if values[2] is not None: + base_location_type = self.ParentWindow.Controler.GetBaseType(values[2]) + if values[2] != variable_type and base_type != base_location_type: + message = _("Incompatible data types between \"{a1}\" and \"{a2}\"").\ + format(a1=values[2], a2=variable_type) + + if message is None: + if not location.startswith("%"): + if location[0].isdigit() and base_type != "BOOL": + message = _("Incompatible size of data between \"%s\" and \"BOOL\"") % location + elif location[0] not in LOCATIONDATATYPES: + message = _("Unrecognized data size \"%s\"") % location[0] + elif base_type not in LOCATIONDATATYPES[location[0]]: + message = _("Incompatible size of data between \"{a1}\" and \"{a2}\"").\ + format(a1=location, a2=variable_type) + else: + dialog = wx.SingleChoiceDialog( + self.ParentWindow.ParentWindow.ParentWindow, + _("Select a variable class:"), + _("Variable class"), + [_("Input"), _("Output"), _("Memory")], + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) + if dialog.ShowModal() == wx.ID_OK: + selected = dialog.GetSelection() + else: + selected = None + dialog.Destroy() + if selected is None: + return + if selected == 0: + location = "%I" + location + elif selected == 1: + location = "%Q" + location + else: + location = "%M" + location + + if message is None: + self.ParentWindow.Table.SetValue(row, col, location) + self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid) + self.ParentWindow.SaveValues() + elif colname == "Initial Value" and values[1] == "Constant": + if not self.ParentWindow.Table.GetValueByName(row, "Edit"): + message = _("Can't set an initial value to a function block instance") + else: + self.ParentWindow.Table.SetValue(row, col, values[0]) + self.ParentWindow.Table.ResetView(self.ParentWindow.VariablesGrid) + self.ParentWindow.SaveValues() + elif (element_type not in ["config", "resource", "function"] and values[1] == "Global" and + self.ParentWindow.Filter in ["All", "Interface", "External"] or + element_type != "function" and values[1] in ["location", "NamedConstant"]): + if values[1] in ["location", "NamedConstant"]: + var_name = values[3] + else: + var_name = values[0] + tagname = self.ParentWindow.GetTagName() + dlg = wx.TextEntryDialog( + self.ParentWindow.ParentWindow.ParentWindow, + _("Confirm or change variable name"), + _('Variable Drop'), var_name) + dlg.SetValue(var_name) + var_name = dlg.GetValue() if dlg.ShowModal() == wx.ID_OK else None + dlg.Destroy() + if var_name is None: + return + elif var_name.upper() in [ + name.upper() for name in + self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: + message = _("\"%s\" pou already exists!") % var_name + elif not var_name.upper() in [ + name.upper() + for name in self.ParentWindow.Controler. + GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: + var_infos = self.ParentWindow.DefaultValue.copy() + var_infos.Name = var_name + var_infos.Type = values[2] + var_infos.Documentation = values[4] + if values[1] == "location": + location = values[0] + if not location.startswith("%"): + dialog = wx.SingleChoiceDialog( + self.ParentWindow.ParentWindow.ParentWindow, + _("Select a variable class:"), + _("Variable class"), + [_("Input"), _("Output"), _("Memory")], + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) + if dialog.ShowModal() == wx.ID_OK: + selected = dialog.GetSelection() + else: + selected = None + dialog.Destroy() + if selected is None: + return + if selected == 0: + location = "%I" + location + elif selected == 1: + location = "%Q" + location + else: + location = "%M" + location + if element_type == "functionBlock": + configs = self.ParentWindow.Controler.GetProjectConfigNames( + self.ParentWindow.Debug) + if len(configs) == 0: + return + if not var_name.upper() in [ + name.upper() for name in + self.ParentWindow.Controler.GetConfigurationVariableNames(configs[0])]: + self.ParentWindow.Controler.AddConfigurationGlobalVar( + configs[0], values[2], var_name, location, "") + var_infos.Class = "External" + else: + if element_type == "program": + var_infos.Class = "Local" + else: + var_infos.Class = "Global" + var_infos.Location = location + elif values[1] == "NamedConstant": + if element_type in ["functionBlock", "program"]: + var_infos.Class = "Local" + var_infos.InitialValue = values[0] + else: + return + else: + var_infos.Class = "External" + var_infos.Number = len(self.ParentWindow.Values) + self.ParentWindow.Values.append(var_infos) + self.ParentWindow.SaveValues() + self.ParentWindow.RefreshValues() + else: + message = _("\"%s\" element for this pou already exists!") % var_name + + if message is not None: + wx.CallAfter(self.ShowMessage, message) + + def ShowMessage(self, message): + message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) + message.ShowModal() + message.Destroy() + + +# ------------------------------------------------------------------------------- +# Variable Panel +# ------------------------------------------------------------------------------- + + +class VariablePanel(wx.Panel): + + def __init__(self, parent, window, controler, element_type, debug=False): + wx.Panel.__init__(self, parent, style=wx.TAB_TRAVERSAL) + + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=10, rows=2, vgap=0) + self.MainSizer.AddGrowableCol(0) + self.MainSizer.AddGrowableRow(1) + + controls_sizer = wx.FlexGridSizer(cols=10, hgap=5, rows=1, vgap=5) + controls_sizer.AddGrowableCol(5) + controls_sizer.AddGrowableRow(0) + self.MainSizer.AddSizer(controls_sizer, border=5, flag=wx.GROW | wx.ALL) + + self.ReturnTypeLabel = wx.StaticText(self, label=_('Return Type:')) + controls_sizer.AddWindow(self.ReturnTypeLabel, flag=wx.ALIGN_CENTER_VERTICAL) + + self.ReturnType = wx.ComboBox(self, + size=wx.Size(145, -1), style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnReturnTypeChanged, self.ReturnType) + controls_sizer.AddWindow(self.ReturnType) + + self.DescriptionLabel = wx.StaticText(self, label=_('Description:')) + controls_sizer.AddWindow(self.DescriptionLabel, flag=wx.ALIGN_CENTER_VERTICAL) + + self.Description = wx.TextCtrl(self, + size=wx.Size(250, -1), style=wx.TE_PROCESS_ENTER) + self.Bind(wx.EVT_TEXT_ENTER, self.OnDescriptionChanged, self.Description) + self.Description.Bind(wx.EVT_KILL_FOCUS, self.OnDescriptionChanged) + controls_sizer.AddWindow(self.Description) + + class_filter_label = wx.StaticText(self, label=_('Class Filter:')) + controls_sizer.AddWindow(class_filter_label, flag=wx.ALIGN_CENTER_VERTICAL) + + self.ClassFilter = wx.ComboBox(self, + size=wx.Size(145, -1), style=wx.CB_READONLY) + self.Bind(wx.EVT_COMBOBOX, self.OnClassFilter, self.ClassFilter) + controls_sizer.AddWindow(self.ClassFilter) + + for name, bitmap, help in [ + ("AddButton", "add_element", _("Add variable")), + ("DeleteButton", "remove_element", _("Remove variable")), + ("UpButton", "up", _("Move variable up")), + ("DownButton", "down", _("Move variable down"))]: + button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) + button.SetToolTipString(help) + setattr(self, name, button) + controls_sizer.AddWindow(button) + + self.VariablesGrid = CustomGrid(self, style=wx.VSCROLL | wx.HSCROLL) + self.VariablesGrid.SetDropTarget(VariableDropTarget(self)) + self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.OnVariablesGridCellChange) + self.VariablesGrid.Bind(wx.grid.EVT_GRID_CELL_LEFT_CLICK, + self.OnVariablesGridCellLeftClick) + self.VariablesGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, + self.OnVariablesGridEditorShown) + self.MainSizer.AddWindow(self.VariablesGrid, flag=wx.GROW) + + self.SetSizer(self.MainSizer) + + self.ParentWindow = window + self.Controler = controler + self.ElementType = element_type + self.Debug = debug + + self.RefreshHighlightsTimer = wx.Timer(self, -1) + self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, + self.RefreshHighlightsTimer) + + self.Filter = "All" + self.FilterChoices = [] + self.FilterChoiceTransfer = GetFilterChoiceTransfer() + + self.DefaultValue = _VariableInfos("", "", "", "", "", True, "", DefaultType, ([], []), 0) + + if element_type in ["config", "resource"]: + self.DefaultTypes = {"All": "Global"} + else: + self.DefaultTypes = {"All": "Local", "Interface": "Input", "Variables": "Local"} + + if element_type in ["config", "resource"] \ + or element_type in ["program", "transition", "action"]: + # this is an element that can have located variables + self.Table = VariableTable(self, [], GetVariableTableColnames(True)) + + if element_type in ["config", "resource"]: + self.FilterChoices = ["All", "Global"] # ,"Access"] + else: + self.FilterChoices = ["All", + "Interface", " Input", " Output", " InOut", " External", + "Variables", " Local", " Temp"] # ,"Access"] + + # these condense the ColAlignements list + left = wx.ALIGN_LEFT + center = wx.ALIGN_CENTER + + # Num Name Class Type Loc Init Option Doc + self.ColSettings = { + "size": [40, 80, 100, 80, 110, 120, 100, 160], + "alignement": [center, left, left, left, left, left, left, left], + "fixed_size": [True, False, True, False, True, True, True, False], + } + + else: + # this is an element that cannot have located variables + self.Table = VariableTable(self, [], GetVariableTableColnames(False)) + + if element_type == "function": + self.FilterChoices = ["All", + "Interface", " Input", " Output", " InOut", + "Variables", " Local"] + else: + self.FilterChoices = ["All", + "Interface", " Input", " Output", " InOut", " External", + "Variables", " Local", " Temp"] + + # these condense the alignements list + left = wx.ALIGN_LEFT + center = wx.ALIGN_CENTER + + # Num Name Class Type Init Option Doc + self.ColSettings = { + "size": [40, 80, 100, 80, 120, 100, 160], + "alignement": [center, left, left, left, left, left, left], + "fixed_size": [True, False, True, False, True, True, False], + } + + self.PanelWidthMin = sum(self.ColSettings["size"]) + + self.ElementType = element_type + self.BodyType = None + + for choice in self.FilterChoices: + self.ClassFilter.Append(_(choice)) + + reverse_transfer = {} + for filter, choice in self.FilterChoiceTransfer.items(): + reverse_transfer[choice] = filter + self.ClassFilter.SetStringSelection(_(reverse_transfer[self.Filter])) + self.RefreshTypeList() + + self.VariablesGrid.SetTable(self.Table) + self.VariablesGrid.SetButtons({"Add": self.AddButton, + "Delete": self.DeleteButton, + "Up": self.UpButton, + "Down": self.DownButton}) + self.VariablesGrid.SetEditable(not self.Debug) + + def _AddVariable(new_row): + if new_row > 0: + row_content = self.Values[new_row - 1].copy() + + result = VARIABLE_NAME_SUFFIX_MODEL.search(row_content.Name) + if result is not None: + name = row_content.Name[:result.start(1)] + suffix = result.group(1) + if suffix != "": + start_idx = int(suffix) + else: + start_idx = 0 + else: + name = row_content.Name + start_idx = 0 + else: + row_content = None + start_idx = 0 + name = "LocalVar" + + if row_content is not None and row_content.Edit: + row_content = self.Values[new_row - 1].copy() + else: + row_content = self.DefaultValue.copy() + if self.Filter in self.DefaultTypes: + row_content.Class = self.DefaultTypes[self.Filter] + else: + row_content.Class = self.Filter + + row_content.Name = self.Controler.GenerateNewName( + self.TagName, None, name + "%d", start_idx) + + if self.Filter == "All" and len(self.Values) > 0: + self.Values.insert(new_row, row_content) + else: + self.Values.append(row_content) + new_row = self.Table.GetNumberRows() + self.SaveValues() + if self.ElementType == "resource": + self.ParentWindow.RefreshView(variablepanel=False) + self.RefreshValues() + return new_row + setattr(self.VariablesGrid, "_AddRow", _AddVariable) + + def _DeleteVariable(row): + if _GetRowEdit(row): + self.Values.remove(self.Table.GetRow(row)) + self.SaveValues() + if self.ElementType == "resource": + self.ParentWindow.RefreshView(variablepanel=False) + self.RefreshValues() + setattr(self.VariablesGrid, "_DeleteRow", _DeleteVariable) + + def _MoveVariable(row, move): + if self.Filter == "All": + new_row = max(0, min(row + move, len(self.Values) - 1)) + if new_row != row: + self.Values.insert(new_row, self.Values.pop(row)) + self.SaveValues() + self.RefreshValues() + return new_row + return row + setattr(self.VariablesGrid, "_MoveRow", _MoveVariable) + + def _GetRowEdit(row): + row_edit = False + if self: + row_edit = self.Table.GetValueByName(row, "Edit") + bodytype = self.Controler.GetEditedElementBodyType(self.TagName) + row_edit = row_edit or (bodytype in ["ST", "IL"]) + return row_edit + + def _RefreshButtons(): + if self: + table_length = len(self.Table.data) + row_edit = True + row = 0 + if table_length > 0: + row = self.VariablesGrid.GetGridCursorRow() + row_edit = _GetRowEdit(row) + self.AddButton.Enable(not self.Debug) + self.DeleteButton.Enable(not self.Debug and (table_length > 0 and row_edit)) + self.UpButton.Enable(not self.Debug and (table_length > 0 and row > 0 and self.Filter == "All")) + self.DownButton.Enable(not self.Debug and (table_length > 0 and row < table_length - 1 and self.Filter == "All")) + setattr(self.VariablesGrid, "RefreshButtons", _RefreshButtons) + + panel_width = window.Parent.ScreenRect.Width - 35 + if panel_width > self.PanelWidthMin: + stretch_cols_width = panel_width + stretch_cols_sum = 0 + for col in range(len(self.ColSettings["fixed_size"])): + if self.ColSettings["fixed_size"][col]: + stretch_cols_width -= self.ColSettings["size"][col] + else: + stretch_cols_sum += self.ColSettings["size"][col] + + self.VariablesGrid.SetRowLabelSize(0) + for col in range(self.Table.GetNumberCols()): + attr = wx.grid.GridCellAttr() + attr.SetAlignment(self.ColSettings["alignement"][col], wx.ALIGN_CENTRE) + self.VariablesGrid.SetColAttr(col, attr) + self.VariablesGrid.SetColMinimalWidth(col, self.ColSettings["size"][col]) + if (panel_width > self.PanelWidthMin) and not self.ColSettings["fixed_size"][col]: + self.VariablesGrid.SetColSize(col, int((float(self.ColSettings["size"][col])/stretch_cols_sum)*stretch_cols_width)) + else: + self.VariablesGrid.SetColSize(col, self.ColSettings["size"][col]) + + def __del__(self): + self.RefreshHighlightsTimer.Stop() + + def SetTagName(self, tagname): + self.TagName = tagname + self.BodyType = self.Controler.GetEditedElementBodyType(self.TagName) + + def GetTagName(self): + return self.TagName + + def IsFunctionBlockType(self, name): + if isinstance(name, TupleType) or \ + self.ElementType != "function" and self.BodyType in ["ST", "IL"]: + return False + else: + return self.Controler.GetBlockType(name, debug=self.Debug) is not None + + def RefreshView(self): + self.PouNames = self.Controler.GetProjectPouNames(self.Debug) + returnType = None + description = None + + words = self.TagName.split("::") + if self.ElementType == "config": + self.Values = self.Controler.GetConfigurationGlobalVars(words[1], self.Debug) + elif self.ElementType == "resource": + self.Values = self.Controler.GetConfigurationResourceGlobalVars(words[1], words[2], self.Debug) + else: + if self.ElementType == "function": + self.ReturnType.Clear() + for data_type in self.Controler.GetDataTypes(self.TagName, debug=self.Debug): + self.ReturnType.Append(data_type) + returnType = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, debug=self.Debug) + description = self.Controler.GetPouDescription(words[1]) + self.Values = self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug) + + if returnType is not None: + self.ReturnType.SetStringSelection(returnType) + self.ReturnType.Enable(not self.Debug) + self.ReturnTypeLabel.Show() + self.ReturnType.Show() + else: + self.ReturnType.Enable(False) + self.ReturnTypeLabel.Hide() + self.ReturnType.Hide() + + if description is not None: + self.Description.SetValue(description) + self.Description.Enable(not self.Debug) + self.DescriptionLabel.Show() + self.Description.Show() + else: + self.Description.Enable(False) + self.DescriptionLabel.Hide() + self.Description.Hide() + + self.RefreshValues() + self.VariablesGrid.RefreshButtons() + self.MainSizer.Layout() + + def OnReturnTypeChanged(self, event): + words = self.TagName.split("::") + self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) + self.Controler.BufferProject() + self.ParentWindow.RefreshView(variablepanel=False) + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + event.Skip() + + def OnDescriptionChanged(self, event): + words = self.TagName.split("::") + old_description = self.Controler.GetPouDescription(words[1]) + new_description = self.Description.GetValue() + if new_description != old_description: + self.Controler.SetPouDescription(words[1], new_description) + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + event.Skip() + + def OnClassFilter(self, event): + self.Filter = self.FilterChoiceTransfer[VARIABLE_CHOICES_DICT[self.ClassFilter.GetStringSelection()]] + self.RefreshTypeList() + self.RefreshValues() + self.VariablesGrid.RefreshButtons() + event.Skip() + + def RefreshTypeList(self): + if self.Filter == "All": + self.ClassList = [self.FilterChoiceTransfer[choice] for choice in self.FilterChoices if self.FilterChoiceTransfer[choice] not in ["All", "Interface", "Variables"]] + elif self.Filter == "Interface": + self.ClassList = ["Input", "Output", "InOut", "External"] + elif self.Filter == "Variables": + self.ClassList = ["Local", "Temp"] + else: + self.ClassList = [self.Filter] + + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + + def OnVariablesGridCellChange(self, event): + row, col = event.GetRow(), event.GetCol() + colname = self.Table.GetColLabelValue(col, False) + value = self.Table.GetValue(row, col) + message = None + + if colname == "Name" and value != "": + if not TestIdentifier(value): + message = _("\"%s\" is not a valid identifier!") % value + elif value.upper() in IEC_KEYWORDS: + message = _("\"%s\" is a keyword. It can't be used!") % value + elif value.upper() in self.PouNames: + message = _("A POU named \"%s\" already exists!") % value + elif value.upper() in [var.Name.upper() for var in self.Values if var != self.Table.data[row]]: + message = _("A variable with \"%s\" as name already exists in this pou!") % value + else: + self.SaveValues(False) + old_value = self.Table.GetOldValue() + if old_value != "": + self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) + self.Controler.BufferProject() + wx.CallAfter(self.ParentWindow.RefreshView, False) + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + else: + self.SaveValues() + if colname == "Class": + self.ClearLocation(row, col, value) + wx.CallAfter(self.ParentWindow.RefreshView) + elif colname == "Location": + wx.CallAfter(self.ParentWindow.RefreshView) + + if message is not None: + wx.CallAfter(self.ShowErrorMessage, message) + event.Veto() + else: + event.Skip() + + def ClearLocation(self, row, col, value): + if self.Values[row].Location != '': + if self.Table.GetColLabelValue(col, False) == 'Class' and value not in ["Local", "Global"] or \ + self.Table.GetColLabelValue(col, False) == 'Type' and not self.Controler.IsLocatableType(value): + self.Values[row].Location = '' + self.RefreshValues() + self.SaveValues() + + def BuildStdIECTypesMenu(self, type_menu): + # build a submenu containing standard IEC types + base_menu = wx.Menu(title='') + for base_type in self.Controler.GetBaseTypes(): + new_id = wx.NewId() + base_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=base_type) + self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(base_type), id=new_id) + + type_menu.AppendMenu(wx.NewId(), _("Base Types"), base_menu) + + def BuildUserTypesMenu(self, type_menu): + # build a submenu containing user-defined types + datatype_menu = wx.Menu(title='') + datatypes = self.Controler.GetDataTypes(basetypes=False, confnodetypes=False) + for datatype in datatypes: + new_id = wx.NewId() + datatype_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) + self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id) + + type_menu.AppendMenu(wx.NewId(), _("User Data Types"), datatype_menu) + + def BuildLibsTypesMenu(self, type_menu): + for category in self.Controler.GetConfNodeDataTypes(): + if len(category["list"]) > 0: + # build a submenu containing confnode types + confnode_datatype_menu = wx.Menu(title='') + for datatype in category["list"]: + new_id = wx.NewId() + confnode_datatype_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=datatype) + self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(datatype), id=new_id) + + type_menu.AppendMenu(wx.NewId(), category["name"], confnode_datatype_menu) + + def BuildProjectTypesMenu(self, type_menu, classtype): + # build a submenu containing function block types + bodytype = self.Controler.GetEditedElementBodyType(self.TagName) + _pouname, poutype = self.Controler.GetEditedElementType(self.TagName) + if classtype in ["Input", "Output", "InOut", "External", "Global"] or \ + poutype != "function" and bodytype in ["ST", "IL"]: + functionblock_menu = wx.Menu(title='') + fbtypes = self.Controler.GetFunctionBlockTypes(self.TagName) + for functionblock_type in fbtypes: + new_id = wx.NewId() + functionblock_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) + self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) + + type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) + + def BuildArrayTypesMenu(self, type_menu): + new_id = wx.NewId() + type_menu.Append(help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array")) + self.Bind(wx.EVT_MENU, self.VariableArrayTypeFunction, id=new_id) + + def OnVariablesGridEditorShown(self, event): + row, col = event.GetRow(), event.GetCol() + + label_value = self.Table.GetColLabelValue(col, False) + if label_value == "Type": + old_value = self.Values[row].Type + classtype = self.Table.GetValueByName(row, "Class") + type_menu = wx.Menu(title='') # the root menu + + self.BuildStdIECTypesMenu(type_menu) + + self.BuildUserTypesMenu(type_menu) + + self.BuildLibsTypesMenu(type_menu) + + self.BuildProjectTypesMenu(type_menu, classtype) + + self.BuildArrayTypesMenu(type_menu) + + rect = self.VariablesGrid.BlockToDeviceRect((row, col), (row, col)) + corner_x = rect.x + rect.width + corner_y = rect.y + self.VariablesGrid.GetColLabelSize() + + # pop up this new menu + self.VariablesGrid.PopupMenuXY(type_menu, corner_x, corner_y) + type_menu.Destroy() + event.Veto() + value = self.Values[row].Type + if old_value != value: + self.ClearLocation(row, col, value) + else: + event.Skip() + + def GetVariableTypeFunction(self, base_type): + def VariableTypeFunction(event): + row = self.VariablesGrid.GetGridCursorRow() + self.Table.SetValueByName(row, "Type", base_type) + self.Table.ResetView(self.VariablesGrid) + self.SaveValues(False) + self.ParentWindow.RefreshView(variablepanel=False) + self.Controler.BufferProject() + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + return VariableTypeFunction + + def VariableArrayTypeFunction(self, event): + row = self.VariablesGrid.GetGridCursorRow() + dialog = ArrayTypeDialog(self, + self.Controler.GetDataTypes(self.TagName), + self.Table.GetValueByName(row, "Type")) + if dialog.ShowModal() == wx.ID_OK: + self.Table.SetValueByName(row, "Type", dialog.GetValue()) + self.Table.ResetView(self.VariablesGrid) + self.SaveValues(False) + self.ParentWindow.RefreshView(variablepanel=False) + self.Controler.BufferProject() + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + dialog.Destroy() + + def OnVariablesGridCellLeftClick(self, event): + row = event.GetRow() + if not self.Debug and (event.GetCol() == 0 and self.Table.GetValueByName(row, "Edit")): + var_name = self.Table.GetValueByName(row, "Name") + var_class = self.Table.GetValueByName(row, "Class") + var_type = self.Table.GetValueByName(row, "Type") + data = wx.TextDataObject(str((var_name, var_class, var_type, self.TagName))) + dragSource = wx.DropSource(self.VariablesGrid) + dragSource.SetData(data) + dragSource.DoDragDrop() + event.Skip() + + def RefreshValues(self): + data = [] + for num, variable in enumerate(self.Values): + if variable.Class in self.ClassList: + variable.Number = num + 1 + data.append(variable) + self.Table.SetData(data) + self.Table.ResetView(self.VariablesGrid) + + def SaveValues(self, buffer=True): + words = self.TagName.split("::") + if self.ElementType == "config": + self.Controler.SetConfigurationGlobalVars(words[1], self.Values) + elif self.ElementType == "resource": + self.Controler.SetConfigurationResourceGlobalVars(words[1], words[2], self.Values) + else: + if self.ReturnType.IsEnabled(): + self.Controler.SetPouInterfaceReturnType(words[1], self.ReturnType.GetStringSelection()) + self.Controler.SetPouInterfaceVars(words[1], self.Values) + if buffer: + self.Controler.BufferProject() + self.ParentWindow._Refresh(TITLE, FILEMENU, EDITMENU, PAGETITLES, POUINSTANCEVARIABLESPANEL, LIBRARYTREE) + + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- + + def OnRefreshHighlightsTimer(self, event): + self.Table.ResetView(self.VariablesGrid) + event.Skip() + + def AddVariableHighlight(self, infos, highlight_type): + if isinstance(infos[0], TupleType): + for i in xrange(*infos[0]): + self.Table.AddHighlight((i,) + infos[1:], highlight_type) + cell_visible = infos[0][0] + else: + self.Table.AddHighlight(infos, highlight_type) + cell_visible = infos[0] + colnames = [colname.lower() for colname in self.Table.colnames] + self.VariablesGrid.MakeCellVisible(cell_visible, colnames.index(infos[1])) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def RemoveVariableHighlight(self, infos, highlight_type): + if isinstance(infos[0], TupleType): + for i in xrange(*infos[0]): + self.Table.RemoveHighlight((i,) + infos[1:], highlight_type) + else: + self.Table.RemoveHighlight(infos, highlight_type) + self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) + + def ClearHighlights(self, highlight_type=None): + self.Table.ClearHighlights(highlight_type) + self.Table.ResetView(self.VariablesGrid) diff -r c1298e7ffe3a -r 8391c11477f4 controls/__init__.py --- a/controls/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/controls/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -24,20 +24,23 @@ # Package initialization -from CustomEditableListBox import CustomEditableListBox -from CustomGrid import CustomGrid -from CustomTable import CustomTable -from CustomTree import CustomTree -from DebugVariablePanel import DebugVariablePanel -from DurationCellEditor import DurationCellEditor -from LibraryPanel import LibraryPanel -from LocationCellEditor import LocationCellEditor -from PouInstanceVariablesPanel import PouInstanceVariablesPanel -from ProjectPropertiesPanel import ProjectPropertiesPanel -from VariablePanel import VariablePanel -from SearchResultPanel import SearchResultPanel -from TextCtrlAutoComplete import TextCtrlAutoComplete -from FolderTree import FolderTree -from LogViewer import LogViewer -from CustomStyledTextCtrl import CustomStyledTextCtrl -from CustomToolTip import CustomToolTip +from __future__ import absolute_import + +from controls.CustomEditableListBox import CustomEditableListBox +from controls.CustomGrid import CustomGrid +from controls.CustomTable import CustomTable +from controls.CustomTree import CustomTree +from controls.CustomIntCtrl import CustomIntCtrl +from controls.DebugVariablePanel import DebugVariablePanel +from controls.DurationCellEditor import DurationCellEditor +from controls.LibraryPanel import LibraryPanel +from controls.LocationCellEditor import LocationCellEditor +from controls.PouInstanceVariablesPanel import PouInstanceVariablesPanel +from controls.ProjectPropertiesPanel import ProjectPropertiesPanel +from controls.VariablePanel import VariablePanel +from controls.SearchResultPanel import SearchResultPanel +from controls.TextCtrlAutoComplete import TextCtrlAutoComplete +from controls.FolderTree import FolderTree +from controls.LogViewer import LogViewer +from controls.CustomStyledTextCtrl import CustomStyledTextCtrl +from controls.CustomToolTip import CustomToolTip diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/AboutDialog.py --- a/dialogs/AboutDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/AboutDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -30,15 +30,13 @@ This module contains classes extended from wx.Dialog used by the GUI. """ + +from __future__ import absolute_import import os -import sys -import time import wx from wx.lib.agw.hyperlink import HyperLinkCtrl -#---------------------------------------------------------------------- - class AboutDialog(wx.Dialog): """ A replacement About Dialog for Windows, as it uses a generic frame that @@ -106,8 +104,6 @@ CreditsDialog(self, self.info) -#---------------------------------------------------------------------- - class CreditsDialog(wx.Dialog): def __init__(self, parent, info): wx.Dialog.__init__(self, parent, title=_("Credits"), size=(475, 320), @@ -144,8 +140,6 @@ close.Bind(wx.EVT_BUTTON, lambda evt: self.Destroy()) -#---------------------------------------------------------------------- - class LicenseDialog(wx.Dialog): def __init__(self, parent, info): wx.Dialog.__init__(self, parent, title=_("License"), size=(500, 400), @@ -173,7 +167,6 @@ close.Bind(wx.EVT_BUTTON, lambda evt: self.Destroy()) -#---------------------------------------------------------------------- def ShowAboutDialog(parent, info): if os.name == "nt": diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/ActionBlockDialog.py --- a/dialogs/ActionBlockDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/ActionBlockDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -4,6 +4,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -21,32 +22,37 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.grid import wx.lib.buttons from controls import CustomGrid, CustomTable +from PLCControler import _ActionInfos from util.BitmapLibrary import GetBitmap -from PLCControler import _ActionInfos - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetActionTableColnames(): - _ = lambda x: x + _ = NoTranslate return [_("Qualifier"), _("Duration"), _("Type"), _("Value"), _("Indicator")] + def GetTypeList(): - _ = lambda x: x + _ = NoTranslate return [_("Action"), _("Variable"), _("Inline")] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Action Table -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ActionTable(CustomTable): - + def GetValue(self, row, col): if row < self.GetNumberRows(): colname = self.GetColLabelValue(col, False) @@ -54,7 +60,7 @@ if colname == "Type": return _(value) return value - + def SetValue(self, row, col, value): if col < len(self.colnames): colname = self.GetColLabelValue(col, False) @@ -63,7 +69,7 @@ elif colname == "Qualifier" and not self.Parent.DurationList[value]: self.data[row].duration = "" setattr(self.data[row], colname.lower(), value) - + def _updateColAttrs(self, grid): """ wx.Grid -> update the column attributes to add the @@ -71,7 +77,7 @@ Otherwise default to the default renderer. """ - + for row in range(self.GetNumberRows()): for col in range(self.GetNumberCols()): editor = None @@ -102,70 +108,71 @@ elif colname == "Indicator": editor = wx.grid.GridCellChoiceEditor() editor.SetParameters(self.Parent.VariableList) - + grid.SetCellEditor(row, col, editor) grid.SetCellRenderer(row, col, renderer) grid.SetReadOnly(row, col, readonly) - + grid.SetCellBackgroundColour(row, col, wx.WHITE) self.ResizeRow(grid, row) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Action Block Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ActionBlockDialog(wx.Dialog): - + def __init__(self, parent): - wx.Dialog.__init__(self, parent, - size=wx.Size(500, 300), title=_('Edit action block properties')) - + wx.Dialog.__init__(self, parent, title=_('Edit action block properties')) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + top_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) top_sizer.AddGrowableCol(0) top_sizer.AddGrowableRow(0) main_sizer.AddSizer(top_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + actions_label = wx.StaticText(self, label=_('Actions:')) top_sizer.AddWindow(actions_label, flag=wx.ALIGN_BOTTOM) - + for name, bitmap, help in [ ("AddButton", "add_element", _("Add action")), ("DeleteButton", "remove_element", _("Remove action")), ("UpButton", "up", _("Move action up")), ("DownButton", "down", _("Move action down"))]: - button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + button = wx.lib.buttons.GenBitmapButton( + self, bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) top_sizer.AddWindow(button) - - self.ActionsGrid = CustomGrid(self, size=wx.Size(0, 0), style=wx.VSCROLL) + + self.ActionsGrid = CustomGrid(self, size=wx.Size(-1, 250), style=wx.VSCROLL) self.ActionsGrid.DisableDragGridSize() self.ActionsGrid.EnableScrolling(False, True) - self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, + self.ActionsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnActionsGridCellChange) main_sizer.AddSizer(self.ActionsGrid, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + flag=wx.GROW | wx.LEFT | wx.RIGHT) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + self.Table = ActionTable(self, [], GetActionTableColnames()) - typelist = GetTypeList() - self.TypeList = ",".join(map(_,typelist)) + typelist = GetTypeList() + self.TypeList = ",".join(map(_, typelist)) self.TranslateType = dict([(_(value), value) for value in typelist]) - self.ColSizes = [60, 90, 80, 110, 80] + self.ColSizes = [60, 90, 130, 200, 50] self.ColAlignements = [wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT] - + self.ActionsGrid.SetTable(self.Table) self.ActionsGrid.SetDefaultValue(_ActionInfos("N", "Action", "", "", "")) self.ActionsGrid.SetButtons({"Add": self.AddButton, @@ -173,18 +180,19 @@ "Up": self.UpButton, "Down": self.DownButton}) self.ActionsGrid.SetRowLabelSize(0) - + for col in range(self.Table.GetNumberCols()): attr = wx.grid.GridCellAttr() attr.SetAlignment(self.ColAlignements[col], wx.ALIGN_CENTRE) self.ActionsGrid.SetColAttr(col, attr) self.ActionsGrid.SetColMinimalWidth(col, self.ColSizes[col]) self.ActionsGrid.AutoSizeColumn(col, False) - + self.Table.ResetView(self.ActionsGrid) self.ActionsGrid.SetFocus() self.ActionsGrid.RefreshButtons() - + self.Fit() + def OnOK(self, event): self.ActionsGrid.CloseEditControl() self.EndModal(wx.ID_OK) @@ -192,14 +200,14 @@ def OnActionsGridCellChange(self, event): wx.CallAfter(self.Table.ResetView, self.ActionsGrid) event.Skip() - + def SetQualifierList(self, list): self.QualifierList = ",".join(list) self.DurationList = list def SetVariableList(self, list): self.VariableList = "," + ",".join([variable.Name for variable in list]) - + def SetActionList(self, list): self.ActionList = "," + ",".join(list) @@ -217,7 +225,7 @@ if len(actions) > 0: self.ActionsGrid.SetGridCursor(0, 0) self.ActionsGrid.RefreshButtons() - + def GetValues(self): actions = self.Table.GetData() for action in actions: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/ArrayTypeDialog.py --- a/dialogs/ArrayTypeDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/ArrayTypeDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -21,6 +21,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re from types import TupleType @@ -28,92 +30,100 @@ from controls import CustomEditableListBox -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Array Type Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ArrayTypeDialog(wx.Dialog): - + def __init__(self, parent, datatypes, infos): - wx.Dialog.__init__(self, parent, - size=wx.Size(500, 350), title=_('Edit array type properties')) - + wx.Dialog.__init__(self, parent, title=_('Edit array type properties')) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + top_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(top_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(top_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + basetype_label = wx.StaticText(self, label=_('Base Type:')) top_sizer.AddWindow(basetype_label, 1, flag=wx.ALIGN_BOTTOM) - + self.BaseType = wx.ComboBox(self, style=wx.CB_READONLY) top_sizer.AddWindow(self.BaseType, 1, flag=wx.GROW) - - self.Dimensions = CustomEditableListBox(self, label=_("Dimensions:"), - style=wx.gizmos.EL_ALLOW_NEW| - wx.gizmos.EL_ALLOW_EDIT| - wx.gizmos.EL_ALLOW_DELETE) - for func in ["_OnLabelEndEdit", - "_OnAddButton", - "_OnDelButton", - "_OnUpButton", + + self.Dimensions = CustomEditableListBox(self, label=_("Dimensions:"), + style=(wx.gizmos.EL_ALLOW_NEW | + wx.gizmos.EL_ALLOW_EDIT | + wx.gizmos.EL_ALLOW_DELETE)) + for func in ["_OnLabelEndEdit", + "_OnAddButton", + "_OnDelButton", + "_OnUpButton", "_OnDownButton"]: setattr(self.Dimensions, func, self.OnDimensionsChanged) - main_sizer.AddSizer(self.Dimensions, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + main_sizer.AddSizer(self.Dimensions, border=20, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + for datatype in datatypes: self.BaseType.Append(datatype) - + if isinstance(infos, TupleType) and infos[0] == "array": self.BaseType.SetStringSelection(infos[1]) - self.Dimensions.SetStrings(map(lambda x : "..".join(x), infos[2])) + self.Dimensions.SetStrings(map("..".join, infos[2])) elif infos in datatypes: self.BaseType.SetStringSelection(infos) - + self.BaseType.SetFocus() - + self.Fit() + def GetDimensions(self): + message = None dimensions_list = [] - for dimensions in self.Dimensions.GetStrings(): + dimension_strings = self.Dimensions.GetStrings() + if len(dimension_strings) == 0: + message = _("Empty dimension isn't allowed.") + + for dimensions in dimension_strings: result = DIMENSION_MODEL.match(dimensions) if result is None: - message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - return None + message = _("\"%s\" value isn't a valid array dimension!") % dimensions + break bounds = result.groups() if int(bounds[0]) >= int(bounds[1]): - message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - return None + message = _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.") % dimensions + break dimensions_list.append(bounds) + + if message is not None: + dlg = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) + dlg.ShowModal() + dlg.Destroy() + return None return dimensions_list - + def OnDimensionsChanged(self, event): wx.CallAfter(self.GetDimensions) event.Skip() - + def OnOK(self, event): if self.GetDimensions() is not None: self.EndModal(wx.ID_OK) - + def GetValue(self): return "array", self.BaseType.GetStringSelection(), self.GetDimensions() diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/BlockPreviewDialog.py --- a/dialogs/BlockPreviewDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/BlockPreviewDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2013: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,78 +23,81 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS from graphics.GraphicCommons import FREEDRAWING_MODE -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Dialog with preview for graphic block -#------------------------------------------------------------------------------- - -""" -Class that implements a generic dialog containing a preview panel for displaying -graphic created by dialog -""" +# ------------------------------------------------------------------------------- + class BlockPreviewDialog(wx.Dialog): - - def __init__(self, parent, controller, tagname, size, title): + """ + Class that implements a generic dialog containing a preview panel for displaying + graphic created by dialog + """ + + def __init__(self, parent, controller, tagname, title): """ Constructor @param parent: Parent wx.Window of dialog for modal @param controller: Reference to project controller @param tagname: Tagname of project POU edited - @param size: wx.Size object containing size of dialog @param title: Title of dialog frame """ - wx.Dialog.__init__(self, parent, size=size, title=title) - + wx.Dialog.__init__(self, parent, title=title) + # Save reference to self.Controller = controller self.TagName = tagname - + # Label for preview self.PreviewLabel = wx.StaticText(self, label=_('Preview:')) - + # Create Preview panel self.Preview = wx.Panel(self, style=wx.SIMPLE_BORDER) self.Preview.SetBackgroundColour(wx.WHITE) - + # Add function to preview panel so that it answers to graphic elements # like Viewer - setattr(self.Preview, "GetDrawingMode", lambda:FREEDRAWING_MODE) - setattr(self.Preview, "GetScaling", lambda:None) + setattr(self.Preview, "GetDrawingMode", lambda: FREEDRAWING_MODE) + setattr(self.Preview, "GetScaling", lambda: None) setattr(self.Preview, "GetBlockType", controller.GetBlockType) setattr(self.Preview, "IsOfType", controller.IsOfType) - + # Bind paint event on Preview panel self.Preview.Bind(wx.EVT_PAINT, self.OnPaint) - + # Add default dialog buttons sizer - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) + self.Bind(wx.EVT_BUTTON, self.OnOK, self.ButtonSizer.GetAffirmativeButton()) - + self.Element = None # Graphic element to display in preview self.MinElementSize = None # Graphic element minimal size - + # Variable containing the graphic element name when dialog is opened self.DefaultElementName = None - + self.Fit() + # List of variables defined in POU {var_name: (var_class, var_type),...} self.VariableList = {} - + def __del__(self): """ Destructor """ # Remove reference to project controller self.Controller = None - - def _init_sizers(self, main_rows, main_growable_row, - left_rows, left_growable_row, - right_rows, right_growable_row): + + def _init_sizers(self, + main_rows, main_growable_row, + left_rows, left_growable_row, + right_rows, right_growable_row): """ Initialize common sizers @param main_rows: Number of rows in main sizer @@ -107,44 +111,44 @@ None if no row is growable """ # Create dialog main sizer - self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, + self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=main_rows, vgap=10) self.MainSizer.AddGrowableCol(0) if main_growable_row is not None: self.MainSizer.AddGrowableRow(main_growable_row) - + # Create a sizer for dividing parameters in two columns - column_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.MainSizer.AddSizer(column_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + self.ColumnSizer = wx.BoxSizer(wx.HORIZONTAL) + self.MainSizer.AddSizer(self.ColumnSizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + # Create a sizer for left column - self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, + self.LeftGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=left_rows, vgap=5) self.LeftGridSizer.AddGrowableCol(0) if left_growable_row is not None: self.LeftGridSizer.AddGrowableRow(left_growable_row) - column_sizer.AddSizer(self.LeftGridSizer, 1, border=5, - flag=wx.GROW|wx.RIGHT) - + self.ColumnSizer.AddSizer(self.LeftGridSizer, 1, border=5, + flag=wx.GROW | wx.RIGHT | wx.EXPAND) + # Create a sizer for right column - self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, + self.RightGridSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=right_rows, vgap=0) self.RightGridSizer.AddGrowableCol(0) if right_growable_row is not None: self.RightGridSizer.AddGrowableRow(right_growable_row) - column_sizer.AddSizer(self.RightGridSizer, 1, border=5, - flag=wx.GROW|wx.LEFT) - + self.ColumnSizer.AddSizer(self.RightGridSizer, 1, border=5, + flag=wx.GROW | wx.LEFT) + self.SetSizer(self.MainSizer) - + def SetMinElementSize(self, size): """ Define minimal graphic element size @param size: Tuple containing minimal size (width, height) """ self.MinElementSize = size - + def GetMinElementSize(self): """ Get minimal graphic element size @@ -154,16 +158,16 @@ """ if self.Element is None: return None - + return self.Element.GetMinSize() - + def SetPreviewFont(self, font): """ Set font of Preview panel @param font: wx.Font object containing font style """ self.Preview.SetFont(font) - + def RefreshVariableList(self): """ Extract list of variables defined in POU @@ -171,23 +175,21 @@ # Get list of variables defined in POU self.VariableList = { var.Name: (var.Class, var.Type) - for var in self.Controller.GetEditedElementInterfaceVars( - self.TagName) + for var in self.Controller.GetEditedElementInterfaceVars(self.TagName) if var.Edit} - - # Add POU name to variable list if POU is a function - returntype = self.Controller.GetEditedElementInterfaceReturnType( - self.TagName) + + # Add POU name to variable list if POU is a function + returntype = self.Controller.GetEditedElementInterfaceReturnType(self.TagName) if returntype is not None: self.VariableList[ self.Controller.GetEditedElementName(self.TagName)] = \ ("Output", returntype) - + # Add POU name if POU is a transition words = self.TagName.split("::") if words[0] == "T": self.VariableList[words[2]] = ("Output", "BOOL") - + def TestElementName(self, element_name): """ Test displayed graphic element name @@ -197,47 +199,46 @@ message_format = None # Get graphic element name in upper case uppercase_element_name = element_name.upper() - + # Test if graphic element name is a valid identifier if not TestIdentifier(element_name): message_format = _("\"%s\" is not a valid identifier!") - + # Test that graphic element name isn't a keyword elif uppercase_element_name in IEC_KEYWORDS: message_format = _("\"%s\" is a keyword. It can't be used!") - + # Test that graphic element name isn't a POU name elif uppercase_element_name in self.Controller.GetProjectPouNames(): message_format = _("\"%s\" pou already exists!") - + # Test that graphic element name isn't already used in POU by a variable # or another graphic element - elif ((self.DefaultElementName is None or - self.DefaultElementName.upper() != uppercase_element_name) and - uppercase_element_name in self.Controller.\ - GetEditedElementVariables(self.TagName)): + elif ((self.DefaultElementName is None or + self.DefaultElementName.upper() != uppercase_element_name) and + uppercase_element_name in self.Controller.GetEditedElementVariables(self.TagName)): message_format = _("\"%s\" element for this pou already exists!") - + # If an error have been identify, show error message dialog if message_format is not None: self.ShowErrorMessage(message_format % element_name) # Test failed return False - + # Test succeed return True - + def ShowErrorMessage(self, message): """ Show an error message dialog over this dialog @param message: Error message to display """ - dialog = wx.MessageDialog(self, message, - _("Error"), - wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, + _("Error"), + wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -247,7 +248,7 @@ """ # Close dialog self.EndModal(wx.ID_OK) - + def RefreshPreview(self): """ Refresh preview panel of graphic element @@ -257,42 +258,42 @@ dc = wx.ClientDC(self.Preview) dc.SetFont(self.Preview.GetFont()) dc.Clear() - + # Return immediately if no graphic element defined if self.Element is None: return - + # Calculate block size according to graphic element min size due to its # parameters and graphic element min size defined min_width, min_height = self.GetMinElementSize() width = max(self.MinElementSize[0], min_width) height = max(self.MinElementSize[1], min_height) self.Element.SetSize(width, height) - + # Get element position and bounding box to center in preview posx, posy = self.Element.GetPosition() bbox = self.Element.GetBoundingBox() - + # Get Preview panel size client_size = self.Preview.GetClientSize() - + # If graphic element is too big to be displayed in preview panel, # calculate preview panel scale so that graphic element fit inside - scale = (max(float(bbox.width) / client_size.width, - float(bbox.height) / client_size.height) * 1.1 - if bbox.width * 1.1 > client_size.width or - bbox.height * 1.1 > client_size.height - else 1.0) + k = 1.1 if (bbox.width * 1.1 > client_size.width or + bbox.height * 1.1 > client_size.height) \ + else 1.0 + scale = (max(float(bbox.width) / client_size.width, + float(bbox.height) / client_size.height) * k) dc.SetUserScale(1.0 / scale, 1.0 / scale) - + # Center graphic element in preview panel x = int(client_size.width * scale - bbox.width) / 2 + posx - bbox.x y = int(client_size.height * scale - bbox.height) / 2 + posy - bbox.y self.Element.SetPosition(x, y) - + # Draw graphic element self.Element.Draw(dc) - + def OnPaint(self, event): """ Called when Preview panel need to be redraw @@ -300,4 +301,3 @@ """ self.RefreshPreview() event.Skip() - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/BrowseLocationsDialog.py --- a/dialogs/BrowseLocationsDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/BrowseLocationsDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,153 +23,165 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.# -import os - + +from __future__ import absolute_import import wx from plcopen.structures import LOCATIONDATATYPES from PLCControler import LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP, LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY from util.BitmapLibrary import GetBitmap - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetDirFilterChoiceOptions(): - _ = lambda x : x - return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), - (_("Input"), [LOCATION_VAR_INPUT]), - (_("Output"), [LOCATION_VAR_OUTPUT]), + _ = NoTranslate + return [(_("All"), [LOCATION_VAR_INPUT, LOCATION_VAR_OUTPUT, LOCATION_VAR_MEMORY]), + (_("Input"), [LOCATION_VAR_INPUT]), + (_("Output"), [LOCATION_VAR_OUTPUT]), (_("Memory"), [LOCATION_VAR_MEMORY])] + + DIRFILTERCHOICE_OPTIONS = dict([(_(option), filter) for option, filter in GetDirFilterChoiceOptions()]) + def GetTypeFilterChoiceOptions(): - _ = lambda x : x - return [_("All"), - _("Type and derivated"), + _ = NoTranslate + return [_("All"), + _("Type and derivated"), _("Type strict")] + # turn LOCATIONDATATYPES inside-out LOCATION_SIZES = {} for size, types in LOCATIONDATATYPES.iteritems(): for type in types: LOCATION_SIZES[type] = size -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Browse Locations Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class BrowseLocationsDialog(wx.Dialog): - + def __init__(self, parent, var_type, controller): - wx.Dialog.__init__(self, parent, - size=wx.Size(600, 400), title=_('Browse Locations'), - style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER) - + wx.Dialog.__init__(self, parent, title=_('Browse Locations'), + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + locations_label = wx.StaticText(self, label=_('Locations available:')) - main_sizer.AddWindow(locations_label, border=20, - flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - - self.LocationsTree = wx.TreeCtrl(self, - style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT) - self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, + main_sizer.AddWindow(locations_label, border=20, + flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + + self.LocationsTree = wx.TreeCtrl(self, + style=(wx.TR_HAS_BUTTONS | + wx.TR_SINGLE | + wx.SUNKEN_BORDER | + wx.TR_HIDE_ROOT | + wx.TR_LINES_AT_ROOT)) + self.LocationsTree.SetInitialSize(wx.Size(-1, 300)) + self.Bind(wx.EVT_TREE_ITEM_ACTIVATED, self.OnLocationsTreeItemActivated, self.LocationsTree) - main_sizer.AddWindow(self.LocationsTree, border=20, - flag=wx.LEFT|wx.RIGHT|wx.GROW) - + main_sizer.AddWindow(self.LocationsTree, border=20, + flag=wx.LEFT | wx.RIGHT | wx.GROW) + button_gridsizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) button_gridsizer.AddGrowableCol(1) button_gridsizer.AddGrowableCol(3) button_gridsizer.AddGrowableRow(0) - main_sizer.AddSizer(button_gridsizer, border=20, - flag=wx.BOTTOM|wx.LEFT|wx.RIGHT|wx.GROW) - + main_sizer.AddSizer(button_gridsizer, border=20, + flag=wx.BOTTOM | wx.LEFT | wx.RIGHT | wx.GROW) + direction_label = wx.StaticText(self, label=_('Direction:')) button_gridsizer.AddWindow(direction_label, - flag=wx.ALIGN_CENTER_VERTICAL) - - self.DirFilterChoice = wx.ComboBox(self, size=wx.Size(0, -1), style=wx.CB_READONLY) + flag=wx.ALIGN_CENTER_VERTICAL) + + self.DirFilterChoice = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.DirFilterChoice) button_gridsizer.AddWindow(self.DirFilterChoice, - flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL) - + flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL) + filter_label = wx.StaticText(self, label=_('Type:')) button_gridsizer.AddWindow(filter_label, - flag=wx.ALIGN_CENTER_VERTICAL) - - self.TypeFilterChoice = wx.ComboBox(self, size=wx.Size(0, -1), style=wx.CB_READONLY) + flag=wx.ALIGN_CENTER_VERTICAL) + + self.TypeFilterChoice = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnFilterChoice, self.TypeFilterChoice) button_gridsizer.AddWindow(self.TypeFilterChoice, - flag=wx.GROW|wx.ALIGN_CENTER_VERTICAL) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + flag=wx.GROW | wx.ALIGN_CENTER_VERTICAL) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) button_gridsizer.AddSizer(button_sizer, flag=wx.ALIGN_RIGHT) - + self.SetSizer(main_sizer) - + self.Controller = controller self.VarType = var_type self.BaseVarType = self.Controller.GetBaseType(self.VarType) self.VarTypeSize = LOCATION_SIZES[self.BaseVarType] self.Locations = self.Controller.GetVariableLocationTree() - + # Define Tree item icon list self.TreeImageList = wx.ImageList(16, 16) self.TreeImageDict = {} - + # Icons for items for imgname, itemtype in [ - ("CONFIGURATION", LOCATION_CONFNODE), - ("RESOURCE", LOCATION_MODULE), - ("PROGRAM", LOCATION_GROUP), - ("VAR_INPUT", LOCATION_VAR_INPUT), - ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), - ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: - self.TreeImageDict[itemtype]=self.TreeImageList.Add(GetBitmap(imgname)) - + ("CONFIGURATION", LOCATION_CONFNODE), + ("RESOURCE", LOCATION_MODULE), + ("PROGRAM", LOCATION_GROUP), + ("VAR_INPUT", LOCATION_VAR_INPUT), + ("VAR_OUTPUT", LOCATION_VAR_OUTPUT), + ("VAR_LOCAL", LOCATION_VAR_MEMORY)]: + self.TreeImageDict[itemtype] = self.TreeImageList.Add(GetBitmap(imgname)) + # Assign icon list to TreeCtrls self.LocationsTree.SetImageList(self.TreeImageList) - + # Set a options for the choice - for option, filter in GetDirFilterChoiceOptions(): + for option, _filter in GetDirFilterChoiceOptions(): self.DirFilterChoice.Append(_(option)) self.DirFilterChoice.SetStringSelection(_("All")) for option in GetTypeFilterChoiceOptions(): self.TypeFilterChoice.Append(_(option)) self.TypeFilterChoice.SetStringSelection(_("All")) self.RefreshFilters() - + self.RefreshLocationsTree() - + self.Fit() + def RefreshFilters(self): self.DirFilter = DIRFILTERCHOICE_OPTIONS[self.DirFilterChoice.GetStringSelection()] self.TypeFilter = self.TypeFilterChoice.GetSelection() - + def RefreshLocationsTree(self): root = self.LocationsTree.GetRootItem() if not root.IsOk(): root = self.LocationsTree.AddRoot("") self.GenerateLocationsTreeBranch(root, self.Locations) - + def FilterType(self, location_type, location_size): if self.TypeFilter == 0: return True - + if location_size != self.VarTypeSize: return False - + if self.TypeFilter == 1: return self.Controller.IsOfType(location_type, self.BaseVarType) elif self.TypeFilter == 2: return location_type == self.VarType - + return True - + def GenerateLocationsTreeBranch(self, root, locations): to_delete = [] item, root_cookie = self.LocationsTree.GetFirstChild(root) @@ -192,28 +205,28 @@ item, root_cookie = self.LocationsTree.GetNextChild(root, root_cookie) for item in to_delete: self.LocationsTree.Delete(item) - + def OnLocationsTreeItemActivated(self, event): infos = self.LocationsTree.GetPyData(event.GetItem()) if infos["type"] not in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]: wx.CallAfter(self.EndModal, wx.ID_OK) event.Skip() - + def OnFilterChoice(self, event): self.RefreshFilters() self.RefreshLocationsTree() - + def GetValues(self): selected = self.LocationsTree.GetSelection() return self.LocationsTree.GetPyData(selected) - + def OnOK(self, event): selected = self.LocationsTree.GetSelection() var_infos = None if selected.IsOk(): var_infos = self.LocationsTree.GetPyData(selected) if var_infos is None or var_infos["type"] in [LOCATION_CONFNODE, LOCATION_MODULE, LOCATION_GROUP]: - dialog = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, _("A location must be selected!"), _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/BrowseValuesLibraryDialog.py --- a/dialogs/BrowseValuesLibraryDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/BrowseValuesLibraryDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,45 +23,48 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx class BrowseValuesLibraryDialog(wx.Dialog): """ Modal dialog that helps in selecting predefined XML attributes sets out of hierarchically organized list - """ + """ def __init__(self, parent, name, library, default=None): wx.Dialog.__init__(self, - name='BrowseValueDialog', parent=parent, - size=wx.Size(600, 400), style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER, - title=_('Browse %s values library') % name) - - self.SetClientSize(wx.Size(600, 400)) + name='BrowseValueDialog', parent=parent, + style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, + title=_('Browse %s values library') % name) self.staticText1 = wx.StaticText( - label=_('Choose a value for %s:') % name, name='staticText1', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - + label=_('Choose a value for %s:') % name, + name='staticText1', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + self.ValuesLibrary = wx.TreeCtrl( - name='ValuesLibrary', parent=self, pos=wx.Point(0, 0), - size=wx.Size(0, 0), style=wx.TR_HAS_BUTTONS|wx.TR_SINGLE|wx.SUNKEN_BORDER|wx.TR_HIDE_ROOT|wx.TR_LINES_AT_ROOT) - - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + name='ValuesLibrary', parent=self, pos=wx.Point(0, 0), + size=wx.Size(400, 200), + style=wx.TR_HAS_BUTTONS | wx.TR_SINGLE | wx.SUNKEN_BORDER | wx.TR_HIDE_ROOT | wx.TR_LINES_AT_ROOT) + + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, id=self.ButtonSizer.GetAffirmativeButton().GetId()) - + self.flexGridSizer1 = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) - - self.flexGridSizer1.AddWindow(self.staticText1, 0, border=20, flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - self.flexGridSizer1.AddWindow(self.ValuesLibrary, 0, border=20, flag=wx.GROW|wx.LEFT|wx.RIGHT) - self.flexGridSizer1.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + + self.flexGridSizer1.AddWindow(self.staticText1, 0, border=20, flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.AddWindow(self.ValuesLibrary, 0, border=20, flag=wx.GROW | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.AddSizer(self.ButtonSizer, 0, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.flexGridSizer1.AddGrowableCol(0) self.flexGridSizer1.AddGrowableRow(1) - + self.SetSizer(self.flexGridSizer1) - + self.Fit() + root = self.ValuesLibrary.AddRoot("") self.GenerateValuesLibraryBranch(root, library, default) @@ -80,9 +84,8 @@ def OnOK(self, event): selected = self.ValuesLibrary.GetSelection() if not selected.IsOk() or self.ValuesLibrary.GetPyData(selected) is None: - message = wx.MessageDialog(self, _("No valid value selected!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("No valid value selected!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() else: self.EndModal(wx.ID_OK) - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/ConnectionDialog.py --- a/dialogs/ConnectionDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/ConnectionDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,23 +23,25 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.GraphicCommons import CONNECTOR, CONTINUATION from graphics.FBD_Objects import FBD_Connector -from BlockPreviewDialog import BlockPreviewDialog - -#------------------------------------------------------------------------------- +from dialogs.BlockPreviewDialog import BlockPreviewDialog + +# ------------------------------------------------------------------------------- # Set Connection Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a connection graphic -element -""" +# ------------------------------------------------------------------------------- + class ConnectionDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a connection graphic + element + """ + def __init__(self, parent, controller, tagname, apply_button=False): """ Constructor @@ -48,46 +51,50 @@ @param apply_button: Enable button for applying connector modification to all connector having the same name in POU (default: False) """ - BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(350, 250), title=_('Connection Properties')) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=_('Connection Properties')) + # Init common sizers - self._init_sizers(2, 0, 5, None, 2, 1) - + self._init_sizers(2, 0, 7, 1, 0, None) + # Create label for connection type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create radio buttons for selecting connection type self.TypeRadioButtons = {} first = True for type, label in [(CONNECTOR, _('Connector')), (CONTINUATION, _('Continuation'))]: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button first = False - + # Create label for connection name name_label = wx.StaticText(self, label=_('Name:')) self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW) - + # Create text control for defining connection name self.ConnectionName = wx.TextCtrl(self) + self.ConnectionName.SetMinSize(wx.Size(200, -1)) self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.ConnectionName) self.LeftGridSizer.AddWindow(self.ConnectionName, flag=wx.GROW) - + # Add preview panel and associated label to sizers - self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) - self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + self.Preview.SetMinSize(wx.Size(-1, 100)) + self.LeftGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) + self.LeftGridSizer.AddWindow(self.Preview, flag=wx.GROW) + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.ColumnSizer.RemoveSizer(self.RightGridSizer) + # Add button for applying connection name modification to all connection # of POU if apply_button: @@ -95,17 +102,16 @@ self.ApplyToAllButton.SetToolTipString( _("Apply name modification to all continuations with the same name")) self.Bind(wx.EVT_BUTTON, self.OnApplyToAll, self.ApplyToAllButton) - self.ButtonSizer.AddWindow(self.ApplyToAllButton, - border=(3 if wx.Platform == '__WXMSW__' else 10), - flag=wx.LEFT) + self.ButtonSizer.AddWindow(self.ApplyToAllButton, flag=wx.LEFT) else: self.ConnectionName.ChangeValue( controller.GenerateNewName( - tagname, None, "Connection%d", 0)) - + tagname, None, "Connection%d", 0)) + self.Fit() + # Connector radio button is default control having keyboard focus self.TypeRadioButtons[CONNECTOR].SetFocus() - + def GetConnectionType(self): """ Return type selected for connection @@ -114,7 +120,7 @@ return (CONNECTOR if self.TypeRadioButtons[CONNECTOR].GetValue() else CONTINUATION) - + def SetValues(self, values): """ Set default connection parameters @@ -122,18 +128,18 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is connection type if name == "type": self.TypeRadioButtons[value].SetValue(True) - + # Parameter is connection name elif name == "name": self.ConnectionName.SetValue(value) - + # Refresh preview panel self.RefreshPreview() - + def GetValues(self): """ Return connection parameters defined in dialog @@ -151,23 +157,23 @@ @return: True if connection name is valid """ message = None - + # Get connection name typed by user connection_name = self.ConnectionName.GetValue() - + # Test that a name have been defined if connection_name == "": message = _("Form isn't complete. Name must be filled!") - + # If an error have been identify, show error message dialog if message is not None: self.ShowErrorMessage(message) # Test failed return False - + # Return result of element name test return self.TestElementName(connection_name) - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -203,17 +209,16 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element Override BlockPreviewDialog function """ # Set graphic element displayed, creating a FBD connection element - self.Element = FBD_Connector(self.Preview, - self.GetConnectionType(), - self.ConnectionName.GetValue()) - + self.Element = FBD_Connector(self.Preview, + self.GetConnectionType(), + self.ConnectionName.GetValue()) + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/DiscoveryDialog.py --- a/dialogs/DiscoveryDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/DiscoveryDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,73 +23,69 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import socket import wx -import wx.lib.mixins.listctrl as listmix -from util.Zeroconf import * - -import connectors +import wx.lib.mixins.listctrl as listmix +from zeroconf import ServiceBrowser, Zeroconf service_type = '_PYRO._tcp.local.' + class AutoWidthListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin): def __init__(self, parent, id, name, pos=wx.DefaultPosition, size=wx.DefaultSize, style=0): wx.ListCtrl.__init__(self, parent, id, pos, size, style, name=name) listmix.ListCtrlAutoWidthMixin.__init__(self) -[ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1, - ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON, - ID_DISCOVERYDIALOGLOCALBUTTON, ID_DISCOVERYDIALOGIPBUTTON, + +[ + ID_DISCOVERYDIALOG, ID_DISCOVERYDIALOGSTATICTEXT1, + ID_DISCOVERYDIALOGSERVICESLIST, ID_DISCOVERYDIALOGREFRESHBUTTON, + ID_DISCOVERYDIALOGLOCALBUTTON, ID_DISCOVERYDIALOGIPBUTTON, ] = [wx.NewId() for _init_ctrls in range(6)] + class DiscoveryDialog(wx.Dialog, listmix.ColumnSorterMixin): - + def _init_coll_MainSizer_Items(self, parent): - parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.GROW) - parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + parent.AddWindow(self.staticText1, 0, border=20, flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + parent.AddWindow(self.ServicesList, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.GROW) + parent.AddSizer(self.ButtonGridSizer, 0, border=20, flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) + def _init_coll_MainSizer_Growables(self, parent): parent.AddGrowableCol(0) parent.AddGrowableRow(1) - + def _init_coll_ButtonGridSizer_Items(self, parent): parent.AddWindow(self.RefreshButton, 0, border=0, flag=0) parent.AddWindow(self.LocalButton, 0, border=0, flag=0) parent.AddWindow(self.IpButton, 0, border=0, flag=0) parent.AddSizer(self.ButtonSizer, 0, border=0, flag=0) - + def _init_coll_ButtonGridSizer_Growables(self, parent): parent.AddGrowableCol(0) parent.AddGrowableCol(1) parent.AddGrowableRow(0) - + def _init_sizers(self): self.MainSizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) self.ButtonGridSizer = wx.FlexGridSizer(cols=4, hgap=5, rows=1, vgap=0) - + self._init_coll_MainSizer_Items(self.MainSizer) self._init_coll_MainSizer_Growables(self.MainSizer) self._init_coll_ButtonGridSizer_Items(self.ButtonGridSizer) self._init_coll_ButtonGridSizer_Growables(self.ButtonGridSizer) - + self.SetSizer(self.MainSizer) - - def _init_ctrls(self, prnt): - wx.Dialog.__init__(self, id=ID_DISCOVERYDIALOG, - name='DiscoveryDialog', parent=prnt, - size=wx.Size(600, 600), style=wx.DEFAULT_DIALOG_STYLE, - title=_('Service Discovery')) - - self.staticText1 = wx.StaticText(id=ID_DISCOVERYDIALOGSTATICTEXT1, - label=_('Services available:'), name='staticText1', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - + + def _init_list_ctrl(self): # Set up list control - self.ServicesList = AutoWidthListCtrl(id=ID_DISCOVERYDIALOGSERVICESLIST, - name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), - style=wx.LC_REPORT|wx.LC_EDIT_LABELS|wx.LC_SORT_ASCENDING|wx.LC_SINGLE_SEL) + self.ServicesList = AutoWidthListCtrl( + id=ID_DISCOVERYDIALOGSERVICESLIST, + name='ServicesList', parent=self, pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.LC_REPORT | wx.LC_EDIT_LABELS | wx.LC_SORT_ASCENDING | wx.LC_SINGLE_SEL) self.ServicesList.InsertColumn(0, _('NAME')) self.ServicesList.InsertColumn(1, _('TYPE')) self.ServicesList.InsertColumn(2, _('IP')) @@ -97,49 +94,69 @@ self.ServicesList.SetColumnWidth(1, 150) self.ServicesList.SetColumnWidth(2, 150) self.ServicesList.SetColumnWidth(3, 150) + self.ServicesList.SetInitialSize(wx.Size(-1, 300)) self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, id=ID_DISCOVERYDIALOGSERVICESLIST) self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.OnItemActivated, id=ID_DISCOVERYDIALOGSERVICESLIST) - + + def _init_ctrls(self, prnt): + self.staticText1 = wx.StaticText( + id=ID_DISCOVERYDIALOGSTATICTEXT1, + label=_('Services available:'), name='staticText1', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + + self.RefreshButton = wx.Button( + id=ID_DISCOVERYDIALOGREFRESHBUTTON, + label=_('Refresh'), name='RefreshButton', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=ID_DISCOVERYDIALOGREFRESHBUTTON) + + self.LocalButton = wx.Button( + id=ID_DISCOVERYDIALOGLOCALBUTTON, + label=_('Local'), name='LocalButton', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + self.Bind(wx.EVT_BUTTON, self.OnLocalButton, id=ID_DISCOVERYDIALOGLOCALBUTTON) + + self.IpButton = wx.Button( + id=ID_DISCOVERYDIALOGIPBUTTON, + label=_('Add IP'), name='IpButton', parent=self, + pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) + self.Bind(wx.EVT_BUTTON, self.OnIpButton, id=ID_DISCOVERYDIALOGIPBUTTON) + + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTER) + + self._init_sizers() + self.Fit() + + def __init__(self, parent): + wx.Dialog.__init__( + self, id=ID_DISCOVERYDIALOG, + name='DiscoveryDialog', parent=parent, + style=wx.DEFAULT_DIALOG_STYLE, + title=_('Service Discovery')) + + self._init_list_ctrl() listmix.ColumnSorterMixin.__init__(self, 4) - - self.RefreshButton = wx.Button(id=ID_DISCOVERYDIALOGREFRESHBUTTON, - label=_('Refresh'), name='RefreshButton', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - self.Bind(wx.EVT_BUTTON, self.OnRefreshButton, id=ID_DISCOVERYDIALOGREFRESHBUTTON) - - self.LocalButton = wx.Button(id=ID_DISCOVERYDIALOGLOCALBUTTON, - label=_('Local'), name='LocalButton', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - self.Bind(wx.EVT_BUTTON, self.OnLocalButton, id=ID_DISCOVERYDIALOGLOCALBUTTON) - - self.IpButton = wx.Button(id=ID_DISCOVERYDIALOGIPBUTTON, - label=_('Add IP'), name='IpButton', parent=self, - pos=wx.Point(0, 0), size=wx.DefaultSize, style=0) - self.Bind(wx.EVT_BUTTON, self.OnIpButton, id=ID_DISCOVERYDIALOGIPBUTTON) - - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTER) - - self._init_sizers() - - def __init__(self, parent): + self._init_ctrls(parent) - + self.itemDataMap = {} self.nextItemId = 0 - + self.URI = None self.Browser = None - + self.ZeroConfInstance = Zeroconf() self.RefreshList() - self.LatestSelection=None - + self.LatestSelection = None + def __del__(self): - if self.Browser is not None : self.Browser.cancel() + if self.Browser is not None: + self.Browser.cancel() self.ZeroConfInstance.close() - + def RefreshList(self): - if self.Browser is not None : self.Browser.cancel() + if self.Browser is not None: + self.Browser.cancel() self.Browser = ServiceBrowser(self.ZeroConfInstance, service_type, self) def OnRefreshButton(self, event): @@ -152,9 +169,11 @@ event.Skip() def OnIpButton(self, event): + def GetColText(col): + return self.getColumnText(self.LatestSelection, col) + if self.LatestSelection is not None: - l = lambda col : self.getColumnText(self.LatestSelection,col) - self.URI = "%s://%s:%s"%tuple(map(l,(1,2,3))) + self.URI = "%s://%s:%s" % tuple(map(GetColText, (1, 2, 3))) self.EndModal(wx.ID_OK) event.Skip() @@ -179,27 +198,26 @@ # connect_type = self.getColumnText(idx, 1) # connect_address = self.getColumnText(idx, 2) # connect_port = self.getColumnText(idx, 3) -# +# # self.URI = "%s://%s:%s"%(connect_type, connect_address, connect_port) def SetURI(self, idx): self.LatestSelection = idx - svcname = self.getColumnText(idx, 0) + svcname = self.getColumnText(idx, 0) connect_type = self.getColumnText(idx, 1) - self.URI = "%s://%s"%(connect_type, svcname + '.' + service_type) - + self.URI = "%s://%s" % (connect_type, svcname + '.' + service_type) + def GetURI(self): return self.URI - - def removeService(self, zeroconf, _type, name): + + def remove_service(self, zeroconf, _type, name): wx.CallAfter(self._removeService, name) - def _removeService(self, name): ''' called when a service with the desired type goes offline. ''' - + # loop through the list items looking for the service that went offline for idx in xrange(self.ServicesList.GetItemCount()): # this is the unique identifier assigned to the item @@ -211,20 +229,19 @@ if item_name == name: self.ServicesList.DeleteItem(idx) break - - def addService(self, zeroconf, _type, name): + + def add_service(self, zeroconf, _type, name): wx.CallAfter(self._addService, _type, name) def _addService(self, _type, name): ''' called when a service with the desired type is discovered. ''' - info = self.ZeroConfInstance.getServiceInfo(_type, name) - - svcname = name.split(".")[0] + info = self.ZeroConfInstance.get_service_info(_type, name) + svcname = name.split(".")[0] typename = _type.split(".")[0][1:] - ip = str(socket.inet_ntoa(info.getAddress())) - port = info.getPort() + ip = str(socket.inet_ntoa(info.address)) + port = info.port num_items = self.ServicesList.GetItemCount() @@ -238,13 +255,12 @@ # we assign every list item a unique id (that won't change when items # are added or removed) self.ServicesList.SetItemData(new_item, self.nextItemId) - + # the value of each column has to be stored in the itemDataMap # so that ColumnSorterMixin knows how to sort the column. # "name" is included at the end so that self.removeService # can access it. - self.itemDataMap[self.nextItemId] = [ svcname, typename, ip, port, name ] + self.itemDataMap[self.nextItemId] = [svcname, typename, ip, port, name] self.nextItemId += 1 - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/DurationEditorDialog.py --- a/dialogs/DurationEditorDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/DurationEditorDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,13 +23,15 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- MICROSECONDS = 0.001 MILLISECONDS = 1 @@ -48,52 +51,52 @@ ("Microseconds", _('Microseconds:')), ] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Edit Duration Value Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class DurationEditorDialog(wx.Dialog): def __init__(self, parent): - wx.Dialog.__init__(self, parent, - size=wx.Size(700, 200), title=_('Edit Duration')) - + wx.Dialog.__init__(self, parent, title=_('Edit Duration')) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + controls_sizer = wx.FlexGridSizer(cols=len(CONTROLS), hgap=10, rows=2, vgap=10) - main_sizer.AddSizer(controls_sizer, border=20, - flag=wx.TOP|wx.LEFT|wx.RIGHT|wx.GROW) - + main_sizer.AddSizer(controls_sizer, border=20, + flag=wx.TOP | wx.LEFT | wx.RIGHT | wx.GROW) + controls = [] for i, (name, label) in enumerate(CONTROLS): controls_sizer.AddGrowableCol(i) - + st = wx.StaticText(self, label=label) txtctrl = wx.TextCtrl(self, value='0', style=wx.TE_PROCESS_ENTER) - self.Bind(wx.EVT_TEXT_ENTER, - self.GetControlValueTestFunction(txtctrl), + self.Bind(wx.EVT_TEXT_ENTER, + self.GetControlValueTestFunction(txtctrl), txtctrl) setattr(self, name, txtctrl) - + controls.append((st, txtctrl)) - + for st, txtctrl in controls: controls_sizer.AddWindow(st, flag=wx.GROW) - + for st, txtctrl in controls: controls_sizer.AddWindow(txtctrl, flag=wx.GROW) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + self.Fit() self.Days.SetFocus() - + def SetDuration(self, value): result = IEC_TIME_MODEL.match(value.upper()) if result is not None: @@ -112,16 +115,17 @@ else: self.Milliseconds.SetValue("0") self.Microseconds.SetValue("0") - + def GetControlValueTestFunction(self, control): def OnValueChanged(event): try: - value = float(control.GetValue()) - except ValueError, e: - message = wx.MessageDialog(self, _("Invalid value!\nYou must fill a numeric value."), _("Error"), wx.OK|wx.ICON_ERROR) + float(control.GetValue()) + except ValueError: + message = wx.MessageDialog(self, _("Invalid value!\nYou must fill a numeric value."), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() event.Skip() + self.OnCloseDialog() return OnValueChanged def GetDuration(self): @@ -129,38 +133,41 @@ for control, factor in [(self.Days, DAY), (self.Hours, HOUR), (self.Minutes, MINUTE), (self.Seconds, SECOND), (self.Milliseconds, MILLISECONDS), (self.Microseconds, MICROSECONDS)]: - + milliseconds += float(control.GetValue()) * factor - + not_null = False duration = "T#" - for value, format in [(int(milliseconds) / DAY, "%dd"), - ((int(milliseconds) % DAY) / HOUR, "%dh"), - ((int(milliseconds) % HOUR) / MINUTE, "%dm"), - ((int(milliseconds) % MINUTE) / SECOND, "%ds")]: - + for value, format in [((int(milliseconds) / DAY), "%dd"), + ((int(milliseconds) % DAY) / HOUR, "%dh"), + ((int(milliseconds) % HOUR) / MINUTE, "%dm"), + ((int(milliseconds) % MINUTE) / SECOND, "%ds")]: + if value > 0 or not_null: duration += format % value not_null = True - + duration += "%gms" % (milliseconds % SECOND) return duration - + def OnOK(self, event): + self.OnCloseDialog() + + def OnCloseDialog(self): errors = [] - for control, name in [(self.Days, _("days")), (self.Hours, _("hours")), + for control, name in [(self.Days, _("days")), (self.Hours, _("hours")), (self.Minutes, _("minutes")), (self.Seconds, _("seconds")), (self.Milliseconds, _("milliseconds"))]: try: - value = float(control.GetValue()) - except ValueError, e: + float(control.GetValue()) + except ValueError: errors.append(name) if len(errors) > 0: if len(errors) == 1: message = _("Field %s hasn't a valid value!") % errors[0] else: message = _("Fields %s haven't a valid value!") % ",".join(errors) - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/FBDBlockDialog.py --- a/dialogs/FBDBlockDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/FBDBlockDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,32 +23,35 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re import wx from graphics.FBD_Objects import FBD_Block from controls.LibraryPanel import LibraryPanel -from BlockPreviewDialog import BlockPreviewDialog - -#------------------------------------------------------------------------------- +from dialogs.BlockPreviewDialog import BlockPreviewDialog + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetBlockTypeDefaultNameModel(blocktype): return re.compile("%s[0-9]+" % blocktype if blocktype is not None else ".*") -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Block Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a FBD block graphic -element -""" +# ------------------------------------------------------------------------------- + class FBDBlockDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a FBD block graphic + element + """ + def __init__(self, parent, controller, tagname): """ Constructor @@ -56,82 +60,84 @@ @param tagname: Tagname of project POU edited """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(600, 450), title=_('Block Properties')) - + title=_('Block Properties')) + # Init common sizers self._init_sizers(2, 0, 1, 0, 3, 2) - + # Create static box around library panel type_staticbox = wx.StaticBox(self, label=_('Type:')) left_staticboxsizer = wx.StaticBoxSizer(type_staticbox, wx.VERTICAL) self.LeftGridSizer.AddSizer(left_staticboxsizer, border=5, flag=wx.GROW) - + # Create Library panel and add it to static box self.LibraryPanel = LibraryPanel(self) + self.LibraryPanel.SetInitialSize(wx.Size(-1, 400)) + # Set function to call when selection in Library panel changed - setattr(self.LibraryPanel, "_OnTreeItemSelected", - self.OnLibraryTreeItemSelected) - left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, - flag=wx.GROW|wx.TOP) - + setattr(self.LibraryPanel, "_OnTreeItemSelected", + self.OnLibraryTreeItemSelected) + left_staticboxsizer.AddWindow(self.LibraryPanel, 1, border=5, + flag=wx.GROW | wx.TOP) + # Create sizer for other block parameters top_right_gridsizer = wx.FlexGridSizer(cols=2, hgap=0, rows=4, vgap=5) top_right_gridsizer.AddGrowableCol(1) self.RightGridSizer.AddSizer(top_right_gridsizer, flag=wx.GROW) - + # Create label for block name name_label = wx.StaticText(self, label=_('Name:')) - top_right_gridsizer.AddWindow(name_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + top_right_gridsizer.AddWindow(name_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create text control for defining block name self.BlockName = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.BlockName) top_right_gridsizer.AddWindow(self.BlockName, flag=wx.GROW) - + # Create label for extended block input number inputs_label = wx.StaticText(self, label=_('Inputs:')) - top_right_gridsizer.AddWindow(inputs_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + top_right_gridsizer.AddWindow(inputs_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create spin control for defining extended block input number self.Inputs = wx.SpinCtrl(self, min=2, max=20, - style=wx.SP_ARROW_KEYS) + style=wx.SP_ARROW_KEYS) self.Bind(wx.EVT_SPINCTRL, self.OnInputsChanged, self.Inputs) top_right_gridsizer.AddWindow(self.Inputs, flag=wx.GROW) - + # Create label for block execution order - execution_order_label = wx.StaticText(self, - label=_('Execution Order:')) - top_right_gridsizer.AddWindow(execution_order_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + execution_order_label = wx.StaticText(self, + label=_('Execution Order:')) + top_right_gridsizer.AddWindow(execution_order_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create spin control for defining block execution order self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) - self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, + self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, self.ExecutionOrder) top_right_gridsizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) - + # Create label for block execution control - execution_control_label = wx.StaticText(self, - label=_('Execution Control:')) - top_right_gridsizer.AddWindow(execution_control_label, - flag=wx.ALIGN_CENTER_VERTICAL) - + execution_control_label = wx.StaticText(self, + label=_('Execution Control:')) + top_right_gridsizer.AddWindow(execution_control_label, + flag=wx.ALIGN_CENTER_VERTICAL) + # Create check box to enable block execution control self.ExecutionControl = wx.CheckBox(self) - self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, + self.Bind(wx.EVT_CHECKBOX, self.OnExecutionOrderChanged, self.ExecutionControl) top_right_gridsizer.AddWindow(self.ExecutionControl, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer(self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Dictionary containing correspondence between parameter exchanged and # control to fill with parameter value self.ParamsControl = { @@ -139,19 +145,20 @@ "executionOrder": self.ExecutionOrder, "executionControl": self.ExecutionControl } - + # Init controls value and sensibility self.BlockName.SetValue("") self.BlockName.Enable(False) self.Inputs.Enable(False) - + # Variable containing last name typed self.CurrentBlockName = None - + # Refresh Library panel values self.LibraryPanel.SetBlockList(controller.GetBlockTypes(tagname)) + self.Fit() self.LibraryPanel.SetFocus() - + def SetValues(self, values): """ Set default block parameters @@ -159,38 +166,38 @@ """ # Extract block type defined in parameters blocktype = values.get("type", None) - - # Select block type in library panel + + # Select block type in library panel if blocktype is not None: - self.LibraryPanel.SelectTreeItem(blocktype, + self.LibraryPanel.SelectTreeItem(blocktype, values.get("inputs", None)) - + # Define regular expression for determine if block name is block # default name default_name_model = GetBlockTypeDefaultNameModel(blocktype) - + # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is block name if name == "name": if value != "": # Set default graphic element name for testing self.DefaultElementName = value - + # Test if block name is type default block name and save # block name if not (name have been typed by user) if default_name_model.match(value) is None: self.CurrentBlockName = value - + self.BlockName.ChangeValue(value) - + # Set value of other controls else: control = self.ParamsControl.get(name, None) if control is not None: control.SetValue(value) - + # Refresh preview panel self.RefreshPreview() @@ -207,7 +214,7 @@ name: control.GetValue() for name, control in self.ParamsControl.iteritems()}) return values - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -215,31 +222,31 @@ @param event: wx.Event from OK button """ message = None - + # Get block type selected selected = self.LibraryPanel.GetSelectedBlock() - + # Get block type name and if block is a function block block_name = self.BlockName.GetValue() name_enabled = self.BlockName.IsEnabled() - + # Test that a type has been selected for block if selected is None: message = _("Form isn't complete. Valid block type must be selected!") - + # Test, if block is a function block, that a name have been defined elif name_enabled and block_name == "": message = _("Form isn't complete. Name must be filled!") - + # Show error message if an error is detected if message is not None: self.ShowErrorMessage(message) - + # Test block name validity if necessary elif not name_enabled or self.TestElementName(block_name): # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) - + def OnLibraryTreeItemSelected(self, event): """ Called when block type selected in library panel @@ -247,12 +254,12 @@ """ # Get type selected in library panel values = self.LibraryPanel.GetSelectedBlock() - + # Get block type informations - blocktype = (self.Controller.GetBlockType(values["type"], + blocktype = (self.Controller.GetBlockType(values["type"], values["inputs"]) if values is not None else None) - + # Set input number spin control according to block type informations if blocktype is not None: self.Inputs.SetValue(len(blocktype["inputs"])) @@ -260,12 +267,12 @@ else: self.Inputs.SetValue(2) self.Inputs.Enable(False) - + # Update block name with default value if block type is a function and # current block name wasn't typed by user if blocktype is not None and blocktype["type"] != "function": self.BlockName.Enable(True) - + if self.CurrentBlockName is None: # Generate new block name according to block type, taking # default element name if it was already a default name for this @@ -273,21 +280,21 @@ default_name_model = GetBlockTypeDefaultNameModel(values["type"]) block_name = ( self.DefaultElementName - if (self.DefaultElementName is not None and + if (self.DefaultElementName is not None and default_name_model.match(self.DefaultElementName)) else self.Controller.GenerateNewName( self.TagName, None, values["type"]+"%d", 0)) else: block_name = self.CurrentBlockName - + self.BlockName.ChangeValue(block_name) else: self.BlockName.Enable(False) self.BlockName.ChangeValue("") - + # Refresh preview panel self.RefreshPreview() - + def OnNameChanged(self, event): """ Called when block name value changed @@ -298,7 +305,7 @@ self.CurrentBlockName = self.BlockName.GetValue() self.RefreshPreview() event.Skip() - + def OnInputsChanged(self, event): """ Called when block inputs number changed @@ -307,7 +314,7 @@ if self.Inputs.IsEnabled(): self.RefreshPreview() event.Skip() - + def OnExecutionOrderChanged(self, event): """ Called when block execution order value changed @@ -315,7 +322,7 @@ """ self.RefreshPreview() event.Skip() - + def OnExecutionControlChanged(self, event): """ Called when block execution control value changed @@ -323,7 +330,7 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element @@ -331,22 +338,21 @@ """ # Get type selected in library panel values = self.LibraryPanel.GetSelectedBlock() - + # If a block type is selected in library panel if values is not None: # Set graphic element displayed, creating a FBD block element - self.Element = FBD_Block(self.Preview, values["type"], - (self.BlockName.GetValue() - if self.BlockName.IsEnabled() - else ""), - extension = self.Inputs.GetValue(), - inputs = values["inputs"], - executionControl = self.ExecutionControl.GetValue(), - executionOrder = self.ExecutionOrder.GetValue()) - + self.Element = FBD_Block( + self.Preview, values["type"], + (self.BlockName.GetValue() if self.BlockName.IsEnabled() else ""), + extension=self.Inputs.GetValue(), + inputs=values["inputs"], + executionControl=self.ExecutionControl.GetValue(), + executionOrder=self.ExecutionOrder.GetValue()) + # Reset graphic element displayed else: - self.Element = None - + self.Element = None + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/FBDVariableDialog.py --- a/dialogs/FBDVariableDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/FBDVariableDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,34 +23,38 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx from graphics.GraphicCommons import INPUT, INOUT, OUTPUT from graphics.FBD_Objects import FBD_Variable -from BlockPreviewDialog import BlockPreviewDialog - -#------------------------------------------------------------------------------- +from dialogs.BlockPreviewDialog import BlockPreviewDialog + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Dictionaries containing correspondence between variable block class and string # to be shown in Class combo box in both sense -VARIABLE_CLASSES_DICT = {INPUT : _("Input"), - INOUT : _("InOut"), - OUTPUT : _("Output")} +VARIABLE_CLASSES_DICT = { + INPUT: _("Input"), + INOUT: _("InOut"), + OUTPUT: _("Output") +} + VARIABLE_CLASSES_DICT_REVERSE = dict( [(value, key) for key, value in VARIABLE_CLASSES_DICT.iteritems()]) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Variable Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a FBD variable graphic -element -""" +# ------------------------------------------------------------------------------- + class FBDVariableDialog(BlockPreviewDialog): + """ + Class that implements a dialog for defining parameters of a FBD variable graphic + element + """ def __init__(self, parent, controller, tagname, exclude_input=False): """ @@ -60,70 +65,74 @@ @param exclude_input: Exclude input from variable class selection """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(400, 380), title=_('Variable Properties')) - + title=_('Variable Properties')) + # Init common sizers self._init_sizers(4, 2, 4, None, 3, 2) - + # Create label for variable class class_label = wx.StaticText(self, label=_('Class:')) self.LeftGridSizer.AddWindow(class_label, flag=wx.GROW) - + # Create a combo box for defining variable class self.Class = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnClassChanged, self.Class) self.LeftGridSizer.AddWindow(self.Class, flag=wx.GROW) - + # Create label for variable execution order - execution_order_label = wx.StaticText(self, - label=_('Execution Order:')) + execution_order_label = wx.StaticText(self, + label=_('Execution Order:')) self.LeftGridSizer.AddWindow(execution_order_label, flag=wx.GROW) - + # Create spin control for defining variable execution order self.ExecutionOrder = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) - self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, + self.Bind(wx.EVT_SPINCTRL, self.OnExecutionOrderChanged, self.ExecutionOrder) self.LeftGridSizer.AddWindow(self.ExecutionOrder, flag=wx.GROW) - + # Create label for variable expression name_label = wx.StaticText(self, label=_('Expression:')) - self.RightGridSizer.AddWindow(name_label, border=5, - flag=wx.GROW|wx.BOTTOM) - + self.RightGridSizer.AddWindow(name_label, border=5, + flag=wx.GROW | wx.BOTTOM) + # Create text control for defining variable expression self.Expression = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnExpressionChanged, self.Expression) self.RightGridSizer.AddWindow(self.Expression, flag=wx.GROW) - + # Create a list box to selected variable expression in the list of # variables defined in POU - self.VariableName = wx.ListBox(self, size=wx.Size(0, 120), - style=wx.LB_SINGLE|wx.LB_SORT) + self.VariableName = wx.ListBox(self, size=wx.Size(-1, 120), + style=wx.LB_SINGLE | wx.LB_SORT) self.Bind(wx.EVT_LISTBOX, self.OnNameChanged, self.VariableName) - self.RightGridSizer.AddWindow(self.VariableName, flag=wx.GROW) - + self.RightGridSizer.AddWindow(self.VariableName, border=4, flag=wx.GROW | wx.TOP) + # Add preview panel and associated label to sizers self.MainSizer.AddWindow(self.PreviewLabel, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.LEFT | wx.RIGHT) self.MainSizer.AddWindow(self.Preview, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + flag=wx.GROW | wx.LEFT | wx.RIGHT) + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Set options that can be selected in class combo box for var_class, choice in VARIABLE_CLASSES_DICT.iteritems(): if not exclude_input or var_class != INPUT: self.Class.Append(choice) self.Class.SetSelection(0) - + # Extract list of variables defined in POU self.RefreshVariableList() - + # Refresh values in name list box self.RefreshNameList() - + + self.Preview.SetInitialSize(wx.Size(-1, 60)) + self.Fit() + # Class combo box is default control having keyboard focus self.Class.SetFocus() @@ -133,33 +142,32 @@ """ # Get variable class to select POU variable applicable var_class = VARIABLE_CLASSES_DICT_REVERSE[ - self.Class.GetStringSelection()] - + self.Class.GetStringSelection()] + # Refresh names in name list box by selecting variables in POU variables # list that can be applied to variable class self.VariableName.Clear() - for name, (var_type, value_type) in self.VariableList.iteritems(): + for name, (var_type, _value_type) in self.VariableList.iteritems(): if var_type != "Input" or var_class == INPUT: self.VariableName.Append(name) - + # Get variable expression and select corresponding value in name list # box if it exists selected = self.Expression.GetValue() - if (selected != "" and - self.VariableName.FindString(selected) != wx.NOT_FOUND): + if selected != "" and self.VariableName.FindString(selected) != wx.NOT_FOUND: self.VariableName.SetStringSelection(selected) else: self.VariableName.SetSelection(wx.NOT_FOUND) - + # Disable name list box if no name present inside self.VariableName.Enable(self.VariableName.GetCount() > 0) - + def SetValues(self, values): """ Set default variable parameters @param values: Variable parameters values """ - + # Get class parameter value var_class = values.get("class", None) if var_class is not None: @@ -167,10 +175,10 @@ self.Class.SetStringSelection(VARIABLE_CLASSES_DICT[var_class]) # Refresh names in name list box according to var class self.RefreshNameList() - + # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is variable expression if name == "expression": # Set expression text control value @@ -180,14 +188,15 @@ self.VariableName.SetStringSelection(value) else: self.VariableName.SetSelection(wx.NOT_FOUND) - + # Parameter is variable execution order elif name == "executionOrder": self.ExecutionOrder.SetValue(value) - + # Refresh preview panel self.RefreshPreview() - + self.Fit() + def GetValues(self): """ Return block parameters defined in dialog @@ -196,7 +205,7 @@ expression = self.Expression.GetValue() values = { "class": VARIABLE_CLASSES_DICT_REVERSE[ - self.Class.GetStringSelection()], + self.Class.GetStringSelection()], "expression": expression, "var_type": self.VariableList.get(expression, (None, None))[1], "executionOrder": self.ExecutionOrder.GetValue()} @@ -210,16 +219,16 @@ @param event: wx.Event from OK button """ message = None - + # Test that an expression have been selected or typed by user value = self.Expression.GetValue() if value == "": message = _("At least a variable or an expression must be selected!") - + # Show error message if an error is detected if message is not None: self.ShowErrorMessage(message) - + else: # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) @@ -231,7 +240,7 @@ """ # Refresh name list box values self.RefreshNameList() - + self.RefreshPreview() event.Skip() @@ -244,10 +253,10 @@ # list box if value selected is valid if self.VariableName.GetSelection() != wx.NOT_FOUND: self.Expression.ChangeValue(self.VariableName.GetStringSelection()) - - self.RefreshPreview() - event.Skip() - + + self.RefreshPreview() + event.Skip() + def OnExpressionChanged(self, event): """ Called when expression text control is changed by user @@ -256,10 +265,10 @@ # Select the corresponding value in name list box if it exists self.VariableName.SetSelection( self.VariableName.FindString(self.Expression.GetValue())) - - self.RefreshPreview() - event.Skip() - + + self.RefreshPreview() + event.Skip() + def OnExecutionOrderChanged(self, event): """ Called when block execution control value changed @@ -267,7 +276,7 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element @@ -275,16 +284,14 @@ """ # Get expression value to put in FBD variable element name = self.Expression.GetValue() - + # Set graphic element displayed, creating a FBD variable element - self.Element = FBD_Variable(self.Preview, - VARIABLE_CLASSES_DICT_REVERSE[ - self.Class.GetStringSelection()], - name, - self.VariableList.get(name, ("", ""))[1], - executionOrder = self.ExecutionOrder.GetValue()) - + self.Element = FBD_Variable( + self.Preview, + VARIABLE_CLASSES_DICT_REVERSE[self.Class.GetStringSelection()], + name, + self.VariableList.get(name, ("", ""))[1], + executionOrder=self.ExecutionOrder.GetValue()) + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/FindInPouDialog.py --- a/dialogs/FindInPouDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/FindInPouDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,118 +23,124 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from plcopen.plcopen import * + class FindInPouDialog(wx.Dialog): def _init_icon(self, parent): if parent and parent.icon: - self.SetIcon(parent.icon) + self.SetIcon(parent.icon) - def __init__(self, parent): - wx.Dialog.__init__(self, parent, title=_("Find"), - size=wx.Size(500, 280), style=wx.CAPTION| - wx.CLOSE_BOX| - wx.CLIP_CHILDREN| - wx.RESIZE_BORDER) - + wx.Dialog.__init__( + self, parent, title=_("Find"), + style=wx.CAPTION | wx.CLOSE_BOX | wx.CLIP_CHILDREN | wx.RESIZE_BORDER) + self._init_icon(parent) panel = wx.Panel(self, style=wx.TAB_TRAVERSAL) - + main_sizer = wx.FlexGridSizer(cols=1, hgap=5, rows=2, vgap=5) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + controls_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(controls_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(controls_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + patterns_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=1, vgap=5) patterns_sizer.AddGrowableCol(1) - controls_sizer.AddSizer(patterns_sizer, border=5, flag=wx.GROW|wx.BOTTOM) - + controls_sizer.AddSizer(patterns_sizer, border=5, flag=wx.GROW | wx.BOTTOM) + find_label = wx.StaticText(panel, label=_("Find:")) patterns_sizer.AddWindow(find_label, flag=wx.ALIGN_CENTER_VERTICAL) - + self.FindPattern = wx.TextCtrl(panel) self.Bind(wx.EVT_TEXT, self.OnFindPatternChanged, self.FindPattern) self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey) patterns_sizer.AddWindow(self.FindPattern, flag=wx.GROW) - + params_sizer = wx.BoxSizer(wx.HORIZONTAL) - controls_sizer.AddSizer(params_sizer, border=5, flag=wx.GROW|wx.BOTTOM) - + controls_sizer.AddSizer(params_sizer, border=5, flag=wx.GROW | wx.BOTTOM) + direction_staticbox = wx.StaticBox(panel, label=_("Direction")) direction_staticboxsizer = wx.StaticBoxSizer( - direction_staticbox, wx.VERTICAL) - params_sizer.AddSizer(direction_staticboxsizer, 1, border=5, - flag=wx.GROW|wx.RIGHT) - - self.Forward = wx.RadioButton(panel, label=_("Forward"), - style=wx.RB_GROUP) - direction_staticboxsizer.AddWindow(self.Forward, border=5, - flag=wx.ALL|wx.GROW) - + direction_staticbox, wx.VERTICAL) + params_sizer.AddSizer(direction_staticboxsizer, 1, border=5, + flag=wx.GROW | wx.RIGHT) + + self.Forward = wx.RadioButton(panel, label=_("Forward"), + style=wx.RB_GROUP) + direction_staticboxsizer.AddWindow(self.Forward, border=5, + flag=wx.ALL | wx.GROW) + self.Backward = wx.RadioButton(panel, label=_("Backward")) - direction_staticboxsizer.AddWindow(self.Backward, border=5, - flag=wx.ALL|wx.GROW) - + direction_staticboxsizer.AddWindow(self.Backward, border=5, + flag=wx.ALL | wx.GROW) + options_staticbox = wx.StaticBox(panel, label=_("Options")) options_staticboxsizer = wx.StaticBoxSizer( - options_staticbox, wx.VERTICAL) + options_staticbox, wx.VERTICAL) params_sizer.AddSizer(options_staticboxsizer, 1, flag=wx.GROW) - + self.CaseSensitive = wx.CheckBox(panel, label=_("Case sensitive")) self.CaseSensitive.SetValue(True) - options_staticboxsizer.AddWindow(self.CaseSensitive, border=5, - flag=wx.ALL|wx.GROW) - + options_staticboxsizer.AddWindow(self.CaseSensitive, border=5, + flag=wx.ALL | wx.GROW) + self.WrapSearch = wx.CheckBox(panel, label=_("Wrap search")) self.WrapSearch.SetValue(True) - options_staticboxsizer.AddWindow(self.WrapSearch, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + options_staticboxsizer.AddWindow(self.WrapSearch, border=5, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) + self.RegularExpressions = wx.CheckBox(panel, label=_("Regular expressions")) - options_staticboxsizer.AddWindow(self.RegularExpressions, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.GROW) - + options_staticboxsizer.AddWindow(self.RegularExpressions, border=5, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.GROW) + buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) - main_sizer.AddSizer(buttons_sizer, border=20, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM|wx.ALIGN_RIGHT) - + main_sizer.AddSizer(buttons_sizer, border=20, + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM | wx.ALIGN_RIGHT) + self.FindButton = wx.Button(panel, label=_("Find")) self.FindButton.SetDefault() self.Bind(wx.EVT_BUTTON, self.OnFindButton, self.FindButton) buttons_sizer.AddWindow(self.FindButton, border=5, flag=wx.RIGHT) - + self.CloseButton = wx.Button(panel, label=_("Close")) self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton) buttons_sizer.AddWindow(self.CloseButton) - self.StatusLabel = wx.StaticText(panel, label= "") + # set the longest message here, to use it length to calculate + # optimal size of dialog window + self.RegExpSyntaxErrMsg = _("Syntax error in regular expression of pattern to search!") + self.StatusLabel = wx.StaticText(panel, label=self.RegExpSyntaxErrMsg) controls_sizer.AddWindow(self.StatusLabel, flag=wx.ALIGN_CENTER_VERTICAL) - + panel.SetSizer(main_sizer) main_sizer.Fit(self) - + + # clear message after dialog size calculation + self.SetStatusText("") + self.ParentWindow = parent - + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) self.infosPrev = {} self.criteria = {} self.FindPattern.SetFocus() self.RefreshButtonsState() - + def RefreshButtonsState(self): find_pattern = self.FindPattern.GetValue() self.FindButton.Enable(find_pattern != "") - + def OnCloseFrame(self, event): self.Hide() event.Veto() - + def OnCloseButton(self, event): self.Hide() event.Skip() @@ -152,7 +159,7 @@ def SetStatusText(self, msg): self.StatusLabel.SetLabel(msg) self.Layout() - + def OnFindButton(self, event): infos = { "find_pattern": self.FindPattern.GetValue(), @@ -167,12 +174,12 @@ try: self.criteria = infos CompilePattern(self.criteria) - except: + except Exception: self.criteria.clear() - message = _("Syntax error in regular expression of pattern to search!") + message = self.RegExpSyntaxErrMsg self.SetStatusText(message) if len(self.criteria) > 0: wx.CallAfter(self.ParentWindow.FindInPou, - {True: 1, False:-1}[self.Forward.GetValue()], - self.criteria) + {True: 1, False: -1}[self.Forward.GetValue()], + self.criteria) event.Skip() diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/ForceVariableDialog.py --- a/dialogs/ForceVariableDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/ForceVariableDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -21,31 +21,36 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re import datetime import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- - -LOCATIONDATATYPES = {"X" : ["BOOL"], - "B" : ["SINT", "USINT", "BYTE", "STRING"], - "W" : ["INT", "UINT", "WORD", "WSTRING"], - "D" : ["DINT", "UDINT", "REAL", "DWORD"], - "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} +# ------------------------------------------------------------------------------- + +LOCATIONDATATYPES = {"X": ["BOOL"], + "B": ["SINT", "USINT", "BYTE", "STRING"], + "W": ["INT", "UINT", "WORD", "WSTRING"], + "D": ["DINT", "UDINT", "REAL", "DWORD"], + "L": ["LINT", "ULINT", "LREAL", "LWORD"]} + def gen_get_function(f): def get_function(v): try: return f(v) - except: + except Exception: return None return get_function + def gen_get_string(delimiter): STRING_MODEL = re.compile("%(delimiter)s([^%(delimiter)s]*)%(delimiter)s$" % {"delimiter": delimiter}) + def get_string(v): result = STRING_MODEL.match(v) if result is not None: @@ -53,6 +58,7 @@ return None return get_string + getinteger = gen_get_function(int) getfloat = gen_get_function(float) getstring = gen_get_string("'") @@ -68,7 +74,8 @@ IEC_DATETIME_MODEL = re.compile("(?:(?:DT|DATE_AND_TIME)#)?([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$") IEC_TIMEOFDAY_MODEL = re.compile("(?:(?:TOD|TIME_OF_DAY)#)?([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]+)?)$") -def gettime(v): + +def gettime(v): result = IEC_TIME_MODEL.match(v.upper()) if result is not None: negative, days, hours, minutes, seconds, milliseconds = result.groups() @@ -87,9 +94,10 @@ if negative is not None: microseconds = -microseconds return datetime.timedelta(microseconds=microseconds) - - else: - return None + + else: + return None + def getdate(v): result = IEC_DATE_MODEL.match(v.upper()) @@ -97,12 +105,13 @@ year, month, day = result.groups() try: date = datetime.datetime(int(year), int(month), int(day)) - except ValueError, e: + except ValueError: return None base_date = datetime.datetime(1970, 1, 1) return date - base_date - else: - return None + else: + return None + def getdatetime(v): result = IEC_DATETIME_MODEL.match(v.upper()) @@ -110,12 +119,13 @@ year, month, day, hours, minutes, seconds = result.groups() try: date = datetime.datetime(int(year), int(month), int(day), int(hours), int(minutes), int(float(seconds)), int((float(seconds) * SECOND) % SECOND)) - except ValueError, e: + except ValueError: return None base_date = datetime.datetime(1970, 1, 1) return date - base_date - else: - return None + else: + return None + def gettimeofday(v): result = IEC_TIMEOFDAY_MODEL.match(v.upper()) @@ -130,6 +140,7 @@ else: return None + GetTypeValue = {"BOOL": lambda x: {"TRUE": True, "FALSE": False, "0": False, "1": True}.get(x.upper(), None), "SINT": getinteger, "INT": getinteger, @@ -152,31 +163,54 @@ "DT": getdatetime, "TOD": gettimeofday} -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Force Variable Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ForceVariableDialog(wx.TextEntryDialog): def __init__(self, parent, iec_type, defaultValue=""): - wx.TextEntryDialog.__init__(self, parent, message = _("Forcing Variable Value"), - caption = _("Please enter value for a \"%s\" variable:") % iec_type, defaultValue = defaultValue, - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition) - - self.IEC_Type = iec_type - - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) - + wx.TextEntryDialog.__init__( + self, parent, + message=_("Forcing Variable Value"), + caption=_("Please enter value for a \"%s\" variable:") % iec_type, + defaultValue=defaultValue, + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition) + + self.IEC_Type = iec_type + + self.Bind(wx.EVT_BUTTON, self.OnOK, + self.GetSizer().GetItem(2).GetSizer().GetItem(1). + GetSizer().GetAffirmativeButton()) + self.ValueTextCtrl = self.GetSizer().GetItem(1).GetWindow() + if self.IEC_Type == "BOOL": + self.ToggleButton = wx.ToggleButton(self, label=_("Toggle value")) + value = GetTypeValue[self.IEC_Type](defaultValue) + if value is not None: + self.ToggleButton.SetValue(value) + + border = self.GetSizer().GetItem(1).GetBorder() + self.GetSizer().Insert(before=2, item=self.ToggleButton, + border=border, + flag=wx.LEFT | wx.RIGHT | wx.EXPAND) + self.Bind(wx.EVT_TOGGLEBUTTON, self.ToggleBoolValue, self.ToggleButton) + + self.Fit() + + def ToggleBoolValue(self, event): + value = self.ToggleButton.GetValue() + self.ValueTextCtrl.SetValue(unicode(value)) + def OnOK(self, event): message = None - value = self.GetSizer().GetItem(1).GetWindow().GetValue() + value = self.ValueTextCtrl.GetValue() if value == "": message = _("You must type a value!") elif GetTypeValue[self.IEC_Type](value) is None: - message = _("Invalid value \"{a1}\" for \"{a2}\" variable!").format(a1 = value, a2 = self.IEC_Type) + message = _("Invalid value \"{a1}\" for \"{a2}\" variable!").format(a1=value, a2=self.IEC_Type) if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/LDElementDialog.py --- a/dialogs/LDElementDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/LDElementDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,25 +23,27 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.GraphicCommons import CONTACT_NORMAL, CONTACT_REVERSE, \ CONTACT_RISING, CONTACT_FALLING, COIL_NORMAL, COIL_REVERSE, COIL_SET, \ COIL_RESET, COIL_RISING, COIL_FALLING from graphics.LD_Objects import LD_Contact, LD_Coil -from BlockPreviewDialog import BlockPreviewDialog - -#------------------------------------------------------------------------------- +from dialogs.BlockPreviewDialog import BlockPreviewDialog + +# ------------------------------------------------------------------------------- # Set Ladder Element Parmeters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a LD contact or coil -graphic element -""" +# ------------------------------------------------------------------------------- + class LDElementDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a LD contact or coil + graphic element + """ + def __init__(self, parent, controller, tagname, type): """ Constructor @@ -49,78 +52,81 @@ @param tagname: Tagname of project POU edited @param type: Type of LD element ('contact or 'coil') """ - BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(350, 320 if type == "contact" else 380), - title=(_("Edit Contact Values") - if type == "contact" - else _("Edit Coil Values"))) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=(_("Edit Contact Values") + if type == "contact" + else _("Edit Coil Values"))) + # Init common sizers - self._init_sizers(2, 0, - (7 if type == "contact" else 9), None, 2, 1) - + self._init_sizers(2, 0, (7 if type == "contact" else 9), + None, 2, 1) + # Create label for LD element modifier modifier_label = wx.StaticText(self, label=_('Modifier:')) - self.LeftGridSizer.AddWindow(modifier_label, border=5, - flag=wx.GROW|wx.BOTTOM) - + self.LeftGridSizer.AddWindow(modifier_label, border=5, + flag=wx.GROW | wx.BOTTOM) + # Create radio buttons for selecting LD element modifier self.ModifierRadioButtons = {} first = True - element_modifiers = ([CONTACT_NORMAL, CONTACT_REVERSE, + element_modifiers = ([CONTACT_NORMAL, CONTACT_REVERSE, CONTACT_RISING, CONTACT_FALLING] if type == "contact" else [COIL_NORMAL, COIL_REVERSE, COIL_SET, COIL_RESET, COIL_RISING, COIL_FALLING]) - modifiers_label = [_("Normal"), _("Negated")] + \ - ([_("Set"), _("Reset")] if type == "coil" else []) + \ - [_("Rising Edge"), _("Falling Edge")] - + modifiers_label = \ + [_("Normal"), _("Negated")] + \ + ([_("Set"), _("Reset")] if type == "coil" else []) + \ + [_("Rising Edge"), _("Falling Edge")] + for modifier, label in zip(element_modifiers, modifiers_label): - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnModifierChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.ModifierRadioButtons[modifier] = radio_button first = False - + # Create label for LD element variable element_variable_label = wx.StaticText(self, label=_('Variable:')) self.LeftGridSizer.AddWindow(element_variable_label, border=5, - flag=wx.GROW|wx.TOP) - + flag=wx.GROW | wx.TOP) + # Create a combo box for defining LD element variable self.ElementVariable = wx.ComboBox(self, style=wx.CB_SORT) - self.Bind(wx.EVT_COMBOBOX, self.OnVariableChanged, + self.Bind(wx.EVT_COMBOBOX, self.OnVariableChanged, + self.ElementVariable) + self.Bind(wx.EVT_TEXT, self.OnVariableChanged, self.ElementVariable) self.LeftGridSizer.AddWindow(self.ElementVariable, border=5, - flag=wx.GROW|wx.TOP) - + flag=wx.GROW | wx.TOP) + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer(self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Save LD element class self.ElementClass = (LD_Contact if type == "contact" else LD_Coil) - + # Extract list of variables defined in POU self.RefreshVariableList() - + # Set values in ElementVariable for name, (var_type, value_type) in self.VariableList.iteritems(): # Only select BOOL variable and avoid input for coil if (type == "contact" or var_type != "Input") and \ value_type == "BOOL": self.ElementVariable.Append(name) - + + self.Fit() # Normal radio button is default control having keyboard focus self.ModifierRadioButtons[element_modifiers[0]].SetFocus() - + def GetElementModifier(self): """ Return modifier selected for LD element @@ -140,15 +146,15 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is LD element variable if name == "variable": self.ElementVariable.SetValue(value) - + # Set value of other controls elif name == "modifier": self.ModifierRadioButtons[value].SetValue(True) - + # Refresh preview panel self.RefreshPreview() @@ -184,11 +190,20 @@ Refresh preview panel of graphic element Override BlockPreviewDialog function """ + value = self.ElementVariable.GetValue() + # Set graphic element displayed, creating a LD element self.Element = self.ElementClass( - self.Preview, - self.GetElementModifier(), - self.ElementVariable.GetValue()) - + self.Preview, + self.GetElementModifier(), + value) + + button = self.ButtonSizer.GetAffirmativeButton() + button.Enable(value != "") + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) + + def OnOK(self, event): + if self.ElementVariable.GetValue() != "": + self.EndModal(wx.ID_OK) diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/LDPowerRailDialog.py --- a/dialogs/LDPowerRailDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/LDPowerRailDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,23 +23,24 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx -from graphics.GraphicCommons import LEFTRAIL, RIGHTRAIL, LD_LINE_SIZE +from graphics.GraphicCommons import LEFTRAIL, RIGHTRAIL from graphics.LD_Objects import LD_PowerRail -from BlockPreviewDialog import BlockPreviewDialog +from dialogs.BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set Ladder Power Rail Parameters Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a dialog for defining parameters of a power rail graphic -element -""" class LDPowerRailDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a power rail graphic + element + """ def __init__(self, parent, controller, tagname): """ Constructor @@ -47,50 +49,52 @@ @param tagname: Tagname of project POU edited """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(350, 260), title=_('Power Rail Properties')) - + title=_('Power Rail Properties')) + # Init common sizers self._init_sizers(2, 0, 5, None, 2, 1) - + # Create label for connection type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create radio buttons for selecting power rail type self.TypeRadioButtons = {} first = True for type, label in [(LEFTRAIL, _('Left PowerRail')), (RIGHTRAIL, _('Right PowerRail'))]: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button first = False - + # Create label for power rail pin number pin_number_label = wx.StaticText(self, label=_('Pin number:')) self.LeftGridSizer.AddWindow(pin_number_label, flag=wx.GROW) - + # Create spin control for defining power rail pin number self.PinNumber = wx.SpinCtrl(self, min=1, max=50, - style=wx.SP_ARROW_KEYS) + style=wx.SP_ARROW_KEYS) self.PinNumber.SetValue(1) self.Bind(wx.EVT_SPINCTRL, self.OnPinNumberChanged, self.PinNumber) self.LeftGridSizer.AddWindow(self.PinNumber, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.Fit() + # Left Power Rail radio button is default control having keyboard focus self.TypeRadioButtons[LEFTRAIL].SetFocus() - + def GetMinElementSize(self): """ Get minimal graphic element size @@ -98,7 +102,7 @@ element defined """ return self.Element.GetMinSize(True) - + def GetPowerRailType(self): """ Return type selected for power rail @@ -107,7 +111,7 @@ return (LEFTRAIL if self.TypeRadioButtons[LEFTRAIL].GetValue() else RIGHTRAIL) - + def SetValues(self, values): """ Set default power rail parameters @@ -115,11 +119,11 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is power rail type if name == "type": self.TypeRadioButtons[value].SetValue(True) - + # Parameter is power rail pin number elif name == "pin_number": self.PinNumber.SetValue(value) @@ -156,11 +160,11 @@ Refresh preview panel of graphic element Override BlockPreviewDialog function """ - + # Set graphic element displayed, creating a power rail element - self.Element = LD_PowerRail(self.Preview, - self.GetPowerRailType(), - connectors = self.PinNumber.GetValue()) - + self.Element = LD_PowerRail(self.Preview, + self.GetPowerRailType(), + connectors=self.PinNumber.GetValue()) + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/PouActionDialog.py --- a/dialogs/PouActionDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/PouActionDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,58 +23,65 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS +from util.TranslationCatalogs import NoTranslate + def GetActionLanguages(): - _ = lambda x : x + _ = NoTranslate return [_("IL"), _("ST"), _("LD"), _("FBD")] + + ACTION_LANGUAGES_DICT = dict([(_(language), language) for language in GetActionLanguages()]) + class PouActionDialog(wx.Dialog): - + def __init__(self, parent): - wx.Dialog.__init__(self, parent, size=wx.Size(320, 200), - title=_('Create a new action')) - + wx.Dialog.__init__(self, parent, title=_('Create a new action')) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - + infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(infos_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + actionname_label = wx.StaticText(self, label=_('Action Name:')) - infos_sizer.AddWindow(actionname_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) - - self.ActionName = wx.TextCtrl(self) + infos_sizer.AddWindow(actionname_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) + + self.ActionName = wx.TextCtrl(self, size=wx.Size(180, -1)) infos_sizer.AddWindow(self.ActionName, flag=wx.GROW) - + language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) - + infos_sizer.AddWindow(language_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) + self.Language = wx.ComboBox(self, style=wx.CB_READONLY) infos_sizer.AddWindow(self.Language, flag=wx.GROW) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, - button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) + self.Bind(wx.EVT_BUTTON, self.OnOK, + button_sizer.GetAffirmativeButton()) + main_sizer.AddSizer(button_sizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + for option in GetActionLanguages(): self.Language.Append(_(option)) - + + self.Fit() self.PouNames = [] self.PouElementNames = [] - + def OnOK(self, event): error = [] action_name = self.ActionName.GetValue() @@ -88,9 +96,9 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += _(", %s")%item + text += _(", %s") % item message = _("Form isn't complete. %s must be filled!") % text elif not TestIdentifier(action_name): message = _("\"%s\" is not a valid identifier!") % action_name @@ -101,28 +109,27 @@ elif action_name.upper() in self.PouElementNames: message = _("\"%s\" element for this pou already exists!") % action_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: self.EndModal(wx.ID_OK) - + def SetPouNames(self, pou_names): self.PouNames = [pou_name.upper() for pou_name in pou_names] - + def SetPouElementNames(self, element_names): self.PouElementNames = [element_name.upper() for element_name in element_names] - + def SetValues(self, values): for item, value in values.items(): if item == "actionName": self.ActionName.SetValue(value) elif item == "language": self.Language.SetStringSelection(_(value)) - + def GetValues(self): values = {} values["actionName"] = self.ActionName.GetValue() values["language"] = ACTION_LANGUAGES_DICT[self.Language.GetStringSelection()] return values - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/PouDialog.py --- a/dialogs/PouDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/PouDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,29 +23,36 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS +from util.TranslationCatalogs import NoTranslate + def GetPouTypes(): - _ = lambda x : x + _ = NoTranslate return [_("function"), _("functionBlock"), _("program")] + + POU_TYPES_DICT = dict([(_(pou_type), pou_type) for pou_type in GetPouTypes()]) + def GetPouLanguages(): - _ = lambda x : x + _ = NoTranslate return [_("IL"), _("ST"), _("LD"), _("FBD"), _("SFC")] + class PouDialog(wx.Dialog): POU_LANGUAGES = GetPouLanguages() POU_LANGUAGES_DICT = dict([(_(language), language) for language in POU_LANGUAGES]) - def __init__(self, parent, pou_type = None): + def __init__(self, parent, pou_type=None, type_readonly=False): wx.Dialog.__init__(self, id=-1, parent=parent, - name='PouDialog', title=_('Create a new POU'), - size=wx.Size(300, 200), style=wx.DEFAULT_DIALOG_STYLE) - self.SetClientSize(wx.Size(300, 200)) + name='PouDialog', title=_('Create a new POU'), + style=wx.DEFAULT_DIALOG_STYLE) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) @@ -53,18 +61,18 @@ infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) infos_sizer.AddGrowableCol(1) main_sizer.AddSizer(infos_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) pouname_label = wx.StaticText(self, label=_('POU Name:')) infos_sizer.AddWindow(pouname_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.PouName = wx.TextCtrl(self) infos_sizer.AddWindow(self.PouName, flag=wx.GROW) poutype_label = wx.StaticText(self, label=_('POU Type:')) infos_sizer.AddWindow(poutype_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.PouType = wx.ComboBox(self, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnTypeChanged, self.PouType) @@ -72,24 +80,25 @@ language_label = wx.StaticText(self, label=_('Language:')) infos_sizer.AddWindow(language_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) self.Language = wx.ComboBox(self, style=wx.CB_READONLY) infos_sizer.AddWindow(self.Language, flag=wx.GROW) - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.SetSizer(main_sizer) for option in GetPouTypes(): - self.PouType.Append(_(option)) + if not type_readonly or _(option) == _(pou_type): + self.PouType.Append(_(option)) if pou_type is not None: self.PouType.SetStringSelection(_(pou_type)) self.RefreshLanguage() - + self.Fit() self.PouNames = [] self.PouElementNames = [] @@ -110,9 +119,9 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += _(", %s")%item + text += _(", %s") % item message = _("Form isn't complete. %s must be filled!") % text elif not TestIdentifier(pou_name): message = _("\"%s\" is not a valid identifier!") % pou_name @@ -125,13 +134,13 @@ question = True if message is not None: if question: - dialog = wx.MessageDialog(self, message, _("Warning"), wx.YES_NO|wx.ICON_EXCLAMATION) + dialog = wx.MessageDialog(self, message, _("Warning"), wx.YES_NO | wx.ICON_EXCLAMATION) result = dialog.ShowModal() dialog.Destroy() if result == wx.ID_YES: self.EndModal(wx.ID_OK) else: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/PouNameDialog.py --- a/dialogs/PouNameDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/PouNameDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,23 +22,27 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx +from plcopen.structures import TestIdentifier, IEC_KEYWORDS -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # POU Name Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class PouNameDialog(wx.TextEntryDialog): - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): + def __init__(self, parent, message, caption=_("Please enter text"), defaultValue="", + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition): wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) - + self.PouNames = [] - - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) - + + self.Bind(wx.EVT_BUTTON, self.OnOK, + self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) + def OnOK(self, event): message = None step_name = self.GetSizer().GetItem(1).GetWindow().GetValue() @@ -51,7 +55,7 @@ elif step_name.upper() in self.PouNames: message = _("A POU named \"%s\" already exists!") % step_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: @@ -60,4 +64,3 @@ def SetPouNames(self, pou_names): self.PouNames = [pou_name.upper() for pou_name in pou_names] - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/PouTransitionDialog.py --- a/dialogs/PouTransitionDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/PouTransitionDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,66 +23,67 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from plcopen.structures import TestIdentifier, IEC_KEYWORDS +from util.TranslationCatalogs import NoTranslate -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # POU Transition Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetTransitionLanguages(): - _ = lambda x : x - # - # IL language is temporary disabled because - # matiec freezes if transition is written in IL - # - # return [_("IL"), _("ST"), _("LD"), _("FBD")] - return [ _("ST"), _("LD"), _("FBD")] + _ = NoTranslate + return [_("IL"), _("ST"), _("LD"), _("FBD")] + + TRANSITION_LANGUAGES_DICT = dict([(_(language), language) for language in GetTransitionLanguages()]) + class PouTransitionDialog(wx.Dialog): - + def __init__(self, parent): - wx.Dialog.__init__(self, parent, size=wx.Size(350, 200), - title=_('Create a new transition')) - + wx.Dialog.__init__(self, parent, title=_('Create a new transition')) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - - infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=15) + + infos_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=3, vgap=10) infos_sizer.AddGrowableCol(1) - main_sizer.AddSizer(infos_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(infos_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + transitionname_label = wx.StaticText(self, label=_('Transition Name:')) - infos_sizer.AddWindow(transitionname_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) + infos_sizer.AddWindow(transitionname_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) - self.TransitionName = wx.TextCtrl(self) + self.TransitionName = wx.TextCtrl(self, size=wx.Size(180, -1)) infos_sizer.AddWindow(self.TransitionName, flag=wx.GROW) language_label = wx.StaticText(self, label=_('Language:')) - infos_sizer.AddWindow(language_label, border=4, - flag=wx.ALIGN_CENTER_VERTICAL|wx.TOP) - + infos_sizer.AddWindow(language_label, border=4, + flag=wx.ALIGN_CENTER_VERTICAL | wx.TOP) + self.Language = wx.ComboBox(self, style=wx.CB_READONLY) infos_sizer.AddWindow(self.Language, flag=wx.GROW) - - button_sizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) + + button_sizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) self.Bind(wx.EVT_BUTTON, self.OnOK, button_sizer.GetAffirmativeButton()) - main_sizer.AddSizer(button_sizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(button_sizer, border=20, flag=wx.ALIGN_RIGHT | wx.BOTTOM) + self.SetSizer(main_sizer) - + for language in GetTransitionLanguages(): self.Language.Append(_(language)) - + + self.Fit() self.PouNames = [] self.PouElementNames = [] - + def OnOK(self, event): error = [] transition_name = self.TransitionName.GetValue() @@ -96,9 +98,9 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += _(", %s")%item + text += _(", %s") % item message = _("Form isn't complete. %s must be filled!") % text elif not TestIdentifier(transition_name): message = _("\"%s\" is not a valid identifier!") % transition_name @@ -109,25 +111,25 @@ elif transition_name.upper() in self.PouElementNames: message = _("\"%s\" element for this pou already exists!") % transition_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: self.EndModal(wx.ID_OK) - + def SetPouNames(self, pou_names): self.PouNames = [pou_name.upper() for pou_name in pou_names] - + def SetPouElementNames(self, pou_names): self.PouElementNames = [pou_name.upper() for pou_name in pou_names] - + def SetValues(self, values): for item, value in values.items(): if item == "transitionName": self.TransitionName.SetValue(value) elif item == "language": self.Language.SetSelection(_(value)) - + def GetValues(self): values = {} values["transitionName"] = self.TransitionName.GetValue() diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/ProjectDialog.py --- a/dialogs/ProjectDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/ProjectDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2012: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,32 +23,39 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from controls.ProjectPropertiesPanel import ProjectPropertiesPanel + class ProjectDialog(wx.Dialog): - + def __init__(self, parent, enable_required=True): - wx.Dialog.__init__(self, parent, title=_('Project properties'), - size=wx.Size(500, 350), style=wx.DEFAULT_DIALOG_STYLE) - + wx.Dialog.__init__(self, parent, title=_('Project properties'), + style=wx.DEFAULT_DIALOG_STYLE) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(0) - - self.ProjectProperties = ProjectPropertiesPanel(self, - enable_required=enable_required) + + self.ProjectProperties = ProjectPropertiesPanel( + self, + enable_required=enable_required) + main_sizer.AddWindow(self.ProjectProperties, flag=wx.GROW) - - self.ButtonSizer = self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.CENTRE) - self.Bind(wx.EVT_BUTTON, self.OnOK, + + self.ButtonSizer = self.CreateButtonSizer(wx.OK | wx.CANCEL | wx.CENTRE) + self.Bind(wx.EVT_BUTTON, self.OnOK, self.ButtonSizer.GetAffirmativeButton()) - main_sizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + self.SetSizer(main_sizer) - + self.ProjectProperties.Fit() + self.Fit() + def OnOK(self, event): values = self.ProjectProperties.GetValues() error = [] @@ -63,12 +71,13 @@ if i == 0: text += item elif i == len(error) - 1: - text += _(" and %s")%item + text += _(" and %s") % item else: - text += ", %s"%item - dialog = wx.MessageDialog(self, - _("Form isn't complete. %s must be filled!") % text, - _("Error"), wx.OK|wx.ICON_ERROR) + text += ", %s" % item + dialog = wx.MessageDialog( + self, + _("Form isn't complete. %s must be filled!") % text, + _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: @@ -76,6 +85,6 @@ def SetValues(self, values): self.ProjectProperties.SetValues(values) - + def GetValues(self): return self.ProjectProperties.GetValues() diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/SFCDivergenceDialog.py --- a/dialogs/SFCDivergenceDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/SFCDivergenceDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,25 +23,26 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx from graphics.GraphicCommons import SELECTION_DIVERGENCE, \ SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE from graphics.SFC_Objects import SFC_Divergence -from BlockPreviewDialog import BlockPreviewDialog +from dialogs.BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Create New Divergence Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a dialog for defining parameters for creating a new -divergence graphic element -""" class SFCDivergenceDialog(BlockPreviewDialog): - - def __init__(self, parent, controller, tagname, poss_div_types = None): + """ + Class that implements a dialog for defining parameters for creating a new + divergence graphic element + """ + + def __init__(self, parent, controller, tagname, poss_div_types=None): """ Constructor @param parent: Parent wx.Window of dialog for modal @@ -48,17 +50,16 @@ @param tagname: Tagname of project POU edited @param poss_div_types: Types of divergence that will be available in the dialog window """ - BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(500, 300), - title=_('Create a new divergence or convergence')) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=_('Create a new divergence or convergence')) + # Init common sizers self._init_sizers(2, 0, 7, None, 2, 1) - + # Create label for divergence type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create radio buttons for selecting divergence type divergence_buttons = [ (SELECTION_DIVERGENCE, _('Selection Divergence')), @@ -67,45 +68,49 @@ (SIMULTANEOUS_CONVERGENCE, _('Simultaneous Convergence'))] poss_div_btns = [] if poss_div_types is not None: - for val in poss_div_types: - poss_div_btns.append(divergence_buttons[val]) + for val in poss_div_types: + poss_div_btns.append(divergence_buttons[val]) else: poss_div_btns = divergence_buttons self.TypeRadioButtons = {} first = True focusbtn = None for type, label in poss_div_btns: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) self.TypeRadioButtons[type] = radio_button - if first: focusbtn = type + if first: + focusbtn = type first = False # Create label for number of divergence sequences - sequences_label = wx.StaticText(self, - label=_('Number of sequences:')) + sequences_label = wx.StaticText(self, + label=_('Number of sequences:')) self.LeftGridSizer.AddWindow(sequences_label, flag=wx.GROW) - + # Create spin control for defining number of divergence sequences self.Sequences = wx.SpinCtrl(self, min=2, max=20, initial=2) self.Bind(wx.EVT_SPINCTRL, self.OnSequencesChanged, self.Sequences) self.LeftGridSizer.AddWindow(self.Sequences, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + + self.Fit() + # Selection divergence radio button is default control having keyboard # focus self.TypeRadioButtons[focusbtn].SetFocus() - + def GetMinElementSize(self): """ Get minimal graphic element size @@ -113,7 +118,7 @@ element defined """ return self.Element.GetMinSize(True) - + def GetDivergenceType(self): """ Return type selected for SFC divergence @@ -125,7 +130,7 @@ if control.GetValue(): return type return None - + def GetValues(self): """ Set default SFC divergence parameters @@ -149,17 +154,16 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element Override BlockPreviewDialog function """ # Set graphic element displayed, creating a SFC divergence - self.Element = SFC_Divergence(self.Preview, - self.GetDivergenceType(), + self.Element = SFC_Divergence(self.Preview, + self.GetDivergenceType(), self.Sequences.GetValue()) - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) - diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/SFCStepDialog.py --- a/dialogs/SFCStepDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/SFCStepDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,22 +23,24 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.SFC_Objects import SFC_Step -from BlockPreviewDialog import BlockPreviewDialog +from dialogs.BlockPreviewDialog import BlockPreviewDialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Set SFC Step Parameters Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a dialog for defining parameters of a SFC step graphic -element -""" class SFCStepDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a SFC step graphic + element + """ + def __init__(self, parent, controller, tagname, initial=False): """ Constructor @@ -46,25 +49,25 @@ @param tagname: Tagname of project POU edited @param initial: True if step is initial (default: False) """ - BlockPreviewDialog.__init__(self,parent, controller, tagname, - size=wx.Size(400, 280), title=_('Edit Step')) - + BlockPreviewDialog.__init__(self, parent, controller, tagname, + title=_('Edit Step')) + # Init common sizers self._init_sizers(2, 0, 6, None, 2, 1) - + # Create label for SFC step name name_label = wx.StaticText(self, label=_('Name:')) self.LeftGridSizer.AddWindow(name_label, flag=wx.GROW) - + # Create text control for defining SFC step name self.StepName = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnNameChanged, self.StepName) self.LeftGridSizer.AddWindow(self.StepName, flag=wx.GROW) - + # Create label for SFC step connectors connectors_label = wx.StaticText(self, label=_('Connectors:')) self.LeftGridSizer.AddWindow(connectors_label, flag=wx.GROW) - + # Create check boxes for defining connectors available on SFC step self.ConnectorsCheckBox = {} for name, label in [("input", _("Input")), @@ -76,25 +79,28 @@ self.Bind(wx.EVT_CHECKBOX, self.OnConnectorsChanged, check_box) self.LeftGridSizer.AddWindow(check_box, flag=wx.GROW) self.ConnectorsCheckBox[name] = check_box - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + # Save flag that indicates that step is initial self.Initial = initial - + # Set default name for step self.StepName.ChangeValue(controller.GenerateNewName( - tagname, None, "Step%d", 0)) - + tagname, None, "Step%d", 0)) + + self.Fit() + # Step name text control is default control having keyboard focus self.StepName.SetFocus() - + def SetValues(self, values): """ Set default block parameters @@ -102,20 +108,20 @@ """ # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is step name if name == "name": self.StepName.ChangeValue(value) - + # Set value of other controls else: control = self.ConnectorsCheckBox.get(name, None) if control is not None: control.SetValue(value) - + # Refresh preview panel self.RefreshPreview() - + def GetValues(self): """ Return step parameters defined in dialog @@ -127,7 +133,7 @@ for name, control in self.ConnectorsCheckBox.iteritems()}) values["width"], values["height"] = self.Element.GetSize() return values - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -135,23 +141,23 @@ @param event: wx.Event from OK button """ message = None - + # Get step name typed by user step_name = self.StepName.GetValue() - + # Test that a name have been defined if step_name == "": message = _("Form isn't complete. Name must be filled!") - + # If an error have been identify, show error message dialog if message is not None: self.ShowErrorMessage(message) - + # Test step name validity elif self.TestElementName(step_name): # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) - + def OnConnectorsChanged(self, event): """ Called when a step connector value changed @@ -167,23 +173,23 @@ """ self.RefreshPreview() event.Skip() - + def RefreshPreview(self): """ Refresh preview panel of graphic element Override BlockPreviewDialog function """ # Set graphic element displayed, creating a SFC step element - self.Element = SFC_Step(self.Preview, - self.StepName.GetValue(), + self.Element = SFC_Step(self.Preview, + self.StepName.GetValue(), self.Initial) - + # Update connectors of SFC step element according to check boxes value for name, control in self.ConnectorsCheckBox.iteritems(): if control.IsChecked(): getattr(self.Element, "Add" + name.capitalize())() else: getattr(self.Element, "Remove" + name.capitalize())() - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/SFCStepNameDialog.py --- a/dialogs/SFCStepNameDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/SFCStepNameDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,25 +22,29 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx +from plcopen.structures import TestIdentifier, IEC_KEYWORDS -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Edit Step Name Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class SFCStepNameDialog(wx.TextEntryDialog): - def __init__(self, parent, message, caption = "Please enter text", defaultValue = "", - style = wx.OK|wx.CANCEL|wx.CENTRE, pos = wx.DefaultPosition): + def __init__(self, parent, message, caption="Please enter text", defaultValue="", + style=wx.OK | wx.CANCEL | wx.CENTRE, pos=wx.DefaultPosition): wx.TextEntryDialog.__init__(self, parent, message, caption, defaultValue, style, pos) - + self.PouNames = [] self.Variables = [] self.StepNames = [] - - self.Bind(wx.EVT_BUTTON, self.OnOK, - self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) - + + self.Bind(wx.EVT_BUTTON, self.OnOK, + self.GetSizer().GetItem(2).GetSizer().GetItem(1).GetSizer().GetAffirmativeButton()) + def OnOK(self, event): message = None step_name = self.GetSizer().GetItem(1).GetWindow().GetValue() @@ -57,7 +61,7 @@ elif step_name.upper() in self.StepNames: message = _("\"%s\" step already exists!") % step_name if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/SFCTransitionDialog.py --- a/dialogs/SFCTransitionDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/SFCTransitionDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,22 +23,23 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +from __future__ import absolute_import import wx from graphics.SFC_Objects import SFC_Transition -from BlockPreviewDialog import BlockPreviewDialog - -#------------------------------------------------------------------------------- +from dialogs.BlockPreviewDialog import BlockPreviewDialog + +# ------------------------------------------------------------------------------- # Set Transition Parameters Dialog -#------------------------------------------------------------------------------- - -""" -Class that implements a dialog for defining parameters of a transition graphic -element -""" +# ------------------------------------------------------------------------------- + class SFCTransitionDialog(BlockPreviewDialog): - + """ + Class that implements a dialog for defining parameters of a transition graphic + element + """ + def __init__(self, parent, controller, tagname, connection=True): """ Constructor @@ -48,34 +50,34 @@ connection (default: True) """ BlockPreviewDialog.__init__(self, parent, controller, tagname, - size=wx.Size(350, 350), title=_('Edit transition')) - + title=_('Edit transition')) + # Init common sizers self._init_sizers(2, 0, 8, None, 2, 1) - + # Create label for transition type type_label = wx.StaticText(self, label=_('Type:')) self.LeftGridSizer.AddWindow(type_label, flag=wx.GROW) - + # Create combo box for selecting reference value reference = wx.ComboBox(self, style=wx.CB_READONLY) reference.Append("") for transition in controller.GetEditedElementTransitions(tagname): reference.Append(transition) self.Bind(wx.EVT_COMBOBOX, self.OnReferenceChanged, reference) - + # Create Text control for defining inline value inline = wx.TextCtrl(self) self.Bind(wx.EVT_TEXT, self.OnInlineChanged, inline) - + # Create radio buttons for selecting power rail type self.TypeRadioButtons = {} first = True for type, label, control in [('reference', _('Reference'), reference), ('inline', _('Inline'), inline), ('connection', _('Connection'), None)]: - radio_button = wx.RadioButton(self, label=label, - style=(wx.RB_GROUP if first else 0)) + radio_button = wx.RadioButton(self, label=label, + style=(wx.RB_GROUP if first else 0)) radio_button.SetValue(first) self.Bind(wx.EVT_RADIOBUTTON, self.OnTypeChanged, radio_button) self.LeftGridSizer.AddWindow(radio_button, flag=wx.GROW) @@ -84,27 +86,30 @@ self.LeftGridSizer.AddWindow(control, flag=wx.GROW) self.TypeRadioButtons[type] = (radio_button, control) first = False - + # Create label for transition priority priority_label = wx.StaticText(self, label=_('Priority:')) self.LeftGridSizer.AddWindow(priority_label, flag=wx.GROW) - + # Create spin control for defining priority value self.Priority = wx.SpinCtrl(self, min=0, style=wx.SP_ARROW_KEYS) self.Bind(wx.EVT_TEXT, self.OnPriorityChanged, self.Priority) self.LeftGridSizer.AddWindow(self.Priority, flag=wx.GROW) - + # Add preview panel and associated label to sizers self.RightGridSizer.AddWindow(self.PreviewLabel, flag=wx.GROW) self.RightGridSizer.AddWindow(self.Preview, flag=wx.GROW) - + # Add buttons sizer to sizers - self.MainSizer.AddSizer(self.ButtonSizer, border=20, - flag=wx.ALIGN_RIGHT|wx.BOTTOM|wx.LEFT|wx.RIGHT) - + self.MainSizer.AddSizer( + self.ButtonSizer, border=20, + flag=wx.ALIGN_RIGHT | wx.BOTTOM | wx.LEFT | wx.RIGHT) + + self.Fit() + # Reference radio button is default control having keyboard focus self.TypeRadioButtons["reference"][0].SetFocus() - + def GetTransitionType(self): """ Return type selected for SFC transition and associated value @@ -121,7 +126,7 @@ else: return type, None return None, None - + def SetValues(self, values): """ Set default SFC transition parameters @@ -129,14 +134,14 @@ """ # Extract transition value according to type type_value = values.get("value", None) - + # For each parameters defined, set corresponding control value for name, value in values.items(): - + # Parameter is SFC transition priority if name == "priority": self.Priority.SetValue(values["priority"]) - + # Parameter is SFC transition type elif name == "type": for type, (radio, control) in self.TypeRadioButtons.iteritems(): @@ -149,20 +154,20 @@ control.SetStringSelection(type_value) elif isinstance(control, wx.TextCtrl): control.ChangeValue(type_value) - + # Refresh preview panel self.RefreshPreview() - + def GetValues(self): """ Return SFC transition parameters defined in dialog @return: {parameter_name: parameter_value,...} """ - values = {"priority" : self.Priority.GetValue()} + values = {"priority": self.Priority.GetValue()} values["type"], values["value"] = self.GetTransitionType() values["width"], values["height"] = self.Element.GetSize() return values - + def OnOK(self, event): """ Called when dialog OK button is pressed @@ -170,18 +175,18 @@ @param event: wx.Event from OK button """ message = None - + # Get transition type and value associated type, value = self.GetTransitionType() - + # Test that value associated to type is defined if type != "connection" and value == "": message = _("Form isn't complete. %s must be filled!") % type - + # Show error message if an error is detected if message is not None: self.ShowErrorMessage(message) - + else: # Call BlockPreviewDialog function BlockPreviewDialog.OnOK(self, event) @@ -192,10 +197,10 @@ @param event: wx.RadioButtonEvent """ # Refresh sensibility of control associated to transition types - for type, (radio, control) in self.TypeRadioButtons.iteritems(): + for _type, (radio, control) in self.TypeRadioButtons.iteritems(): if control is not None: control.Enable(radio.GetValue()) - + # Refresh preview panel self.RefreshPreview() event.Skip() @@ -233,6 +238,6 @@ self.Element = SFC_Transition(self.Preview) self.Element.SetType(*self.GetTransitionType()) self.Element.SetPriority(self.Priority.GetValue()) - + # Call BlockPreviewDialog function BlockPreviewDialog.RefreshPreview(self) diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/SearchInProjectDialog.py --- a/dialogs/SearchInProjectDialog.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/SearchInProjectDialog.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,76 +23,77 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import re + +from __future__ import absolute_import +import wx from plcopen.plcopen import * -import wx +from util.TranslationCatalogs import NoTranslate -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Search In Project Dialog -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def GetElementsChoices(): - _ = lambda x: x - return [("datatype", _("Data Type")), - ("function", _("Function")), - ("functionBlock", _("Function Block")), - ("program", _("Program")), + _ = NoTranslate + return [("datatype", _("Data Type")), + ("function", _("Function")), + ("functionBlock", _("Function Block")), + ("program", _("Program")), ("configuration", _("Configuration"))] + class SearchInProjectDialog(wx.Dialog): - + def __init__(self, parent): - wx.Dialog.__init__(self, parent, title=_('Search in Project'), - size=wx.Size(600, 350)) - + wx.Dialog.__init__(self, parent, title=_('Search in Project')) + main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=3, vgap=10) main_sizer.AddGrowableCol(0) main_sizer.AddGrowableRow(1) - + pattern_sizer = wx.FlexGridSizer(cols=2, hgap=5, rows=2, vgap=5) pattern_sizer.AddGrowableCol(0) - main_sizer.AddSizer(pattern_sizer, border=20, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(pattern_sizer, border=20, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) + pattern_label = wx.StaticText(self, label=_('Pattern to search:')) pattern_sizer.AddWindow(pattern_label, flag=wx.ALIGN_BOTTOM) - + self.CaseSensitive = wx.CheckBox(self, label=_('Case sensitive')) pattern_sizer.AddWindow(self.CaseSensitive, flag=wx.GROW) - - self.Pattern = wx.TextCtrl(self) + + self.Pattern = wx.TextCtrl(self, size=wx.Size(250, -1)) self.Bind(wx.EVT_TEXT, self.FindPatternChanged, self.Pattern) pattern_sizer.AddWindow(self.Pattern, flag=wx.GROW) self.Bind(wx.EVT_CHAR_HOOK, self.OnEscapeKey) self.RegularExpression = wx.CheckBox(self, label=_('Regular expression')) pattern_sizer.AddWindow(self.RegularExpression, flag=wx.GROW) - + scope_staticbox = wx.StaticBox(self, label=_('Scope')) scope_sizer = wx.StaticBoxSizer(scope_staticbox, wx.HORIZONTAL) - main_sizer.AddSizer(scope_sizer, border=20, - flag=wx.GROW|wx.LEFT|wx.RIGHT) - + main_sizer.AddSizer(scope_sizer, border=20, + flag=wx.GROW | wx.LEFT | wx.RIGHT) + scope_selection_sizer = wx.BoxSizer(wx.VERTICAL) - scope_sizer.AddSizer(scope_selection_sizer, 1, border=5, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.BOTTOM) - - self.WholeProject = wx.RadioButton(self, label=_('Whole Project'), - size=wx.Size(0, 24), style=wx.RB_GROUP) + scope_sizer.AddSizer(scope_selection_sizer, 1, border=5, + flag=wx.GROW | wx.TOP | wx.LEFT | wx.BOTTOM) + + self.WholeProject = wx.RadioButton(self, label=_('Whole Project'), style=wx.RB_GROUP) self.WholeProject.SetValue(True) self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, self.WholeProject) - scope_selection_sizer.AddWindow(self.WholeProject, border=5, - flag=wx.GROW|wx.BOTTOM) - - self.OnlyElements = wx.RadioButton(self, - label=_('Only Elements'), size=wx.Size(0, 24)) + scope_selection_sizer.AddWindow(self.WholeProject, border=5, + flag=wx.GROW | wx.BOTTOM) + + self.OnlyElements = wx.RadioButton(self, label=_('Only Elements')) self.Bind(wx.EVT_RADIOBUTTON, self.OnScopeChanged, self.OnlyElements) self.OnlyElements.SetValue(False) scope_selection_sizer.AddWindow(self.OnlyElements, flag=wx.GROW) - + self.ElementsList = wx.CheckListBox(self) self.ElementsList.Enable(False) - scope_sizer.AddWindow(self.ElementsList, 1, border=5, - flag=wx.GROW|wx.TOP|wx.RIGHT|wx.BOTTOM) + scope_sizer.AddWindow(self.ElementsList, 1, border=5, + flag=wx.GROW | wx.TOP | wx.RIGHT | wx.BOTTOM) buttons_sizer = wx.BoxSizer(wx.HORIZONTAL) main_sizer.AddSizer(buttons_sizer, border=20, @@ -105,11 +107,13 @@ self.CloseButton = wx.Button(self, label=_("Close")) self.Bind(wx.EVT_BUTTON, self.OnCloseButton, self.CloseButton) buttons_sizer.AddWindow(self.CloseButton) - + self.SetSizer(main_sizer) - - for name, label in GetElementsChoices(): + + for _name, label in GetElementsChoices(): self.ElementsList.Append(_(label)) + + self.Fit() self.infosPrev = {} self.criteria = {} self.Pattern.SetFocus() @@ -139,7 +143,7 @@ self.OnCloseButton(event) else: event.Skip() - + def OnFindButton(self, event): message = None infos = { @@ -151,7 +155,7 @@ infos["filter"] = "all" elif self.OnlyElements.GetValue(): infos["filter"] = [] - for index, (name, label) in enumerate(GetElementsChoices()): + for index, (name, _label) in enumerate(GetElementsChoices()): if self.ElementsList.IsChecked(index): infos["filter"].append(name) @@ -160,12 +164,12 @@ self.criteria = infos CompilePattern(self.criteria) self.infosPrev = infos - except: + except Exception: self.criteria.clear() message = _("Syntax error in regular expression of pattern to search!") - + if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() else: diff -r c1298e7ffe3a -r 8391c11477f4 dialogs/__init__.py --- a/dialogs/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/dialogs/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -25,25 +25,27 @@ # Package initialization -from ConnectionDialog import ConnectionDialog -from ActionBlockDialog import ActionBlockDialog -from FBDBlockDialog import FBDBlockDialog -from FBDVariableDialog import FBDVariableDialog -from LDElementDialog import LDElementDialog -from LDPowerRailDialog import LDPowerRailDialog -from SFCStepDialog import SFCStepDialog -from SFCStepNameDialog import SFCStepNameDialog -from SFCTransitionDialog import SFCTransitionDialog -from SFCDivergenceDialog import SFCDivergenceDialog -from ForceVariableDialog import ForceVariableDialog -from ArrayTypeDialog import ArrayTypeDialog -from DurationEditorDialog import DurationEditorDialog -from SearchInProjectDialog import SearchInProjectDialog -from BrowseLocationsDialog import BrowseLocationsDialog -from ProjectDialog import ProjectDialog -from PouDialog import PouDialog -from PouTransitionDialog import PouTransitionDialog -from PouActionDialog import PouActionDialog -from FindInPouDialog import FindInPouDialog -from BrowseValuesLibraryDialog import BrowseValuesLibraryDialog -from DiscoveryDialog import DiscoveryDialog +from __future__ import absolute_import + +from dialogs.ConnectionDialog import ConnectionDialog +from dialogs.ActionBlockDialog import ActionBlockDialog +from dialogs.FBDBlockDialog import FBDBlockDialog +from dialogs.FBDVariableDialog import FBDVariableDialog +from dialogs.LDElementDialog import LDElementDialog +from dialogs.LDPowerRailDialog import LDPowerRailDialog +from dialogs.SFCStepDialog import SFCStepDialog +from dialogs.SFCStepNameDialog import SFCStepNameDialog +from dialogs.SFCTransitionDialog import SFCTransitionDialog +from dialogs.SFCDivergenceDialog import SFCDivergenceDialog +from dialogs.ForceVariableDialog import ForceVariableDialog +from dialogs.ArrayTypeDialog import ArrayTypeDialog +from dialogs.DurationEditorDialog import DurationEditorDialog +from dialogs.SearchInProjectDialog import SearchInProjectDialog +from dialogs.BrowseLocationsDialog import BrowseLocationsDialog +from dialogs.ProjectDialog import ProjectDialog +from dialogs.PouDialog import PouDialog +from dialogs.PouTransitionDialog import PouTransitionDialog +from dialogs.PouActionDialog import PouActionDialog +from dialogs.FindInPouDialog import FindInPouDialog +from dialogs.BrowseValuesLibraryDialog import BrowseValuesLibraryDialog +from dialogs.DiscoveryDialog import DiscoveryDialog diff -r c1298e7ffe3a -r 8391c11477f4 doc/Makefile --- a/doc/Makefile Fri Mar 24 12:07:47 2017 +0000 +++ b/doc/Makefile Tue Jan 30 16:06:58 2018 +0100 @@ -38,6 +38,8 @@ endef $(foreach lang, $(langs), $(eval $(call by_lang,$(lang)))) +%.pot : gettext + echo "Build missing pot files" all: $(sitelist) mkdir -p $(BUILDDIR)/final diff -r c1298e7ffe3a -r 8391c11477f4 doc/conf.py --- a/doc/conf.py Fri Mar 24 12:07:47 2017 +0000 +++ b/doc/conf.py Tue Jan 30 16:06:58 2018 +0100 @@ -11,17 +11,15 @@ # All configuration values have a default; values that are commented out # serve to show the default. -import sys, os - # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. @@ -34,7 +32,7 @@ source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -54,40 +52,40 @@ # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None gettext_compact = False -locale_dirs = [ "locale/" ] +locale_dirs = ["locale/"] # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- @@ -99,26 +97,26 @@ # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, @@ -127,44 +125,44 @@ # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'Beremizdoc' @@ -173,42 +171,42 @@ # -- Options for LaTeX output -------------------------------------------------- latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', + # The paper size ('letterpaper' or 'a4paper'). + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'Beremiz.tex', u'Beremiz Documentation', - u'Beremiz Documentation Authors', 'manual'), + ('index', 'Beremiz.tex', u'Beremiz Documentation', + u'Beremiz Documentation Authors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -221,7 +219,7 @@ ] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ @@ -230,16 +228,16 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Beremiz', u'Beremiz Documentation', - u'Beremiz Documentation Authors', 'Beremiz', 'One line description of project.', - 'Miscellaneous'), + ('index', 'Beremiz', u'Beremiz Documentation', + u'Beremiz Documentation Authors', 'Beremiz', 'One line description of project.', + 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' diff -r c1298e7ffe3a -r 8391c11477f4 doc/manual/connectors_fr.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/manual/connectors_fr.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,183 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) is 21st century inquisition +# This file is distributed under the same license as the Beremiz package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Beremiz 1.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-08-09 14:06+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../../manual/connectors.rst:2 +msgid "Beremiz and Beremiz_service connectors" +msgstr "" + +#: ../../manual/connectors.rst:6 +msgid "To connect a PLC, Beremiz provides 2 types of connectors :" +msgstr "" + +#: ../../manual/connectors.rst:5 +msgid "a Pyro connector" +msgstr "" + +#: ../../manual/connectors.rst:6 +msgid "a WAMP connector" +msgstr "" + +#: ../../manual/connectors.rst:8 +msgid "" +"To configure the connection, you have to set the *URI_location* in your " +"project Config tab according to this documentation." +msgstr "" + +#: ../../manual/connectors.rst:11 +msgid "The Pyro connector" +msgstr "" + +#: ../../manual/connectors.rst:13 +msgid "" +"Pyro is an advanced and powerful Distributed Object Technology system " +"written entirely in Python. Beremiz_service spawns a Pyro server, serving a " +"PLCObject (see runtime/PLCObject.py). Therefore, Beremiz acts as a Pyro " +"client." +msgstr "" + +#: ../../manual/connectors.rst:16 +msgid "TODO:: link to PLCObject API documentation" +msgstr "" + +#: ../../manual/connectors.rst:22 ../../manual/connectors.rst:105 +msgid "URI_location :" +msgstr "" + +#: ../../manual/connectors.rst:19 +msgid "" +"LOCAL:// is a facility that starts the PLC service locally and connect " +"Beremiz to it via Pyro. This is intended for use in development stage." +msgstr "" + +#: ../../manual/connectors.rst:21 +msgid "" +"PYRO:// normal connection to a remote PLC. PLC default port is 3000." +msgstr "" + +#: ../../manual/connectors.rst:22 +msgid "PYROS:// SSL connection to a remote PLC, see below." +msgstr "" + +#: ../../manual/connectors.rst:24 +msgid "" +"more information about Pyro can be found on http://pythonhosted.org//Pyro/1-" +"intro.html" +msgstr "" + +#: ../../manual/connectors.rst:28 +msgid "Setup a Pyro SSL connection" +msgstr "" + +#: ../../manual/connectors.rst:30 +msgid "" +"Pyro v3 has a limited TLS/SSL support based on m2crypto. Pyro v4 had dropped " +"it. In order to have a full and reliable SSL, we recommand to use a TLS/SSL " +"wrapper as nginx, stub or stunnel." +msgstr "" + +#: ../../manual/connectors.rst:35 +msgid "TLS-PSK with stunnel" +msgstr "" + +#: ../../manual/connectors.rst:37 +msgid "" +"In this example, we setup a simple TLS-PSK connection according to rfc4279. " +"This ciphersuite avoid the need for public key operations and certificate " +"management. It is perfect for a performance-constrained environments with " +"limited CPU power as a PLC." +msgstr "" + +#: ../../manual/connectors.rst:43 +msgid "Needed :" +msgstr "" + +#: ../../manual/connectors.rst:43 +msgid "stunnel >= 5.09" +msgstr "" + +#: ../../manual/connectors.rst:45 +msgid "verify openssl support for PSK cipher::" +msgstr "" + +#: ../../manual/connectors.rst:51 +msgid "Client setup (Beremiz)" +msgstr "" + +#: ../../manual/connectors.rst:53 +msgid "" +"You need to choose an identity for your client, here *client1*. generate a " +"valid and strong key::" +msgstr "" + +#: ../../manual/connectors.rst:58 +msgid "write a stunnel client configuration file *stunnel-client.conf*::" +msgstr "" + +#: ../../manual/connectors.rst:69 +msgid "start stunnel client side::" +msgstr "" + +#: ../../manual/connectors.rst:73 +msgid "" +"You could now connect beremiz with classic URI_location = " +"PYRO://127.0.0.1:3002" +msgstr "" + +#: ../../manual/connectors.rst:77 +msgid "Server setup (PLC)" +msgstr "" + +#: ../../manual/connectors.rst:79 +msgid "import the client key in a keyfile psk.txt, concatening all client key." +msgstr "" + +#: ../../manual/connectors.rst:81 +msgid "write a stunnel server configuration file *stunnel-server.conf*::" +msgstr "" + +#: ../../manual/connectors.rst:90 +msgid "start stunnel server side::" +msgstr "" + +#: ../../manual/connectors.rst:94 +msgid "more documentation on stunnel http://www.stunnel.org/docs.html" +msgstr "" + +#: ../../manual/connectors.rst:97 +msgid "The WAMP connector" +msgstr "" + +#: ../../manual/connectors.rst:99 +msgid "" +"WAMP is an open standard WebSocket subprotocol that provides two application " +"messaging patterns in one unified protocol: Remote Procedure Calls + Publish " +"& Subscribe." +msgstr "" + +#: ../../manual/connectors.rst:102 +msgid "Beremiz WAMP connector implementation uses Autobahn and crossbar." +msgstr "" + +#: ../../manual/connectors.rst:105 +msgid "WAMP://127.0.0.1:8888#Automation#2534667845" +msgstr "" + +#: ../../manual/connectors.rst:107 +msgid "more information about WAMP can be found on http://wamp.ws/" +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 doc/manual/connectors_kr.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/manual/connectors_kr.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,183 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) is 21st century inquisition +# This file is distributed under the same license as the Beremiz package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Beremiz 1.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-08-09 14:06+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../../manual/connectors.rst:2 +msgid "Beremiz and Beremiz_service connectors" +msgstr "" + +#: ../../manual/connectors.rst:6 +msgid "To connect a PLC, Beremiz provides 2 types of connectors :" +msgstr "" + +#: ../../manual/connectors.rst:5 +msgid "a Pyro connector" +msgstr "" + +#: ../../manual/connectors.rst:6 +msgid "a WAMP connector" +msgstr "" + +#: ../../manual/connectors.rst:8 +msgid "" +"To configure the connection, you have to set the *URI_location* in your " +"project Config tab according to this documentation." +msgstr "" + +#: ../../manual/connectors.rst:11 +msgid "The Pyro connector" +msgstr "" + +#: ../../manual/connectors.rst:13 +msgid "" +"Pyro is an advanced and powerful Distributed Object Technology system " +"written entirely in Python. Beremiz_service spawns a Pyro server, serving a " +"PLCObject (see runtime/PLCObject.py). Therefore, Beremiz acts as a Pyro " +"client." +msgstr "" + +#: ../../manual/connectors.rst:16 +msgid "TODO:: link to PLCObject API documentation" +msgstr "" + +#: ../../manual/connectors.rst:22 ../../manual/connectors.rst:105 +msgid "URI_location :" +msgstr "" + +#: ../../manual/connectors.rst:19 +msgid "" +"LOCAL:// is a facility that starts the PLC service locally and connect " +"Beremiz to it via Pyro. This is intended for use in development stage." +msgstr "" + +#: ../../manual/connectors.rst:21 +msgid "" +"PYRO:// normal connection to a remote PLC. PLC default port is 3000." +msgstr "" + +#: ../../manual/connectors.rst:22 +msgid "PYROS:// SSL connection to a remote PLC, see below." +msgstr "" + +#: ../../manual/connectors.rst:24 +msgid "" +"more information about Pyro can be found on http://pythonhosted.org//Pyro/1-" +"intro.html" +msgstr "" + +#: ../../manual/connectors.rst:28 +msgid "Setup a Pyro SSL connection" +msgstr "" + +#: ../../manual/connectors.rst:30 +msgid "" +"Pyro v3 has a limited TLS/SSL support based on m2crypto. Pyro v4 had dropped " +"it. In order to have a full and reliable SSL, we recommand to use a TLS/SSL " +"wrapper as nginx, stub or stunnel." +msgstr "" + +#: ../../manual/connectors.rst:35 +msgid "TLS-PSK with stunnel" +msgstr "" + +#: ../../manual/connectors.rst:37 +msgid "" +"In this example, we setup a simple TLS-PSK connection according to rfc4279. " +"This ciphersuite avoid the need for public key operations and certificate " +"management. It is perfect for a performance-constrained environments with " +"limited CPU power as a PLC." +msgstr "" + +#: ../../manual/connectors.rst:43 +msgid "Needed :" +msgstr "" + +#: ../../manual/connectors.rst:43 +msgid "stunnel >= 5.09" +msgstr "" + +#: ../../manual/connectors.rst:45 +msgid "verify openssl support for PSK cipher::" +msgstr "" + +#: ../../manual/connectors.rst:51 +msgid "Client setup (Beremiz)" +msgstr "" + +#: ../../manual/connectors.rst:53 +msgid "" +"You need to choose an identity for your client, here *client1*. generate a " +"valid and strong key::" +msgstr "" + +#: ../../manual/connectors.rst:58 +msgid "write a stunnel client configuration file *stunnel-client.conf*::" +msgstr "" + +#: ../../manual/connectors.rst:69 +msgid "start stunnel client side::" +msgstr "" + +#: ../../manual/connectors.rst:73 +msgid "" +"You could now connect beremiz with classic URI_location = " +"PYRO://127.0.0.1:3002" +msgstr "" + +#: ../../manual/connectors.rst:77 +msgid "Server setup (PLC)" +msgstr "" + +#: ../../manual/connectors.rst:79 +msgid "import the client key in a keyfile psk.txt, concatening all client key." +msgstr "" + +#: ../../manual/connectors.rst:81 +msgid "write a stunnel server configuration file *stunnel-server.conf*::" +msgstr "" + +#: ../../manual/connectors.rst:90 +msgid "start stunnel server side::" +msgstr "" + +#: ../../manual/connectors.rst:94 +msgid "more documentation on stunnel http://www.stunnel.org/docs.html" +msgstr "" + +#: ../../manual/connectors.rst:97 +msgid "The WAMP connector" +msgstr "" + +#: ../../manual/connectors.rst:99 +msgid "" +"WAMP is an open standard WebSocket subprotocol that provides two application " +"messaging patterns in one unified protocol: Remote Procedure Calls + Publish " +"& Subscribe." +msgstr "" + +#: ../../manual/connectors.rst:102 +msgid "Beremiz WAMP connector implementation uses Autobahn and crossbar." +msgstr "" + +#: ../../manual/connectors.rst:105 +msgid "WAMP://127.0.0.1:8888#Automation#2534667845" +msgstr "" + +#: ../../manual/connectors.rst:107 +msgid "more information about WAMP can be found on http://wamp.ws/" +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 docutil/__init__.py --- a/docutil/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/docutil/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from dochtml import * -from docpdf import * -from docsvg import * + +from __future__ import absolute_import +from docutil.dochtml import * +from docutil.docpdf import * +from docutil.docsvg import * diff -r c1298e7ffe3a -r 8391c11477f4 docutil/dochtml.py --- a/docutil/dochtml.py Fri Mar 24 12:07:47 2017 +0000 +++ b/docutil/dochtml.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,28 +22,36 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx, os, wx.html, subprocess + +from __future__ import absolute_import +import subprocess +import wx +import wx.html HtmlFrameOpened = [] + def OpenHtmlFrame(self, title, file, size): - if title not in HtmlFrameOpened: - HtmlFrameOpened.append(title) - window = HtmlFrame(self, HtmlFrameOpened) - window.SetTitle(title) - window.SetHtmlPage(file) - window.SetClientSize(size) - window.Show() + if title not in HtmlFrameOpened: + HtmlFrameOpened.append(title) + window = HtmlFrame(self, HtmlFrameOpened) + window.SetTitle(title) + window.SetHtmlPage(file) + window.SetClientSize(size) + window.Show() + [ID_HTMLFRAME, ID_HTMLFRAMEHTMLCONTENT] = [wx.NewId() for _init_ctrls in range(2)] EVT_HTML_URL_CLICK = wx.NewId() + class HtmlWindowUrlClick(wx.PyEvent): def __init__(self, linkinfo): wx.PyEvent.__init__(self) self.SetEventType(EVT_HTML_URL_CLICK) self.linkinfo = (linkinfo.GetHref(), linkinfo.GetTarget()) - + + class UrlClickHtmlWindow(wx.html.HtmlWindow): """ HTML window that generates and OnLinkClicked event. @@ -51,47 +59,49 @@ """ def OnLinkClicked(self, linkinfo): wx.PostEvent(self, HtmlWindowUrlClick(linkinfo)) - + def Bind(self, event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY): if event == HtmlWindowUrlClick: self.Connect(-1, -1, EVT_HTML_URL_CLICK, handler) else: - wx.html.HtmlWindow.Bind(event, handler, source=source, id=id, id2=id2) + wx.html.HtmlWindow.Bind(self, event, handler, source=source, id=id, id2=id2) + class HtmlFrame(wx.Frame): - def _init_ctrls(self, prnt): - wx.Frame.__init__(self, id=ID_HTMLFRAME, name='HtmlFrame', - parent=prnt, pos=wx.Point(320, 231), size=wx.Size(853, 616), - style=wx.DEFAULT_FRAME_STYLE, title='') - self.SetIcon(prnt.icon) - self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) - - self.HtmlContent = UrlClickHtmlWindow(id=ID_HTMLFRAMEHTMLCONTENT, - name='HtmlContent', parent=self, pos=wx.Point(0, 0), - size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO|wx.html.HW_NO_SELECTION) - self.HtmlContent.Bind(HtmlWindowUrlClick, self.OnLinkClick) + def _init_ctrls(self, prnt): + self.SetIcon(prnt.icon) + self.Bind(wx.EVT_CLOSE, self.OnCloseFrame) - def __init__(self, parent, opened): - self._init_ctrls(parent) - self.HtmlFrameOpened = opened - - def SetHtmlCode(self, htmlcode): - self.HtmlContent.SetPage(htmlcode) - - def SetHtmlPage(self, htmlpage): - self.HtmlContent.LoadPage(htmlpage) - - def OnCloseFrame(self, event): - self.HtmlFrameOpened.remove(self.GetTitle()) - event.Skip() - - def OnLinkClick(self, event): - url = event.linkinfo[0] - try: - if wx.Platform == '__WXMSW__': - import webbrowser - webbrowser.open(url) - elif subprocess.call("firefox %s"%url, shell=True) != 0: - wx.MessageBox("""Firefox browser not found.\nPlease point your browser at :\n%s""" % url) - except ImportError: - wx.MessageBox('Please point your browser at: %s' % url) + self.HtmlContent = UrlClickHtmlWindow(id=ID_HTMLFRAMEHTMLCONTENT, + name='HtmlContent', parent=self, pos=wx.Point(0, 0), + size=wx.Size(-1, -1), style=wx.html.HW_SCROLLBAR_AUTO | wx.html.HW_NO_SELECTION) + self.HtmlContent.Bind(HtmlWindowUrlClick, self.OnLinkClick) + + def __init__(self, parent, opened): + wx.Frame.__init__(self, id=ID_HTMLFRAME, name='HtmlFrame', + parent=parent, pos=wx.Point(320, 231), + size=wx.Size(853, 616), + style=wx.DEFAULT_FRAME_STYLE, title='') + self._init_ctrls(parent) + self.HtmlFrameOpened = opened + + def SetHtmlCode(self, htmlcode): + self.HtmlContent.SetPage(htmlcode) + + def SetHtmlPage(self, htmlpage): + self.HtmlContent.LoadPage(htmlpage) + + def OnCloseFrame(self, event): + self.HtmlFrameOpened.remove(self.GetTitle()) + event.Skip() + + def OnLinkClick(self, event): + url = event.linkinfo[0] + try: + if wx.Platform == '__WXMSW__': + import webbrowser + webbrowser.open(url) + elif subprocess.call("firefox %s" % url, shell=True) != 0: + wx.MessageBox("""Firefox browser not found.\nPlease point your browser at :\n%s""" % url) + except ImportError: + wx.MessageBox('Please point your browser at: %s' % url) diff -r c1298e7ffe3a -r 8391c11477f4 docutil/docpdf.py --- a/docutil/docpdf.py Fri Mar 24 12:07:47 2017 +0000 +++ b/docutil/docpdf.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,10 +22,16 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx, os + +from __future__ import absolute_import +from __future__ import print_function +import os +import wx + readerexepath = None - + + def get_acroversion(): " Return version of Adobe Acrobat executable or None" import _winreg @@ -39,39 +45,42 @@ try: res = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, 'Software\\Adobe\\%s\\%s\\InstallPath' % (key, numver)) return res - except: + except Exception: pass return None -def open_win_pdf(readerexepath, pdffile, pagenum = None): - if pagenum != None : - os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", "/A", "page=%d=OpenActions" % pagenum, '"%s"'%pdffile) + +def open_win_pdf(readerexepath, pdffile, pagenum=None): + if pagenum is not None: + os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", "/A", "page=%d=OpenActions" % pagenum, '"%s"' % pdffile) else: - os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"'%pdffile) + os.spawnl(os.P_DETACH, readerexepath, "AcroRd32.exe", '"%s"' % pdffile) -def open_lin_pdf(readerexepath, pdffile, pagenum = None): - if pagenum == None : - os.system("%s -remote DS301 %s &"%(readerexepath, pdffile)) + +def open_lin_pdf(readerexepath, pdffile, pagenum=None): + if pagenum is None: + os.system("%s -remote DS301 %s &" % (readerexepath, pdffile)) else: - print "Open pdf %s at page %d"%(pdffile, pagenum) - os.system("%s -remote DS301 %s %d &"%(readerexepath, pdffile, pagenum)) + print("Open pdf %s at page %d" % (pdffile, pagenum)) + os.system("%s -remote DS301 %s %d &" % (readerexepath, pdffile, pagenum)) -def open_pdf(pdffile, pagenum = None): - if wx.Platform == '__WXMSW__' : + +def open_pdf(pdffile, pagenum=None): + if wx.Platform == '__WXMSW__': try: readerpath = get_acroversion() - except: + except Exception: wx.MessageBox("Acrobat Reader is not found or installed !") return None - + readerexepath = os.path.join(readerpath, "AcroRd32.exe") - if(os.path.isfile(readerexepath)): + if os.path.isfile(readerexepath): open_win_pdf(readerexepath, pdffile, pagenum) else: return None else: - readerexepath = os.path.join("/usr/bin","xpdf") - if(os.path.isfile(readerexepath)): + readerexepath = os.path.join("/usr/bin", "xpdf") + if os.path.isfile(readerexepath): open_lin_pdf(readerexepath, pdffile, pagenum) else: wx.MessageBox("xpdf is not found or installed !") diff -r c1298e7ffe3a -r 8391c11477f4 docutil/docsvg.py --- a/docutil/docsvg.py Fri Mar 24 12:07:47 2017 +0000 +++ b/docutil/docsvg.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,41 +22,52 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx, os, subprocess + +from __future__ import absolute_import +import os +import subprocess +import wx + def get_inkscape_path(): """ Return the Inkscape path """ import _winreg - svgexepath = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, - 'Software\\Classes\\svgfile\\shell\\Inkscape\\command') + try: + svgexepath = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, + 'Software\\Classes\\svgfile\\shell\\Inkscape\\command') + except OSError: + svgexepath = _winreg.QueryValue(_winreg.HKEY_LOCAL_MACHINE, + 'Software\\Classes\\inkscape.svg\\shell\\open\\command') svgexepath = svgexepath.replace('"%1"', '') return svgexepath.replace('"', '') + def open_win_svg(svgexepath, svgfile): """ Open Inkscape on Windows platform """ popenargs = [svgexepath] - if svgfile is not None : + if svgfile is not None: popenargs.append(svgfile) - subprocess.Popen(popenargs).pid + subprocess.Popen(popenargs) + def open_lin_svg(svgexepath, svgfile): """ Open Inkscape on Linux platform """ if os.path.isfile("/usr/bin/inkscape"): - os.system("%s %s &"%(svgexepath , svgfile)) - + os.system("%s %s &" % (svgexepath, svgfile)) + + def open_svg(svgfile): """ Generic function to open SVG file """ - if wx.Platform == '__WXMSW__' : + if wx.Platform == '__WXMSW__': try: open_win_svg(get_inkscape_path(), svgfile) - except: + except Exception: wx.MessageBox("Inkscape is not found or installed !") return None else: - svgexepath = os.path.join("/usr/bin","inkscape") - if(os.path.isfile(svgexepath)): + svgexepath = os.path.join("/usr/bin", "inkscape") + if os.path.isfile(svgexepath): open_lin_svg(svgexepath, svgfile) else: wx.MessageBox("Inkscape is not found or installed !") return None - diff -r c1298e7ffe3a -r 8391c11477f4 editors/CodeFileEditor.py --- a/editors/CodeFileEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/CodeFileEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re import wx @@ -32,12 +34,14 @@ from plcopen.plcopen import TestTextElement from plcopen.structures import TestIdentifier, IEC_KEYWORDS, DefaultType from controls import CustomGrid, CustomTable +from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS +from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL from editors.ConfTreeNodeEditor import ConfTreeNodeEditor from util.BitmapLibrary import GetBitmap -from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS -from controls.VariablePanel import VARIABLE_NAME_SUFFIX_MODEL +from util.TranslationCatalogs import NoTranslate from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD + [STC_CODE_ERROR, STC_CODE_SEARCH_RESULT, STC_CODE_SECTION] = range(15, 18) @@ -48,6 +52,7 @@ EDGE_COLUMN = 80 + class CodeEditor(CustomStyledTextCtrl): KEYWORDS = [] @@ -55,14 +60,14 @@ def __init__(self, parent, window, controler): CustomStyledTextCtrl.__init__(self, parent, -1, wx.DefaultPosition, - wx.Size(-1, 300), 0) + wx.Size(-1, 300), 0) self.SetMarginType(1, stc.STC_MARGIN_NUMBER) self.SetMarginWidth(1, 25) self.SetProperty("fold", "1") self.SetProperty("tab.timmy.whinge.level", "1") - self.SetMargins(0,0) + self.SetMargins(0, 0) self.SetViewWhiteSpace(False) @@ -133,12 +138,13 @@ for section in self.Controler.SECTIONS_NAMES: section_comment = " %s section " % (section) len_headers = EDGE_COLUMN - len(section_comment) - section_comment = self.COMMENT_HEADER * (len_headers / 2) + \ - section_comment + \ - self.COMMENT_HEADER * (len_headers - len_headers / 2) + section_comment = \ + self.COMMENT_HEADER * (len_headers / 2) + \ + section_comment + \ + self.COMMENT_HEADER * (len_headers - len_headers / 2) self.SectionsComments[section] = { - "comment": section_comment, + "comment": section_comment, } for i, section in enumerate(self.Controler.SECTIONS_NAMES): @@ -152,7 +158,7 @@ section_infos["comment"] + "(.*)" + section_end, re.DOTALL) - self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT|wx.stc.STC_MOD_BEFOREDELETE) + self.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT | wx.stc.STC_MOD_BEFOREDELETE) self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop) self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus) @@ -170,17 +176,17 @@ def OnModification(self, event): if not self.DisableEvents: mod_type = event.GetModificationType() - if not (mod_type&wx.stc.STC_PERFORMED_UNDO or mod_type&wx.stc.STC_PERFORMED_REDO): - if mod_type&wx.stc.STC_MOD_BEFOREINSERT: - if self.CurrentAction == None: + if not (mod_type & wx.stc.STC_PERFORMED_UNDO or mod_type & wx.stc.STC_PERFORMED_REDO): + if mod_type & wx.stc.STC_MOD_BEFOREINSERT: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: self.Controler.EndBuffering() self.StartBuffering() self.CurrentAction = ("Add", event.GetPosition()) wx.CallAfter(self.RefreshModel) - elif mod_type&wx.stc.STC_MOD_BEFOREDELETE: - if self.CurrentAction == None: + elif mod_type & wx.stc.STC_MOD_BEFOREDELETE: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: self.Controler.EndBuffering() @@ -193,10 +199,9 @@ def OnDoDrop(self, event): try: values = eval(event.GetDragText()) - except: + except Exception: values = event.GetDragText() if isinstance(values, tuple): - message = None if values[3] == self.Controler.GetCurrentLocation(): self.ResetBuffer() event.SetDragText(values[0]) @@ -226,7 +231,7 @@ self.ParentWindow.RefreshPageTitles() def ResetBuffer(self): - if self.CurrentAction != None: + if self.CurrentAction is not None: self.Controler.EndBuffering() self.CurrentAction = None @@ -258,7 +263,7 @@ self.SetText(new_text) new_cursor_pos = GetCursorPos(old_text, new_text) self.LineScroll(column, line) - if new_cursor_pos != None: + if new_cursor_pos is not None: self.GotoPos(new_cursor_pos) else: self.GotoPos(old_cursor_pos) @@ -319,11 +324,9 @@ newline_size = 1 # Disable to type any character in section header lines - if (self.GetLineState(self.LineFromPosition(current_pos)) and - not text_selected and - key not in NAVIGATION_KEYS + [ - wx.WXK_RETURN, - wx.WXK_NUMPAD_ENTER]): + if self.GetLineState(self.LineFromPosition(current_pos)) and \ + not text_selected and \ + key not in NAVIGATION_KEYS + [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: return # Disable to delete line between code and header lines @@ -335,8 +338,6 @@ return elif key == 32 and event.ControlDown(): - pos = self.GetCurrentPos() - # Tips if event.ShiftDown(): pass @@ -381,7 +382,7 @@ if braceAtCaret >= 0: braceOpposite = self.BraceMatch(braceAtCaret) - if braceAtCaret != -1 and braceOpposite == -1: + if braceAtCaret != -1 and braceOpposite == -1: self.BraceBadLight(braceAtCaret) else: self.BraceHighlight(braceAtCaret, braceOpposite) @@ -392,17 +393,17 @@ def OnMarginClick(self, event): # fold and unfold as needed - if evt.GetMargin() == 2: - if evt.GetShift() and evt.GetControl(): + if event.GetMargin() == 2: + if event.GetShift() and event.GetControl(): self.FoldAll() else: - lineClicked = self.LineFromPosition(evt.GetPosition()) + lineClicked = self.LineFromPosition(event.GetPosition()) if self.GetFoldLevel(lineClicked) & stc.STC_FOLDLEVELHEADERFLAG: - if evt.GetShift(): + if event.GetShift(): self.SetFoldExpanded(lineClicked, True) self.Expand(lineClicked, True, True, 1) - elif evt.GetControl(): + elif event.GetControl(): if self.GetFoldExpanded(lineClicked): self.SetFoldExpanded(lineClicked, False) self.Expand(lineClicked, False, True, 0) @@ -443,8 +444,6 @@ lineNum = lineNum + 1 - - def Expand(self, line, doExpand, force=False, visLevels=0, level=-1): lastChild = self.GetLastChild(line, level) line = line + 1 @@ -509,7 +508,7 @@ self.SearchResults = [ (start, end, SEARCH_RESULT_HIGHLIGHT) - for start, end, text in + for start, end, _text in TestTextElement(self.GetText(), search_params)] self.CurrentFindHighlight = None @@ -548,9 +547,9 @@ self.RemoveHighlight(*self.CurrentFindHighlight) self.CurrentFindHighlight = None -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView(True) @@ -575,8 +574,8 @@ def RemoveHighlight(self, start, end, highlight_type): highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) - if (highlight_type is not None and - (start, end, highlight_type) in self.Highlights): + if highlight_type is not None and \ + (start, end, highlight_type) in self.Highlights: self.Highlights.remove((start, end, highlight_type)) self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) @@ -596,9 +595,9 @@ self.SetStyling(len(self.GetText()) - highlight_end_pos, stc.STC_STYLE_DEFAULT) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helper for VariablesGrid values -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class VariablesTable(CustomTable): @@ -607,7 +606,7 @@ if col == 0: return row + 1 else: - return str(self.data[row].get(self.GetColLabelValue(col, False), "")) + return unicode(self.data[row].get(self.GetColLabelValue(col, False), "")) def _updateColAttrs(self, grid): """ @@ -617,8 +616,6 @@ Otherwise default to the default renderer. """ - typelist = None - accesslist = None for row in range(self.GetNumberRows()): for col in range(self.GetNumberCols()): editor = None @@ -660,7 +657,7 @@ ("UpVariableButton", "up", _("Move variable up")), ("DownVariableButton", "down", _("Move variable down"))]: button = wx.lib.buttons.GenBitmapButton(self, bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) controls_sizer.AddWindow(button, border=5, flag=wx.BOTTOM) @@ -676,12 +673,15 @@ self.ParentWindow = window self.Controler = controler - self.VariablesDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial": "", - "Description":"", "OnChange":"", "Options":""} - self.Table = VariablesTable(self, [], ["#", "Name","Type", "Initial", - "Description", "OnChange", "Options"]) - # self.Table = VariablesTable(self, [], [_("#"), _("Name"), _("Type"), _("Initial"), - # _("Description"), _("OnChange"), _("Options")]) + self.VariablesDefaultValue = { + "Name": "", + "Type": DefaultType, + "Initial": "", + "Description": "", + "OnChange": "", + "Options": "" + } + self.Table = VariablesTable(self, [], self.GetVariableTableColnames()) self.ColAlignements = [wx.ALIGN_RIGHT] + \ [wx.ALIGN_LEFT]*(len(self.VariablesDefaultValue)) self.ColSizes = [20, 150] + [130]*(len(self.VariablesDefaultValue)-1) @@ -706,7 +706,7 @@ name = row_content["Name"] start_idx = 0 row_content["Name"] = self.Controler.GenerateNewName( - name + "%d", start_idx) + name + "%d", start_idx) else: row_content = self.VariablesDefaultValue.copy() self.Table.InsertRow(new_row, row_content) @@ -737,6 +737,16 @@ self.VariablesGrid.SetColSize(col, self.ColSizes[col]) self.Table.ResetView(self.VariablesGrid) + def GetVariableTableColnames(self): + _ = NoTranslate + return ["#", + _("Name"), + _("Type"), + _("Initial"), + _("Description"), + _("OnChange"), + _("Options")] + def RefreshModel(self): self.Controler.SetVariables(self.Table.GetData()) self.RefreshBuffer() @@ -757,6 +767,11 @@ def DoGetBestSize(self): return self.ParentWindow.GetPanelBestSize() + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + def OnVariablesGridCellChange(self, event): row, col = event.GetRow(), event.GetCol() colname = self.Table.GetColLabelValue(col, False) @@ -780,10 +795,8 @@ wx.CallAfter(self.RefreshView) if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) - dialog.ShowModal() - dialog.Destroy() event.Veto() + wx.CallAfter(self.ShowErrorMessage, message) else: event.Skip() @@ -827,7 +840,7 @@ data_type = self.Table.GetValueByName(row, "Type") var_name = self.Table.GetValueByName(row, "Name") data = wx.TextDataObject(str((var_name, "Global", data_type, - self.Controler.GetCurrentLocation()))) + self.Controler.GetCurrentLocation()))) dragSource = wx.DropSource(self.VariablesGrid) dragSource.SetData(data) dragSource.DoDragDrop() @@ -835,9 +848,9 @@ event.Skip() -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # CodeFileEditor Main Frame Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class CodeFileEditor(ConfTreeNodeEditor): @@ -849,14 +862,15 @@ self.CodeEditorPanel.SetMinimumPaneSize(1) self.VariablesPanel = VariablesEditor(self.CodeEditorPanel, - self.ParentWindow, self.Controler) + self.ParentWindow, + self.Controler) if self.CODE_EDITOR is not None: self.CodeEditor = self.CODE_EDITOR(self.CodeEditorPanel, - self.ParentWindow, self.Controler) + self.ParentWindow, self.Controler) self.CodeEditorPanel.SplitHorizontally(self.VariablesPanel, - self.CodeEditor, 150) + self.CodeEditor, 150) else: self.CodeEditorPanel.Initialize(self.VariablesPanel) @@ -886,4 +900,3 @@ def Find(self, direction, search_params): self.CodeEditor.Find(direction, search_params) - diff -r c1298e7ffe3a -r 8391c11477f4 editors/ConfTreeNodeEditor.py --- a/editors/ConfTreeNodeEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/ConfTreeNodeEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,12 +23,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os + +from __future__ import absolute_import import types import wx -from EditorPanel import EditorPanel +from editors.EditorPanel import EditorPanel from IDEFrame import TITLE, FILEMENU, PROJECTTREE, PAGETITLES @@ -36,22 +38,25 @@ from util.BitmapLibrary import GetBitmap if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 16, - } + faces = { + 'times': 'Times New Roman', + 'mono': 'Courier New', + 'helv': 'Arial', + 'other': 'Comic Sans MS', + 'size': 16, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 18, - } + faces = { + 'times': 'Times', + 'mono': 'Courier', + 'helv': 'Helvetica', + 'other': 'new century schoolbook', + 'size': 18, + } SCROLLBAR_UNIT = 10 + class GenBitmapTextButton(wx.lib.buttons.GenBitmapTextButton): def _GetLabelSize(self): """ used internally """ @@ -70,17 +75,17 @@ def DrawLabel(self, dc, width, height, dw=0, dy=0): bmp = self.bmpLabel - if bmp != None: # if the bitmap is used + if bmp is not None: # if the bitmap is used if self.bmpDisabled and not self.IsEnabled(): bmp = self.bmpDisabled if self.bmpFocus and self.hasFocus: bmp = self.bmpFocus if self.bmpSelected and not self.up: bmp = self.bmpSelected - bw,bh = bmp.GetWidth(), bmp.GetHeight() + bw, bh = bmp.GetWidth(), bmp.GetHeight() if not self.up: dw = dy = self.labelDelta - hasMask = bmp.GetMask() != None + hasMask = bmp.GetMask() is not None else: bw = bh = 0 # no bitmap -> size is zero @@ -97,8 +102,8 @@ pos_x = (width-bw)/2+dw # adjust for bitmap and text to centre pos_y = (height-bh-th)/2+dy - if bmp !=None: - dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available + if bmp is not None: + dc.DrawBitmap(bmp, pos_x, pos_y, hasMask) # draw bitmap if available pos_x = (width-tw)/2+dw # adjust for bitmap and text to centre pos_y += bh + 2 @@ -109,19 +114,20 @@ """ Customized GenStaticBitmap, fix transparency redraw bug on wx2.8/win32, and accept image name as __init__ parameter, fail silently if file do not exist""" def __init__(self, parent, ID, bitmapname, - pos = wx.DefaultPosition, size = wx.DefaultSize, - style = 0, - name = "genstatbmp"): + pos=wx.DefaultPosition, size=wx.DefaultSize, + style=0, + name="genstatbmp"): bitmap = GetBitmap(bitmapname) if bitmap is None: bitmap = wx.EmptyBitmap(0, 0) wx.StaticBitmap.__init__(self, parent, ID, - bitmap, - pos, size, - style, - name) + bitmap, + pos, size, + style, + name) + class ConfTreeNodeEditor(EditorPanel): @@ -136,48 +142,54 @@ if tabs_num > 1 or self.SHOW_BASE_PARAMS: self.Editor = wx.Panel(parent, - style=wx.SUNKEN_BORDER|wx.SP_3D) + style=wx.SUNKEN_BORDER | wx.SP_3D) self.MainSizer = wx.BoxSizer(wx.VERTICAL) if self.SHOW_BASE_PARAMS: baseparamseditor_sizer = wx.BoxSizer(wx.HORIZONTAL) self.MainSizer.AddSizer(baseparamseditor_sizer, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) self.FullIECChannel = wx.StaticText(self.Editor, -1) self.FullIECChannel.SetFont( wx.Font(faces["size"], wx.DEFAULT, wx.NORMAL, - wx.BOLD, faceName = faces["helv"])) + wx.BOLD, faceName=faces["helv"])) baseparamseditor_sizer.AddWindow(self.FullIECChannel, - flag=wx.ALIGN_CENTER_VERTICAL) + flag=wx.ALIGN_CENTER_VERTICAL) updownsizer = wx.BoxSizer(wx.VERTICAL) baseparamseditor_sizer.AddSizer(updownsizer, border=5, - flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL) - - self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton(self.Editor, - bitmap=GetBitmap('IECCDown'), size=wx.Size(16, 16), style=wx.NO_BORDER) + flag=wx.LEFT | wx.ALIGN_CENTER_VERTICAL) + + self.IECCUpButton = wx.lib.buttons.GenBitmapTextButton( + self.Editor, + bitmap=GetBitmap('IECCDown'), + size=wx.Size(16, 16), + style=wx.NO_BORDER) self.IECCUpButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(1), - self.IECCUpButton) + self.IECCUpButton) updownsizer.AddWindow(self.IECCUpButton, flag=wx.ALIGN_LEFT) - self.IECCDownButton = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap('IECCUp'), size=wx.Size(16, 16), style=wx.NO_BORDER) + self.IECCDownButton = wx.lib.buttons.GenBitmapButton( + self.Editor, bitmap=GetBitmap('IECCUp'), + size=wx.Size(16, 16), style=wx.NO_BORDER) self.IECCDownButton.Bind(wx.EVT_BUTTON, self.GetItemChannelChangedFunction(-1), - self.IECCDownButton) + self.IECCDownButton) updownsizer.AddWindow(self.IECCDownButton, flag=wx.ALIGN_LEFT) self.ConfNodeName = wx.TextCtrl(self.Editor, - size=wx.Size(150, 25)) + size=wx.Size(150, 25)) self.ConfNodeName.SetFont( wx.Font(faces["size"] * 0.75, wx.DEFAULT, wx.NORMAL, - wx.BOLD, faceName = faces["helv"])) - self.ConfNodeName.Bind(wx.EVT_TEXT, - self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True), - self.ConfNodeName) - baseparamseditor_sizer.AddWindow(self.ConfNodeName, border=5, - flag=wx.LEFT|wx.RIGHT|wx.ALIGN_CENTER_VERTICAL) + wx.BOLD, faceName=faces["helv"])) + self.ConfNodeName.Bind( + wx.EVT_TEXT, + self.GetTextCtrlCallBackFunction(self.ConfNodeName, "BaseParams.Name", True), + self.ConfNodeName) + baseparamseditor_sizer.AddWindow( + self.ConfNodeName, border=5, + flag=wx.LEFT | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL) buttons_sizer = self.GenerateMethodButtonSizer() baseparamseditor_sizer.AddSizer(buttons_sizer, flag=wx.ALIGN_CENTER) @@ -206,11 +218,11 @@ if self.SHOW_PARAMS and len(self.Controler.GetParamsAttributes()) > 0: - panel_style = wx.TAB_TRAVERSAL|wx.HSCROLL|wx.VSCROLL + panel_style = wx.TAB_TRAVERSAL | wx.HSCROLL | wx.VSCROLL if self.ConfNodeNoteBook is None and parent != self.Editor: panel_style |= wx.SUNKEN_BORDER self.ParamsEditor = wx.ScrolledWindow(parent, - style=panel_style) + style=panel_style) self.ParamsEditor.Bind(wx.EVT_SIZE, self.OnParamsEditorResize) self.ParamsEditor.Bind(wx.EVT_SCROLLWIN, self.OnParamsEditorScroll) @@ -221,7 +233,7 @@ self.ConfNodeParamsSizer = wx.BoxSizer(wx.VERTICAL) self.ParamsEditorSizer.AddSizer(self.ConfNodeParamsSizer, border=5, - flag=wx.LEFT|wx.RIGHT|wx.BOTTOM) + flag=wx.LEFT | wx.RIGHT | wx.BOTTOM) self.RefreshConfNodeParamsSizer() @@ -293,16 +305,17 @@ self.Thaw() def GenerateMethodButtonSizer(self): - normal_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName = faces["helv"]) - mouseover_bt_font=wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, underline=True, faceName = faces["helv"]) + normal_bt_font = wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName=faces["helv"]) + mouseover_bt_font = wx.Font(faces["size"] / 3, wx.DEFAULT, wx.NORMAL, wx.NORMAL, faceName=faces["helv"], underline=True) msizer = wx.BoxSizer(wx.HORIZONTAL) for confnode_method in self.Controler.ConfNodeMethods: - if "method" in confnode_method and confnode_method.get("shown",True): + if "method" in confnode_method and confnode_method.get("shown", True): button = GenBitmapTextButton(self.Editor, - bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")), - label=confnode_method["name"], style=wx.NO_BORDER) + bitmap=GetBitmap(confnode_method.get("bitmap", "Unknown")), + label=confnode_method["name"], + style=wx.NO_BORDER) button.SetFont(normal_bt_font) button.SetToolTipString(confnode_method["tooltip"]) if confnode_method.get("push", False): @@ -310,6 +323,7 @@ else: button.Bind(wx.EVT_BUTTON, self.GetButtonCallBackFunction(confnode_method["method"]), button) # a fancy underline on mouseover + def setFontStyle(b, s): def fn(event): b.SetFont(s) @@ -318,19 +332,19 @@ return fn button.Bind(wx.EVT_ENTER_WINDOW, setFontStyle(button, mouseover_bt_font)) button.Bind(wx.EVT_LEAVE_WINDOW, setFontStyle(button, normal_bt_font)) - #hack to force size to mini - if not confnode_method.get("enabled",True): + # hack to force size to mini + if not confnode_method.get("enabled", True): button.Disable() msizer.AddWindow(button, flag=wx.ALIGN_CENTER) return msizer - def GenerateSizerElements(self, sizer, elements, path, clean = True): + def GenerateSizerElements(self, sizer, elements, path, clean=True): if clean: sizer.Clear(True) first = True for element_infos in elements: if path: - element_path = "%s.%s"%(path, element_infos["name"]) + element_path = "%s.%s" % (path, element_infos["name"]) else: element_path = element_infos["name"] if element_infos["type"] == "element": @@ -340,7 +354,7 @@ if value is not None: label += " - %s" % _(value) staticbox = wx.StaticBox(self.ParamsEditor, - label=_(label), size=wx.Size(10, 0)) + label=_(label), size=wx.Size(10, 0)) staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) flags = (wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) if first: @@ -356,15 +370,16 @@ if first: flags |= wx.TOP sizer.AddSizer(boxsizer, border=5, flag=flags) - staticbitmap = GenStaticBitmap(ID=-1, bitmapname=element_infos["name"], - name="%s_bitmap"%element_infos["name"], parent=self.ParamsEditor, + staticbitmap = GenStaticBitmap( + ID=-1, bitmapname=element_infos["name"], + name="%s_bitmap" % element_infos["name"], parent=self.ParamsEditor, pos=wx.Point(0, 0), size=wx.Size(24, 24), style=0) boxsizer.AddWindow(staticbitmap, border=5, flag=wx.RIGHT) statictext = wx.StaticText(self.ParamsEditor, - label="%s:"%_(element_infos["name"])) + label="%s:" % _(element_infos["name"])) boxsizer.AddWindow(statictext, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.RIGHT) + flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT) if isinstance(element_infos["type"], types.ListType): if isinstance(element_infos["value"], types.TupleType): @@ -372,7 +387,7 @@ boxsizer.AddSizer(browse_boxsizer) textctrl = wx.TextCtrl(self.ParamsEditor, - size=wx.Size(275, -1), style=wx.TE_READONLY) + size=wx.Size(275, -1), style=wx.TE_READONLY) if element_infos["value"] is not None: textctrl.SetValue(element_infos["value"][0]) value_infos = element_infos["value"][1] @@ -380,8 +395,7 @@ value_infos = None browse_boxsizer.AddWindow(textctrl) - button = wx.Button(self.ParamsEditor, - label="...", size=wx.Size(25, 25)) + button = wx.Button(self.ParamsEditor, label="...") browse_boxsizer.AddWindow(button) button.Bind(wx.EVT_BUTTON, self.GetBrowseCallBackFunction(element_infos["name"], textctrl, element_infos["type"], @@ -389,19 +403,20 @@ button) else: combobox = wx.ComboBox(self.ParamsEditor, - size=wx.Size(300, -1), style=wx.CB_READONLY) + size=wx.Size(300, -1), style=wx.CB_READONLY) boxsizer.AddWindow(combobox) if element_infos["use"] == "optional": combobox.Append("") if len(element_infos["type"]) > 0 and isinstance(element_infos["type"][0], types.TupleType): - for choice, xsdclass in element_infos["type"]: + for choice, _xsdclass in element_infos["type"]: combobox.Append(choice) name = element_infos["name"] value = element_infos["value"] staticbox = wx.StaticBox(self.ParamsEditor, - label="%s - %s"%(_(name), _(value)), size=wx.Size(10, 0)) + label="%s - %s" % (_(name), _(value)), + size=wx.Size(10, 0)) staticboxsizer = wx.StaticBoxSizer(staticbox, wx.VERTICAL) sizer.AddSizer(staticboxsizer, border=5, flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) self.GenerateSizerElements(staticboxsizer, element_infos["children"], element_path) @@ -424,7 +439,8 @@ if "max" in element_infos["type"]: scmax = element_infos["type"]["max"] spinctrl = wx.SpinCtrl(self.ParamsEditor, - size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + size=wx.Size(300, -1), + style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT) spinctrl.SetRange(scmin, scmax) boxsizer.AddWindow(spinctrl) if element_infos["value"] is not None: @@ -443,14 +459,15 @@ self.GetCheckBoxCallBackFunction(checkbox, element_path), checkbox) - elif element_infos["type"] in ["unsignedLong", "long","integer"]: + elif element_infos["type"] in ["unsignedLong", "long", "integer"]: if element_infos["type"].startswith("unsigned"): scmin = 0 else: scmin = -(2**31) scmax = 2**31-1 spinctrl = wx.SpinCtrl(self.ParamsEditor, - size=wx.Size(300, -1), style=wx.SP_ARROW_KEYS|wx.ALIGN_RIGHT) + size=wx.Size(300, -1), + style=wx.SP_ARROW_KEYS | wx.ALIGN_RIGHT) spinctrl.SetRange(scmin, scmax) boxsizer.AddWindow(spinctrl) if element_infos["value"] is not None: @@ -477,11 +494,10 @@ sizer.Layout() self.RefreshScrollbars() - def GetItemChannelChangedFunction(self, dir): def OnConfNodeTreeItemChannelChanged(event): confnode_IECChannel = self.Controler.BaseParams.getIEC_Channel() - res = self.SetConfNodeParamsAttribute("BaseParams.IEC_Channel", confnode_IECChannel + dir) + self.SetConfNodeParamsAttribute("BaseParams.IEC_Channel", confnode_IECChannel + dir) wx.CallAfter(self.RefreshIECChannelControlsState) wx.CallAfter(self.ParentWindow._Refresh, TITLE, FILEMENU, PROJECTTREE) event.Skip() @@ -500,7 +516,7 @@ # Disable button to prevent re-entrant call event.GetEventObject().Disable() # Call - getattr(self.Controler,method)() + getattr(self.Controler, method)() # Re-enable button event.GetEventObject().Enable() @@ -519,7 +535,7 @@ def GetChoiceContentCallBackFunction(self, choicectrl, staticboxsizer, path): def OnChoiceContentChanged(event): - res = self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) + self.SetConfNodeParamsAttribute(path, choicectrl.GetStringSelection()) wx.CallAfter(self.RefreshConfNodeParamsSizer) event.Skip() return OnChoiceContentChanged @@ -547,6 +563,7 @@ def GetBrowseCallBackFunction(self, name, textctrl, library, value_infos, path): infos = [value_infos] + def OnBrowseButton(event): dialog = BrowseValuesLibraryDialog(self, name, library, infos[0]) if dialog.ShowModal() == wx.ID_OK: @@ -566,7 +583,8 @@ posy = max(0, min(ystart, (maxy - window_size[1]) / SCROLLBAR_UNIT)) self.ParamsEditor.Scroll(posx, posy) self.ParamsEditor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, - maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, posx, posy) + maxx / SCROLLBAR_UNIT, maxy / SCROLLBAR_UNIT, + posx, posy) def OnParamsEditorResize(self, event): self.RefreshScrollbars() @@ -578,4 +596,3 @@ control.DismissListBox() self.Refresh() event.Skip() - diff -r c1298e7ffe3a -r 8391c11477f4 editors/DataTypeEditor.py --- a/editors/DataTypeEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/DataTypeEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,41 +23,47 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re from types import TupleType import wx import wx.grid import wx.lib.buttons - from plcopen.structures import IEC_KEYWORDS, TestIdentifier, DefaultType from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD -from controls import CustomEditableListBox, CustomGrid, CustomTable +from controls import CustomEditableListBox, CustomGrid, CustomTable, CustomIntCtrl from dialogs import ArrayTypeDialog -from EditorPanel import EditorPanel +from editors.EditorPanel import EditorPanel from util.BitmapLibrary import GetBitmap - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate + +# ------------------------------------------------------------------------------- # Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- DIMENSION_MODEL = re.compile("([0-9]+)\.\.([0-9]+)$") + def AppendMenu(parent, help, id, kind, text): parent.Append(help=help, id=id, kind=kind, text=text) + def GetElementsTableColnames(): - _ = lambda x : x + _ = NoTranslate return ["#", _("Name"), _("Type"), _("Initial Value")] + def GetDatatypeTypes(): - _ = lambda x : x + _ = NoTranslate return [_("Directly"), _("Subrange"), _("Enumerated"), _("Array"), _("Structure")] -DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()]) - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Structure Elements Table -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class ElementsTable(CustomTable): @@ -74,10 +81,10 @@ return row + 1 colname = self.GetColLabelValue(col, False) value = self.data[row].get(colname, "") - + if colname == "Type" and isinstance(value, TupleType): if value[0] == "array": - return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "..".join(x), value[2])), value[1]) + return "ARRAY [%s] OF %s" % (",".join(map("..".join, value[2])), value[1]) return value def SetValue(self, row, col, value): @@ -133,9 +140,10 @@ col_highlights = row_highlights.setdefault(infos[1], []) col_highlights.append(highlight_type) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Datatype Editor class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + class DataTypeEditor(EditorPanel): @@ -148,21 +156,21 @@ top_sizer = wx.BoxSizer(wx.HORIZONTAL) self.MainSizer.AddSizer(top_sizer, border=5, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) derivation_type_label = wx.StaticText(self.Editor, label=_('Derivation Type:')) top_sizer.AddWindow(derivation_type_label, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT) + flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT | wx.RIGHT) self.DerivationType = wx.ComboBox(self.Editor, - size=wx.Size(200, -1), style=wx.CB_READONLY) + size=wx.Size(200, -1), style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnDerivationTypeChanged, self.DerivationType) - top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW|wx.RIGHT) + top_sizer.AddWindow(self.DerivationType, border=5, flag=wx.GROW | wx.RIGHT) typeinfos_staticbox = wx.StaticBox(self.Editor, label=_('Type infos:')) typeinfos_sizer = wx.StaticBoxSizer(typeinfos_staticbox, wx.HORIZONTAL) self.MainSizer.AddSizer(typeinfos_sizer, border=5, - flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) # Panel for Directly derived data types @@ -172,25 +180,25 @@ directly_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) directly_basetype_label = wx.StaticText(self.DirectlyPanel, - label=_('Base Type:')) + label=_('Base Type:')) directly_panel_sizer.AddWindow(directly_basetype_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.DirectlyBaseType = wx.ComboBox(self.DirectlyPanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.DirectlyBaseType) directly_panel_sizer.AddWindow(self.DirectlyBaseType, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) directly_initialvalue_label = wx.StaticText(self.DirectlyPanel, - label=_('Initial Value:')) + label=_('Initial Value:')) directly_panel_sizer.AddWindow(directly_initialvalue_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.DirectlyInitialValue = wx.TextCtrl(self.DirectlyPanel, - style=wx.TE_PROCESS_ENTER|wx.TE_RICH) + style=wx.TE_PROCESS_ENTER | wx.TE_RICH) self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.DirectlyInitialValue) directly_panel_sizer.AddWindow(self.DirectlyInitialValue, 1, border=5, - flag=wx.ALL) + flag=wx.ALL) self.DirectlyPanel.SetSizer(directly_panel_sizer) @@ -202,49 +210,48 @@ subrange_panel_sizer = wx.GridSizer(cols=4, hgap=5, rows=3, vgap=0) subrange_basetype_label = wx.StaticText(self.SubrangePanel, - label=_('Base Type:')) + label=_('Base Type:')) subrange_panel_sizer.AddWindow(subrange_basetype_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.SubrangeBaseType = wx.ComboBox(self.SubrangePanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnSubrangeBaseTypeChanged, - self.SubrangeBaseType) + self.SubrangeBaseType) subrange_panel_sizer.AddWindow(self.SubrangeBaseType, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) subrange_initialvalue_label = wx.StaticText(self.SubrangePanel, - label=_('Initial Value:')) + label=_('Initial Value:')) subrange_panel_sizer.AddWindow(subrange_initialvalue_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) - - self.SubrangeInitialValue = wx.SpinCtrl(self.SubrangePanel, - style=wx.TAB_TRAVERSAL) - self.Bind(wx.EVT_SPINCTRL, self.OnInfosChanged, self.SubrangeInitialValue) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) + + self.SubrangeInitialValue = CustomIntCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) + self.SubrangeInitialValue.Bind(CustomIntCtrl.EVT_CUSTOM_INT, self.OnInfosChanged) subrange_panel_sizer.AddWindow(self.SubrangeInitialValue, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) subrange_minimum_label = wx.StaticText(self.SubrangePanel, label=_('Minimum:')) subrange_panel_sizer.AddWindow(subrange_minimum_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) - - self.SubrangeMinimum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) - self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMinimumChanged, self.SubrangeMinimum) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) + + self.SubrangeMinimum = CustomIntCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) + self.SubrangeMinimum.Bind(CustomIntCtrl.EVT_CUSTOM_INT, self.OnSubrangeMinimumChanged) subrange_panel_sizer.AddWindow(self.SubrangeMinimum, 1, border=5, - flag=wx.GROW|wx.ALL) - - for i in xrange(2): + flag=wx.GROW | wx.ALL) + + for dummy in xrange(2): subrange_panel_sizer.AddWindow(wx.Size(0, 0), 1) subrange_maximum_label = wx.StaticText(self.SubrangePanel, - label=_('Maximum:')) + label=_('Maximum:')) subrange_panel_sizer.AddWindow(subrange_maximum_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) - - self.SubrangeMaximum = wx.SpinCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) - self.Bind(wx.EVT_SPINCTRL, self.OnSubrangeMaximumChanged, self.SubrangeMaximum) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) + + self.SubrangeMaximum = CustomIntCtrl(self.SubrangePanel, style=wx.TAB_TRAVERSAL) + self.SubrangeMaximum.Bind(CustomIntCtrl.EVT_CUSTOM_INT, self.OnSubrangeMaximumChanged) subrange_panel_sizer.AddWindow(self.SubrangeMaximum, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) self.SubrangePanel.SetSizer(subrange_panel_sizer) @@ -255,29 +262,32 @@ enumerated_panel_sizer = wx.BoxSizer(wx.HORIZONTAL) - self.EnumeratedValues = CustomEditableListBox(self.EnumeratedPanel, - label=_("Values:"), style=wx.gizmos.EL_ALLOW_NEW| - wx.gizmos.EL_ALLOW_EDIT| - wx.gizmos.EL_ALLOW_DELETE) + self.EnumeratedValues = CustomEditableListBox( + self.EnumeratedPanel, + label=_("Values:"), + style=(wx.gizmos.EL_ALLOW_NEW | + wx.gizmos.EL_ALLOW_EDIT | + wx.gizmos.EL_ALLOW_DELETE)) setattr(self.EnumeratedValues, "_OnLabelEndEdit", self.OnEnumeratedValueEndEdit) for func in ["_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: setattr(self.EnumeratedValues, func, self.OnEnumeratedValuesChanged) enumerated_panel_sizer.AddWindow(self.EnumeratedValues, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) enumerated_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) enumerated_panel_sizer.AddSizer(enumerated_panel_rightsizer, 1) enumerated_initialvalue_label = wx.StaticText(self.EnumeratedPanel, - label=_('Initial Value:')) + label=_('Initial Value:')) enumerated_panel_rightsizer.AddWindow(enumerated_initialvalue_label, 1, - border=5, flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + border=5, + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.EnumeratedInitialValue = wx.ComboBox(self.EnumeratedPanel, - style=wx.CB_READONLY) + style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.EnumeratedInitialValue) enumerated_panel_rightsizer.AddWindow(self.EnumeratedInitialValue, 1, - border=5, flag=wx.ALL) + border=5, flag=wx.ALL) self.EnumeratedPanel.SetSizer(enumerated_panel_sizer) @@ -296,36 +306,38 @@ array_basetype_label = wx.StaticText(self.ArrayPanel, label=_('Base Type:')) array_panel_leftSizer.AddWindow(array_basetype_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.ArrayBaseType = wx.ComboBox(self.ArrayPanel, style=wx.CB_READONLY) self.Bind(wx.EVT_COMBOBOX, self.OnInfosChanged, self.ArrayBaseType) array_panel_leftSizer.AddWindow(self.ArrayBaseType, 1, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) array_panel_rightsizer = wx.BoxSizer(wx.HORIZONTAL) array_panel_sizer.AddSizer(array_panel_rightsizer, flag=wx.GROW) array_initialvalue_label = wx.StaticText(self.ArrayPanel, - label=_('Initial Value:')) + label=_('Initial Value:')) array_panel_rightsizer.AddWindow(array_initialvalue_label, 1, border=5, - flag=wx.ALIGN_CENTER_VERTICAL|wx.ALL) + flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL) self.ArrayInitialValue = wx.TextCtrl(self.ArrayPanel, - style=wx.TE_PROCESS_ENTER|wx.TE_RICH) + style=wx.TE_PROCESS_ENTER | wx.TE_RICH) self.Bind(wx.EVT_TEXT_ENTER, self.OnReturnKeyPressed, self.ArrayInitialValue) array_panel_rightsizer.AddWindow(self.ArrayInitialValue, 1, border=5, - flag=wx.ALL) - - self.ArrayDimensions = CustomEditableListBox(self.ArrayPanel, - label=_("Dimensions:"), style=wx.gizmos.EL_ALLOW_NEW| - wx.gizmos.EL_ALLOW_EDIT| - wx.gizmos.EL_ALLOW_DELETE) + flag=wx.ALL) + + self.ArrayDimensions = CustomEditableListBox( + self.ArrayPanel, + label=_("Dimensions:"), + style=(wx.gizmos.EL_ALLOW_NEW | + wx.gizmos.EL_ALLOW_EDIT | + wx.gizmos.EL_ALLOW_DELETE)) for func in ["_OnLabelEndEdit", "_OnAddButton", "_OnDelButton", "_OnUpButton", "_OnDownButton"]: setattr(self.ArrayDimensions, func, self.OnDimensionsChanged) array_panel_sizer.AddWindow(self.ArrayDimensions, 0, border=5, - flag=wx.GROW|wx.ALL) + flag=wx.GROW | wx.ALL) self.ArrayPanel.SetSizer(array_panel_sizer) @@ -342,10 +354,10 @@ structure_button_sizer.AddGrowableCol(0) structure_button_sizer.AddGrowableRow(0) structure_panel_sizer.AddSizer(structure_button_sizer, 0, border=5, - flag=wx.ALL|wx.GROW) + flag=wx.ALL | wx.GROW) structure_elements_label = wx.StaticText(self.StructurePanel, - label=_('Elements :')) + label=_('Elements :')) structure_button_sizer.AddWindow(structure_elements_label, flag=wx.ALIGN_BOTTOM) for name, bitmap, help in [ @@ -354,17 +366,19 @@ ("StructureUpButton", "up", _("Move element up")), ("StructureDownButton", "down", _("Move element down"))]: button = wx.lib.buttons.GenBitmapButton(self.StructurePanel, - bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), + style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) structure_button_sizer.AddWindow(button) self.StructureElementsGrid = CustomGrid(self.StructurePanel, - size=wx.Size(0, 150), style=wx.VSCROLL) + size=wx.Size(0, 150), style=wx.VSCROLL) self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, - self.OnStructureElementsGridCellChange) + self.OnStructureElementsGridCellChange) self.StructureElementsGrid.Bind(wx.grid.EVT_GRID_EDITOR_SHOWN, - self.OnStructureElementsGridEditorShown) + self.OnStructureElementsGridEditorShown) structure_panel_sizer.AddWindow(self.StructureElementsGrid, flag=wx.GROW) self.StructurePanel.SetSizer(structure_panel_sizer) @@ -374,7 +388,7 @@ def __init__(self, parent, tagname, window, controler): EditorPanel.__init__(self, parent, tagname, window, controler) - self.StructureElementDefaultValue = {"Name" : "", "Type" : DefaultType, "Initial Value" : ""} + self.StructureElementDefaultValue = {"Name": "", "Type": DefaultType, "Initial Value": ""} self.StructureElementsTable = ElementsTable(self, [], GetElementsTableColnames()) self.StructureColSizes = [40, 150, 100, 250] self.StructureColAlignements = [wx.ALIGN_CENTER, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT] @@ -385,6 +399,8 @@ "Up": self.StructureUpButton, "Down": self.StructureDownButton}) + self.DATATYPE_TYPES_DICT = dict([(_(datatype), datatype) for datatype in GetDatatypeTypes()]) + def _AddStructureElement(new_row): self.StructureElementsTable.InsertRow(new_row, self.StructureElementDefaultValue.copy()) self.RefreshTypeInfos() @@ -496,7 +512,7 @@ self.EnumeratedInitialValue.SetStringSelection(type_infos["initial"]) elif type_infos["type"] == "Array": self.ArrayBaseType.SetStringSelection(type_infos["base_type"]) - self.ArrayDimensions.SetStrings(map(lambda x : "..".join(x), type_infos["dimensions"])) + self.ArrayDimensions.SetStrings(map("..".join, type_infos["dimensions"])) self.ArrayInitialValue.SetValue(type_infos["initial"]) elif type_infos["type"] == "Structure": self.StructureElementsTable.SetData(type_infos["elements"]) @@ -547,12 +563,12 @@ index = event.GetIndex() if index >= len(values) or values[index].upper() != text.upper(): if text.upper() in [value.upper() for value in values]: - message = wx.MessageDialog(self, _("\"%s\" value already defined!")%text, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" value already defined!") % text, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() event.Veto() elif text.upper() in IEC_KEYWORDS: - message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%text, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!") % text, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() else: @@ -570,39 +586,37 @@ wx.CallAfter(self.RefreshTypeInfos) event.Skip() + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + def OnStructureElementsGridCellChange(self, event): row, col = event.GetRow(), event.GetCol() colname = self.StructureElementsTable.GetColLabelValue(col, False) value = self.StructureElementsTable.GetValue(row, col) if colname == "Name": + message = None if not TestIdentifier(value): - message = wx.MessageDialog(self, _("\"%s\" is not a valid identifier!")%value, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - event.Veto() + message = _("\"%s\" is not a valid identifier!") % value elif value.upper() in IEC_KEYWORDS: - message = wx.MessageDialog(self, _("\"%s\" is a keyword. It can't be used!")%value, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - event.Veto() -## elif value.upper() in self.PouNames: -## message = wx.MessageDialog(self, "A pou with \"%s\" as name exists!"%value, "Error", wx.OK|wx.ICON_ERROR) -## message.ShowModal() -## message.Destroy() -## event.Veto() + message = _("\"%s\" is a keyword. It can't be used!") % value +# elif value.upper() in self.PouNames: +# message = _("A pou with \"%s\" as name exists!")%value elif value.upper() in [var["Name"].upper() for idx, var in enumerate(self.StructureElementsTable.GetData()) if idx != row]: - message = wx.MessageDialog(self, _("An element named \"%s\" already exists in this structure!")%value, _("Error"), wx.OK|wx.ICON_ERROR) - message.ShowModal() - message.Destroy() - event.Veto() + message = _("An element named \"%s\" already exists in this structure!") % value else: self.RefreshTypeInfos() wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) -## old_value = self.Table.GetOldValue() -## if old_value != "": -## self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) -## self.Controler.BufferProject() +# old_value = self.Table.GetOldValue() +# if old_value != "": +# self.Controler.UpdateEditedElementUsedVariable(self.TagName, old_value, value) +# self.Controler.BufferProject() event.Skip() + + if message is not None: + event.Veto() + wx.CallAfter(self.ShowErrorMessage, message) else: self.RefreshTypeInfos() wx.CallAfter(self.StructureElementsTable.ResetView, self.StructureElementsGrid) @@ -635,15 +649,15 @@ AppendMenu(type_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=_("Array")) self.Bind(wx.EVT_MENU, self.ElementArrayTypeFunction, id=new_id) -## functionblock_menu = wx.Menu(title='') -## bodytype = self.Controler.GetEditedElementBodyType(self.TagName) -## pouname, poutype = self.Controler.GetEditedElementType(self.TagName) -## if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]: -## for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName): -## new_id = wx.NewId() -## AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) -## self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) -## type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) +# functionblock_menu = wx.Menu(title='') +# bodytype = self.Controler.GetEditedElementBodyType(self.TagName) +# pouname, poutype = self.Controler.GetEditedElementType(self.TagName) +# if classtype in ["Input","Output","InOut","External","Global"] or poutype != "function" and bodytype in ["ST", "IL"]: +# for functionblock_type in self.Controler.GetFunctionBlockTypes(self.TagName): +# new_id = wx.NewId() +# AppendMenu(functionblock_menu, help='', id=new_id, kind=wx.ITEM_NORMAL, text=functionblock_type) +# self.Bind(wx.EVT_MENU, self.GetVariableTypeFunction(functionblock_type), id=new_id) +# type_menu.AppendMenu(wx.NewId(), _("Function Block Types"), functionblock_menu) rect = self.StructureElementsGrid.BlockToDeviceRect((row, col), (row, col)) self.StructureElementsGrid.PopupMenuXY(type_menu, rect.x + rect.width, rect.y + self.StructureElementsGrid.GetColLabelSize()) @@ -672,7 +686,7 @@ dialog.Destroy() def RefreshDisplayedInfos(self): - selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] + selected = self.DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] if selected != self.CurrentPanel: if self.CurrentPanel == "Directly": self.DirectlyPanel.Hide() @@ -710,17 +724,17 @@ range = self.Controler.GetDataTypeRange(self.SubrangeBaseType.GetStringSelection()) if range is not None: min_value, max_value = range - self.SubrangeMinimum.SetRange(min_value, max_value) + self.SubrangeMinimum.SetBounds(min_value, max_value) self.SubrangeMinimum.SetValue(min(max(min_value, self.SubrangeMinimum.GetValue()), max_value)) - self.SubrangeMaximum.SetRange(min_value, max_value) + self.SubrangeMaximum.SetBounds(min_value, max_value) self.SubrangeMaximum.SetValue(min(max(min_value, self.SubrangeMaximum.GetValue()), max_value)) def RefreshSubrangeInitialValueRange(self): - self.SubrangeInitialValue.SetRange(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()) + self.SubrangeInitialValue.SetBounds(self.SubrangeMinimum.GetValue(), self.SubrangeMaximum.GetValue()) def RefreshTypeInfos(self): - selected = DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] - infos = {"type" : selected} + selected = self.DATATYPE_TYPES_DICT[self.DerivationType.GetStringSelection()] + infos = {"type": selected} if selected == "Directly": infos["base_type"] = self.DirectlyBaseType.GetStringSelection() infos["initial"] = self.DirectlyInitialValue.GetValue() @@ -742,14 +756,14 @@ for dimensions in self.ArrayDimensions.GetStrings(): result = DIMENSION_MODEL.match(dimensions) if result is None: - message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!") % dimensions, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() self.RefreshView() return bounds = result.groups() if int(bounds[0]) >= int(bounds[1]): - message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.")%dimensions, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("\"%s\" value isn't a valid array dimension!\nRight value must be greater than left value.") % dimensions, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() self.RefreshView() @@ -764,9 +778,9 @@ self.ParentWindow.RefreshFileMenu() self.ParentWindow.RefreshEditMenu() -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() @@ -782,7 +796,8 @@ control.SetBackgroundColour(wx.NullColour) control.SetForegroundColour(wx.NullColour) elif isinstance(control, wx.TextCtrl): - value = control.GetValue() + value = control.GetValueStr() if isinstance(control, CustomIntCtrl) else \ + control.GetValue() control.SetStyle(0, len(value), wx.TextAttr(wx.NullColour)) elif isinstance(control, wx.gizmos.EditableListBox): listctrl = control.GetListCtrl() @@ -792,7 +807,7 @@ self.StructureElementsTable.ClearHighlights(highlight_type) self.RefreshView() - def AddHighlight(self, infos, start, end ,highlight_type): + def AddHighlight(self, infos, start, end, highlight_type): self.Highlights.append((infos, start, end, highlight_type)) self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) @@ -814,4 +829,3 @@ listctrl.SetItemBackgroundColour(infos[1], highlight_type[0]) listctrl.SetItemTextColour(infos[1], highlight_type[1]) listctrl.Select(listctrl.FocusedItem, False) - diff -r c1298e7ffe3a -r 8391c11477f4 editors/DebugViewer.py --- a/editors/DebugViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/DebugViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,25 +22,27 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from threading import Lock, Timer from time import time as gettime import wx -REFRESH_PERIOD = 0.1 # Minimum time between 2 refresh -DEBUG_REFRESH_LOCK = Lock() # Common refresh lock for all debug viewers - -#------------------------------------------------------------------------------- +REFRESH_PERIOD = 0.1 # Minimum time between 2 refresh +DEBUG_REFRESH_LOCK = Lock() # Common refresh lock for all debug viewers + +# ------------------------------------------------------------------------------- # Debug Viewer Class -#------------------------------------------------------------------------------- - -""" -Class that implements common behavior of every viewers able to display debug -values -""" - -class DebugViewer: - +# ------------------------------------------------------------------------------- + + +class DebugViewer(object): + """ + Class that implements common behavior of every viewers able to display debug + values + """ + def __init__(self, producer, debug, subscribe_tick=True): """ Constructor @@ -52,49 +54,49 @@ """ self.Debug = debug self.SubscribeTick = subscribe_tick - + # Flag indicating that consumer value update inhibited # (DebugViewer is refreshing) self.Inhibited = False - + # List of data consumers subscribed to DataProducer self.DataConsumers = {} - + # Time stamp indicating when last refresh have been initiated self.LastRefreshTime = gettime() # Flag indicating that DebugViewer has acquire common debug lock self.HasAcquiredLock = False # Lock for access to the two preceding variable self.AccessLock = Lock() - + # Timer to refresh Debug Viewer one last time in the case that a new # value have been received during refresh was inhibited and no one # after refresh was activated self.LastRefreshTimer = None # Lock for access to the timer self.TimerAccessLock = Lock() - + # Set DataProducer and subscribe tick if needed self.SetDataProducer(producer) - + def __del__(self): """ Destructor """ # Unsubscribe all data consumers self.UnsubscribeAllDataConsumers() - + # Delete reference to DataProducer self.DataProducer = None - + # Stop last refresh timer if self.LastRefreshTimer is not None: self.LastRefreshTimer.cancel() - + # Release Common debug lock if DebugViewer has acquired it if self.HasAcquiredLock: DEBUG_REFRESH_LOCK.release() - + def SetDataProducer(self, producer): """ Set Data Producer @@ -103,37 +105,37 @@ # In the case that tick need to be subscribed and DebugViewer is # debugging if self.SubscribeTick and self.Debug: - + # Subscribe tick to new data producer if producer is not None: producer.SubscribeDebugIECVariable("__tick__", self, True) - + # Unsubscribe tick from old data producer if getattr(self, "DataProducer", None) is not None: self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self) - + # Save new data producer self.DataProducer = producer - + def IsDebugging(self): """ Get flag indicating if Debug Viewer is debugging @return: Debugging flag """ return self.Debug - + def Inhibit(self, inhibit): """ Set consumer value update inhibit flag @param inhibit: Inhibit flag """ # Inhibit every data consumers in list - for consumer, iec_path in self.DataConsumers.iteritems(): + for consumer, _iec_path in self.DataConsumers.iteritems(): consumer.Inhibit(inhibit) - + # Save inhibit flag self.Inhibited = inhibit - + def AddDataConsumer(self, iec_path, consumer, buffer_list=False): """ Subscribe data consumer to DataProducer @@ -145,19 +147,19 @@ # Return immediately if no DataProducer defined if self.DataProducer is None: return None - + # Subscribe data consumer to DataProducer result = self.DataProducer.SubscribeDebugIECVariable( - iec_path, consumer, buffer_list) + iec_path, consumer, buffer_list) if result is not None and consumer != self: - + # Store data consumer if successfully subscribed and inform # consumer of variable data type self.DataConsumers[consumer] = iec_path consumer.SetDataType(self.GetDataType(iec_path)) - + return result - + def RemoveDataConsumer(self, consumer): """ Unsubscribe data consumer from DataProducer @@ -165,12 +167,11 @@ """ # Remove consumer from data consumer list iec_path = self.DataConsumers.pop(consumer, None) - + # Unsubscribe consumer from DataProducer if iec_path is not None: - self.DataProducer.UnsubscribeDebugIECVariable( - iec_path, consumer) - + self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer) + def SubscribeAllDataConsumers(self): """ Called to Subscribe all data consumers contained in DebugViewer. @@ -179,24 +180,23 @@ # Subscribe tick if needed if self.SubscribeTick and self.Debug and self.DataProducer is not None: self.DataProducer.SubscribeDebugIECVariable("__tick__", self, True) - + def UnsubscribeAllDataConsumers(self, tick=True): """ Called to Unsubscribe all data consumers. """ if self.DataProducer is not None: - + # Unscribe tick if needed if self.SubscribeTick and tick and self.Debug: self.DataProducer.UnsubscribeDebugIECVariable("__tick__", self) - + # Unsubscribe all data consumers in list for consumer, iec_path in self.DataConsumers.iteritems(): - self.DataProducer.UnsubscribeDebugIECVariable( - iec_path, consumer) - + self.DataProducer.UnsubscribeDebugIECVariable(iec_path, consumer) + self.DataConsumers = {} - + def GetDataType(self, iec_path): """ Return variable data type. @@ -204,20 +204,20 @@ @return: variable data type (None if not found) """ if self.DataProducer is not None: - + # Search for variable informations in project compilation files data_type = self.DataProducer.GetDebugIECVariableType( - iec_path.upper()) + iec_path.upper()) if data_type is not None: return data_type - + # Search for variable informations in project data infos = self.DataProducer.GetInstanceInfos(iec_path) if infos is not None: return infos.type - + return None - + def IsNumType(self, data_type): """ Indicate if data type given is a numeric data type @@ -226,9 +226,9 @@ """ if self.DataProducer is not None: return self.DataProducer.IsNumType(data_type) - + return False - + def ForceDataValue(self, iec_path, value): """ Force PLC variable value @@ -237,7 +237,7 @@ """ if self.DataProducer is not None: self.DataProducer.ForceDebugIECVariable(iec_path, value) - + def ReleaseDataValue(self, iec_path): """ Release PLC variable value @@ -245,40 +245,40 @@ """ if self.DataProducer is not None: self.DataProducer.ReleaseDebugIECVariable(iec_path) - + def NewDataAvailable(self, ticks): """ Called by DataProducer for each tick captured @param tick: PLC tick captured - All other parameters are passed to refresh function + All other parameters are passed to refresh function """ # Stop last refresh timer self.TimerAccessLock.acquire() if self.LastRefreshTimer is not None: self.LastRefreshTimer.cancel() - self.LastRefreshTimer=None + self.LastRefreshTimer = None self.TimerAccessLock.release() - + # Only try to refresh DebugViewer if it is visible on screen and not # already refreshing if self.IsShown() and not self.Inhibited: - + # Try to get acquire common refresh lock if minimum period between # two refresh has expired if gettime() - self.LastRefreshTime > REFRESH_PERIOD and \ DEBUG_REFRESH_LOCK.acquire(False): self.StartRefreshing() - + # If common lock wasn't acquired for any reason, restart last # refresh timer else: self.StartLastRefreshTimer() - + # In the case that DebugViewer isn't visible on screen and has already # acquired common refresh lock, reset DebugViewer elif not self.IsShown() and self.HasAcquiredLock: DebugViewer.RefreshNewData(self) - + def ShouldRefresh(self): """ Callback function called when last refresh timer expired @@ -286,15 +286,15 @@ """ # Cancel if DebugViewer is not visible on screen if self and self.IsShown(): - + # Try to acquire common refresh lock if DEBUG_REFRESH_LOCK.acquire(False): self.StartRefreshing() - + # Restart last refresh timer if common refresh lock acquired failed else: self.StartLastRefreshTimer() - + def StartRefreshing(self): """ Called to initiate a refresh of DebugViewer @@ -306,13 +306,13 @@ self.HasAcquiredLock = True self.LastRefreshTime = gettime() self.AccessLock.release() - + # Inhibit data consumer value update self.Inhibit(True) - + # Initiate DebugViewer refresh wx.CallAfter(self.RefreshNewData) - + def StartLastRefreshTimer(self): """ Called to start last refresh timer for the minimum time between 2 @@ -324,19 +324,19 @@ REFRESH_PERIOD, self.ShouldRefresh) self.LastRefreshTimer.start() self.TimerAccessLock.release() - + def RefreshNewData(self): """ Called to refresh DebugViewer according to values received by data consumers May be overridden by inherited classes Can receive any parameters depending on what is needed by inherited - class + class """ if self: # Activate data consumer value update self.Inhibit(False) - + # Release common refresh lock if acquired and update # last refresh time self.AccessLock.acquire() diff -r c1298e7ffe3a -r 8391c11477f4 editors/EditorPanel.py --- a/editors/EditorPanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/EditorPanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,71 +22,75 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from controls import VariablePanel + class EditorPanel(wx.SplitterWindow): - + VARIABLE_PANEL_TYPE = None - + def _init_Editor(self, prnt): self.Editor = None - + def _init_MenuItems(self): self.MenuItems = [] - + def _init_ctrls(self, parent): - wx.SplitterWindow.__init__(self, parent, - style=wx.SUNKEN_BORDER|wx.SP_3D) self.SetMinimumPaneSize(1) - + self._init_MenuItems() - + if self.VARIABLE_PANEL_TYPE is not None: self.VariableEditor = VariablePanel(self, self, self.Controler, self.VARIABLE_PANEL_TYPE, self.Debug) self.VariableEditor.SetTagName(self.TagName) else: self.VariableEditor = None - + self._init_Editor(self) - + if self.Editor is not None and self.VariableEditor is not None: self.SplitHorizontally(self.VariableEditor, self.Editor, 200) elif self.VariableEditor is not None: self.Initialize(self.VariableEditor) elif self.Editor is not None: self.Initialize(self.Editor) - + def __init__(self, parent, tagname, window, controler, debug=False): + wx.SplitterWindow.__init__(self, parent, + style=wx.SUNKEN_BORDER | wx.SP_3D) + self.ParentWindow = window self.Controler = controler self.TagName = tagname self.Icon = None self.Debug = debug - + self._init_ctrls(parent) - + def SetTagName(self, tagname): self.TagName = tagname if self.VARIABLE_PANEL_TYPE is not None: self.VariableEditor.SetTagName(tagname) - + def GetTagName(self): return self.TagName - + def Select(self): self.ParentWindow.EditProjectElement(None, self.GetTagName(), True) - + def GetTitle(self): return ".".join(self.TagName.split("::")[1:]) - + def GetIcon(self): return self.Icon - + def SetIcon(self, icon): self.Icon = icon - + def IsViewing(self, tagname): return self.GetTagName() == tagname @@ -98,54 +102,54 @@ def ResetBuffer(self): pass - + def IsModified(self): return False - + def CheckSaveBeforeClosing(self): return True - + def Save(self): pass - + def SaveAs(self): pass - + def GetBufferState(self): if self.Controler is not None: return self.Controler.GetBufferState() return False, False - + def Undo(self): if self.Controler is not None: self.Controler.LoadPrevious() self.RefreshView() - + def Redo(self): if self.Controler is not None: self.Controler.LoadNext() self.RefreshView() - + def Find(self, direction, search_params): pass - + def HasNoModel(self): return False - + def RefreshView(self, variablepanel=True): if variablepanel: self.RefreshVariablePanel() - + def RefreshVariablePanel(self): if self.VariableEditor is not None: self.VariableEditor.RefreshView() - + def GetConfNodeMenuItems(self): return self.MenuItems - + def RefreshConfNodeMenu(self, confnode_menu): pass - + def _Refresh(self, *args): self.ParentWindow._Refresh(*args) @@ -159,7 +163,7 @@ def RemoveHighlight(self, infos, start, end, highlight_type): if self.VariableEditor is not None and infos[0] in ["var_local", "var_input", "var_output", "var_inout"]: self.VariableEditor.RemoveVariableHighlight(infos[1:], highlight_type) - + def ClearHighlights(self, highlight_type=None): if self.VariableEditor is not None: self.VariableEditor.ClearHighlights(highlight_type) diff -r c1298e7ffe3a -r 8391c11477f4 editors/FileManagementPanel.py --- a/editors/FileManagementPanel.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/FileManagementPanel.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,51 +22,55 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import os import shutil import wx import wx.lib.buttons -from EditorPanel import EditorPanel +from editors.EditorPanel import EditorPanel from util.BitmapLibrary import GetBitmap from controls import FolderTree FILTER = _("All files (*.*)|*.*|CSV files (*.csv)|*.csv") + class FileManagementPanel(EditorPanel): - + def _init_Editor(self, parent): self.Editor = wx.Panel(parent) - + main_sizer = wx.BoxSizer(wx.HORIZONTAL) - + left_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW|wx.ALL) - + main_sizer.AddSizer(left_sizer, 1, border=5, flag=wx.GROW | wx.ALL) + managed_dir_label = wx.StaticText(self.Editor, label=_(self.TagName) + ":") - left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW|wx.BOTTOM) - + left_sizer.AddWindow(managed_dir_label, border=5, flag=wx.GROW | wx.BOTTOM) + self.ManagedDir = FolderTree(self.Editor, self.Folder, FILTER) left_sizer.AddWindow(self.ManagedDir, 1, flag=wx.GROW) - + managed_treectrl = self.ManagedDir.GetTreeCtrl() self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, managed_treectrl) if self.EnableDragNDrop: self.Bind(wx.EVT_TREE_BEGIN_DRAG, self.OnTreeBeginDrag, managed_treectrl) - + button_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(button_sizer, border=5, - flag=wx.ALL|wx.ALIGN_CENTER_VERTICAL) - + main_sizer.AddSizer(button_sizer, border=5, + flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL) + for idx, (name, bitmap, help) in enumerate([ ("DeleteButton", "remove_element", _("Remove file from left folder")), ("LeftCopyButton", "LeftCopy", _("Copy file from right folder to left")), ("RightCopyButton", "RightCopy", _("Copy file from left folder to right")), ("EditButton", "edit", _("Edit file"))]): - button = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap(bitmap), - size=wx.Size(28, 28), style=wx.NO_BORDER) + button = wx.lib.buttons.GenBitmapButton( + self.Editor, + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) if idx > 0: @@ -75,62 +79,62 @@ flag = 0 self.Bind(wx.EVT_BUTTON, getattr(self, "On" + name), button) button_sizer.AddWindow(button, border=20, flag=flag) - + right_sizer = wx.BoxSizer(wx.VERTICAL) - main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW|wx.ALL) - + main_sizer.AddSizer(right_sizer, 1, border=5, flag=wx.GROW | wx.ALL) + if wx.Platform == '__WXMSW__': system_dir_label = wx.StaticText(self.Editor, label=_("My Computer:")) else: system_dir_label = wx.StaticText(self.Editor, label=_("Home Directory:")) - right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW|wx.BOTTOM) - + right_sizer.AddWindow(system_dir_label, border=5, flag=wx.GROW | wx.BOTTOM) + self.SystemDir = FolderTree(self.Editor, self.HomeDirectory, FILTER, False) right_sizer.AddWindow(self.SystemDir, 1, flag=wx.GROW) - + system_treectrl = self.SystemDir.GetTreeCtrl() self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnTreeItemChanged, system_treectrl) - + self.Editor.SetSizer(main_sizer) - + def __init__(self, parent, controler, name, folder, enable_dragndrop=False): self.Folder = os.path.realpath(folder) self.EnableDragNDrop = enable_dragndrop - + if wx.Platform == '__WXMSW__': self.HomeDirectory = "/" else: self.HomeDirectory = os.path.expanduser("~") - + EditorPanel.__init__(self, parent, name, None, None) - + self.Controler = controler - + self.EditableFileExtensions = [] self.EditButton.Hide() - + self.SetIcon(GetBitmap("FOLDER")) - + def __del__(self): self.Controler.OnCloseEditor(self) - + def GetTitle(self): return _(self.TagName) - + def SetEditableFileExtensions(self, extensions): self.EditableFileExtensions = extensions if len(self.EditableFileExtensions) > 0: self.EditButton.Show() - + def RefreshView(self): self.ManagedDir.RefreshTree() self.SystemDir.RefreshTree() self.RefreshButtonsState() - + def RefreshButtonsState(self): managed_filepath = self.ManagedDir.GetPath() system_filepath = self.SystemDir.GetPath() - + self.DeleteButton.Enable(os.path.isfile(managed_filepath)) self.LeftCopyButton.Enable(os.path.isfile(system_filepath)) self.RightCopyButton.Enable(os.path.isfile(managed_filepath)) @@ -138,22 +142,23 @@ self.EditButton.Enable( os.path.isfile(managed_filepath) and os.path.splitext(managed_filepath)[1] in self.EditableFileExtensions) - + def OnTreeItemChanged(self, event): self.RefreshButtonsState() event.Skip() - + def OnDeleteButton(self, event): filepath = self.ManagedDir.GetPath() if os.path.isfile(filepath): - folder, filename = os.path.split(filepath) - - dialog = wx.MessageDialog(self, - _("Do you really want to delete the file '%s'?") % filename, - _("Delete File"), wx.YES_NO|wx.ICON_QUESTION) + _folder, filename = os.path.split(filepath) + + dialog = wx.MessageDialog(self, + _("Do you really want to delete the file '%s'?") % filename, + _("Delete File"), + wx.YES_NO | wx.ICON_QUESTION) remove = dialog.ShowModal() == wx.ID_YES dialog.Destroy() - + if remove: os.remove(filepath) self.ManagedDir.RefreshTree() @@ -161,29 +166,30 @@ def OnEditButton(self, event): filepath = self.ManagedDir.GetPath() - if (os.path.isfile(filepath) and - os.path.splitext(filepath)[1] in self.EditableFileExtensions): + if os.path.isfile(filepath) and \ + os.path.splitext(filepath)[1] in self.EditableFileExtensions: self.Controler._OpenView(filepath + "::") event.Skip() - + def CopyFile(self, src, dst): if os.path.isfile(src): - src_folder, src_filename = os.path.split(src) + _src_folder, src_filename = os.path.split(src) if os.path.isfile(dst): - dst_folder, dst_filename = os.path.split(dst) + dst_folder, _dst_filename = os.path.split(dst) else: dst_folder = dst - + dst_filepath = os.path.join(dst_folder, src_filename) if os.path.isfile(dst_filepath): - dialog = wx.MessageDialog(self, - _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, - _("Replace File"), wx.YES_NO|wx.ICON_QUESTION) + dialog = wx.MessageDialog( + self, + _("The file '%s' already exist.\nDo you want to replace it?") % src_filename, + _("Replace File"), wx.YES_NO | wx.ICON_QUESTION) copy = dialog.ShowModal() == wx.ID_YES dialog.Destroy() else: copy = True - + if copy: shutil.copyfile(src, dst_filepath) return dst_filepath @@ -202,7 +208,7 @@ self.SystemDir.RefreshTree() self.SystemDir.SetPath(filepath) event.Skip() - + def OnTreeBeginDrag(self, event): filepath = self.ManagedDir.GetPath() if os.path.isfile(filepath): @@ -211,4 +217,3 @@ dragSource = wx.DropSource(self) dragSource.SetData(data) dragSource.DoDragDrop() - diff -r c1298e7ffe3a -r 8391c11477f4 editors/IECCodeViewer.py --- a/editors/IECCodeViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/IECCodeViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,19 +22,22 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from editors.TextViewer import TextViewer from plcopen.plcopen import TestTextElement + class IECCodeViewer(TextViewer): - + def __del__(self): TextViewer.__del__(self) if getattr(self, "_OnClose"): self._OnClose(self) - + def Paste(self): if self.Controler is not None: TextViewer.Paste(self) - + def Search(self, criteria): return [((self.TagName, "body", 0),) + result for result in TestTextElement(self.Editor.GetText(), criteria)] diff -r c1298e7ffe3a -r 8391c11477f4 editors/LDViewer.py --- a/editors/LDViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/LDViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,11 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +from types import * + import wx -import time -from types import * - -from Viewer import * + +from editors.Viewer import * + def ExtractNextBlocks(block, block_list): current_list = [block] @@ -43,13 +46,14 @@ if "input" in connectors: input_connectors = [connectors["input"]] for connector in input_connectors: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): next = wire.EndConnected.GetParentBlock() if not isinstance(next, LD_PowerRail) and next not in block_list: block_list.append(next) next_list.append(next) current_list = next_list - + + def CalcBranchSize(elements, stops): branch_size = 0 stop_list = stops @@ -58,19 +62,19 @@ element_tree = {} for element in elements: if element not in element_tree: - element_tree[element] = {"parents":["start"], "children":[], "weight":None} + element_tree[element] = {"parents": ["start"], "children": [], "weight": None} GenerateTree(element, element_tree, stop_list) elif element_tree[element]: element_tree[element]["parents"].append("start") - remove_stops = {"start":[], "stop":[]} + remove_stops = {"start": [], "stop": []} for element, values in element_tree.items(): if "stop" in values["children"]: removed = [] for child in values["children"]: if child != "stop": -## if child in elements: -## RemoveElement(child, element_tree) -## removed.append(child) + # if child in elements: + # RemoveElement(child, element_tree) + # removed.append(child) if "start" in element_tree[child]["parents"]: if element not in remove_stops["stop"]: remove_stops["stop"].append(element) @@ -89,16 +93,17 @@ branch_size += values["weight"] else: return 1 - #print branch_size return branch_size + def RemoveElement(remove, element_tree): if remove in element_tree and element_tree[remove]: for child in element_tree[remove]["children"]: if child != "stop": RemoveElement(child, element_tree) element_tree.pop(remove) -## element_tree[remove] = None +# element_tree[remove] = None + def GenerateTree(element, element_tree, stop_list): if element in element_tree: @@ -112,23 +117,24 @@ if "input" in connectors: input_connectors = [connectors["input"]] for connector in input_connectors: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): next = wire.EndConnected.GetParentBlock() if isinstance(next, LD_PowerRail) and next.GetType() == LEFTRAIL or next in stop_list: -## for remove in element_tree[element]["children"]: -## RemoveElement(remove, element_tree) -## element_tree[element]["children"] = ["stop"] + # for remove in element_tree[element]["children"]: + # RemoveElement(remove, element_tree) + # element_tree[element]["children"] = ["stop"] element_tree[element]["children"].append("stop") -## elif element_tree[element]["children"] == ["stop"]: -## element_tree[next] = None + # elif element_tree[element]["children"] == ["stop"]: + # element_tree[next] = None elif next not in element_tree or element_tree[next]: element_tree[element]["children"].append(next) if next in element_tree: element_tree[next]["parents"].append(element) else: - element_tree[next] = {"parents":[element], "children":[], "weight":None} + element_tree[next] = {"parents": [element], "children": [], "weight": None} GenerateTree(next, element_tree, stop_list) + def CalcWeight(element, element_tree): weight = 0 parts = None @@ -156,26 +162,25 @@ element_tree[element]["weight"] = max(1, weight / parts) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Ladder Diagram Graphic elements Viewer class -#------------------------------------------------------------------------------- - - -""" -Class derived from Viewer class that implements a Viewer of Ladder Diagram -""" +# ------------------------------------------------------------------------------- + class LD_Viewer(Viewer): - - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + """ + Class derived from Viewer class that implements a Viewer of Ladder Diagram + """ + + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath) self.Rungs = [] self.RungComments = [] self.CurrentLanguage = "LD" -#------------------------------------------------------------------------------- -# Refresh functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Refresh functions + # ------------------------------------------------------------------------------- def ResetView(self): self.Rungs = [] @@ -194,7 +199,7 @@ self.RungComments.insert(i, None) else: self.RungComments.insert(i, None) - + def loadInstance(self, instance, ids, selection): Viewer.loadInstance(self, instance, ids, selection) if self.GetDrawingMode() != FREEDRAWING_MODE: @@ -212,12 +217,15 @@ if rung not in rungs: rungs.append(rung) if len(rungs) > 1: - raise ValueError, _("Ladder element with id %d is on more than one rung.")%instance["id"] + raise ValueError( + _("Ladder element with id %d is on more than one rung.") + % instance["id"]) + element = self.FindElementById(instance["id"]) element_connectors = element.GetConnectors() self.Rungs[rungs[0]].SelectElement(element) for connector in element_connectors["inputs"]: - for wire, num in connector.GetWires(): + for wire, _num in connector.GetWires(): self.Rungs[rungs[0]].SelectElement(wire) wx.CallAfter(self.RefreshPosition, element) elif instance["type"] in ["contact", "coil"]: @@ -228,11 +236,14 @@ if rung not in rungs: rungs.append(rung) if len(rungs) > 1: - raise ValueError, _("Ladder element with id %d is on more than one rung.")%instance["id"] + raise ValueError( + _("Ladder element with id %d is on more than one rung.") + % instance["id"]) + element = self.FindElementById(instance["id"]) - element_connectors = element.GetConnectors() + element_connectors = element.GetConnectors() self.Rungs[rungs[0]].SelectElement(element) - for wire, num in element_connectors["inputs"][0].GetWires(): + for wire, _num in element_connectors["inputs"][0].GetWires(): self.Rungs[rungs[0]].SelectElement(wire) wx.CallAfter(self.RefreshPosition, element) elif instance["type"] == "comment": @@ -240,7 +251,7 @@ pos = element.GetPosition() i = 0 inserted = False - while i < len(self.RungComments) and not inserted: + while i < len(self.RungComments) and not inserted: ipos = self.RungComments[i].GetPosition() if pos[1] < ipos[1]: self.RungComments.insert(i, element) @@ -248,10 +259,10 @@ i += 1 if not inserted: self.RungComments.append(element) - -#------------------------------------------------------------------------------- -# Search Element functions -#------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- + # Search Element functions + # ------------------------------------------------------------------------------- def FindRung(self, element): for i, rung in enumerate(self.Rungs): @@ -259,10 +270,10 @@ return i return None - def FindElement(self, event, exclude_group = False, connectors = True): + def FindElement(self, event, exclude_group=False, connectors=True): if self.GetDrawingMode() == FREEDRAWING_MODE: return Viewer.FindElement(self, event, exclude_group, connectors) - + dc = self.GetLogicalDC() pos = event.GetLogicalPosition(dc) if self.SelectedElement and not isinstance(self.SelectedElement, (Graphic_Group, Wire)): @@ -286,16 +297,16 @@ def SearchElements(self, bbox): if self.GetDrawingMode() == FREEDRAWING_MODE: return Viewer.SearchElements(self, bbox) - + elements = [] for element in self.Blocks.values() + self.Comments.values(): if element.IsInSelection(bbox): elements.append(element) return elements -#------------------------------------------------------------------------------- -# Mouse event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Mouse event functions + # ------------------------------------------------------------------------------- def OnViewerLeftDown(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -345,7 +356,7 @@ self.SelectedElement.SetElements(elements) self.SelectedElement.SetSelected(True) elif self.Mode == MODE_SELECTION and self.SelectedElement: - dc = self.GetLogicalDC() + dc = self.GetLogicalDC() if not isinstance(self.SelectedElement, Graphic_Group): if self.IsWire(self.SelectedElement): result = self.SelectedElement.TestSegment(event.GetLogicalPosition(dc), True) @@ -383,9 +394,9 @@ wx.CallAfter(self.SetCurrentCursor, 0) event.Skip() -#------------------------------------------------------------------------------- -# Keyboard event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Keyboard event functions + # ------------------------------------------------------------------------------- def OnChar(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -434,9 +445,9 @@ else: event.Skip() -#------------------------------------------------------------------------------- -# Model adding functions from Drop Target -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model adding functions from Drop Target + # ------------------------------------------------------------------------------- def AddVariableBlock(self, x, y, scaling, var_class, var_name, var_type): if var_type == "BOOL": @@ -474,12 +485,12 @@ else: Viewer.AddVariableBlock(self, x, y, scaling, var_class, var_name, var_type) -#------------------------------------------------------------------------------- -# Adding element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Adding element functions + # ------------------------------------------------------------------------------- def AddLadderRung(self): - dialog = LDElementDialog(self.ParentWindow, self.Controler, "coil") + dialog = LDElementDialog(self.ParentWindow, self.Controler, self.TagName, "coil") dialog.SetPreviewFont(self.GetFont()) varlist = [] vars = self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug) @@ -491,7 +502,7 @@ if returntype == "BOOL": varlist.append(self.Controler.GetEditedElementName(self.TagName)) dialog.SetVariables(varlist) - dialog.SetValues({"name":"","type":COIL_NORMAL}) + dialog.SetValues({"name": "", "type": COIL_NORMAL}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() startx, starty = LD_OFFSET[0], 0 @@ -500,7 +511,7 @@ starty = bbox.y + bbox.height starty += LD_OFFSET[1] rung = Graphic_Group(self) - + # Create comment id = self.GetNewId() comment = Comment(self, _("Comment"), id) @@ -511,7 +522,7 @@ self.Controler.AddEditedElementComment(self.TagName, id) self.RefreshCommentModel(comment) starty += LD_COMMENT_DEFAULTSIZE[1] + LD_OFFSET[1] - + # Create LeftPowerRail id = self.GetNewId() leftpowerrail = LD_PowerRail(self, LEFTRAIL, id) @@ -521,7 +532,7 @@ rung.SelectElement(leftpowerrail) self.Controler.AddEditedElementPowerRail(self.TagName, id, LEFTRAIL) self.RefreshPowerRailModel(leftpowerrail) - + # Create Coil id = self.GetNewId() coil = LD_Coil(self, values["type"], values["name"], id) @@ -530,7 +541,7 @@ self.AddBlock(coil) rung.SelectElement(coil) self.Controler.AddEditedElementCoil(self.TagName, id) - + # Create Wire between LeftPowerRail and Coil wire = Wire(self) start_connector = coil_connectors["inputs"][0] @@ -541,7 +552,7 @@ wire.ConnectEndPoint(None, end_connector) self.AddWire(wire) rung.SelectElement(wire) - + # Create RightPowerRail id = self.GetNewId() rightpowerrail = LD_PowerRail(self, RIGHTRAIL, id) @@ -550,7 +561,7 @@ self.AddBlock(rightpowerrail) rung.SelectElement(rightpowerrail) self.Controler.AddEditedElementPowerRail(self.TagName, id, RIGHTRAIL) - + # Create Wire between LeftPowerRail and Coil wire = Wire(self) start_connector = rightpowerrail_connectors["inputs"][0] @@ -574,12 +585,12 @@ left_element = self.SelectedElement.EndConnected if not isinstance(left_element.GetParentBlock(), LD_Coil): wires.append(self.SelectedElement) - elif self.SelectedElement and isinstance(self.SelectedElement,Graphic_Group): + elif self.SelectedElement and isinstance(self.SelectedElement, Graphic_Group): if False not in [self.IsWire(element) for element in self.SelectedElement.GetElements()]: for element in self.SelectedElement.GetElements(): wires.append(element) if len(wires) > 0: - dialog = LDElementDialog(self.ParentWindow, self.Controler, "contact") + dialog = LDElementDialog(self.ParentWindow, self.Controler, self.TagName, "contact") dialog.SetPreviewFont(self.GetFont()) varlist = [] vars = self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug) @@ -588,7 +599,7 @@ if var.Class != "Output" and var.Type == "BOOL": varlist.append(var.Name) dialog.SetVariables(varlist) - dialog.SetValues({"name":"","type":CONTACT_NORMAL}) + dialog.SetValues({"name": "", "type": CONTACT_NORMAL}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() points = wires[0].GetSelectedSegmentPoints() @@ -663,7 +674,7 @@ self.RefreshVisibleElements() self.Refresh(False) else: - message = wx.MessageDialog(self, _("You must select the wire where a contact should be added!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("You must select the wire where a contact should be added!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() @@ -683,10 +694,10 @@ right_index = [] for block in blocks: connectors = block.GetConnectors() - block_infos = {"lefts":[],"rights":[]} + block_infos = {"lefts": [], "rights": []} block_infos.update(connectors) for connector in block_infos["inputs"]: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): found = False for infos in blocks_infos: if wire.EndConnected in infos["outputs"]: @@ -703,7 +714,7 @@ index = left_elements.index(wire.EndConnected) left_index[index] = max(left_index[index], wire.EndConnected.GetWireIndex(wire)) for connector in block_infos["outputs"]: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): found = False for infos in blocks_infos: if wire.StartConnected in infos["inputs"]: @@ -778,7 +789,7 @@ if left_powerrail: powerrail = left_elements[0].GetParentBlock() index = 0 - for left_element in left_elements: + for left_element in left_elements: index = max(index, powerrail.GetConnectorIndex(left_element)) powerrail.InsertConnector(index + 1) powerrail.RefreshModel() @@ -793,7 +804,7 @@ new_wire.ConnectEndPoint(None, connectors["outputs"][index + 1]) right_elements.reverse() elif right_powerrail: - dialog = LDElementDialog(self.ParentWindow, self.Controleur, "coil") + dialog = LDElementDialog(self.ParentWindow, self.Controleur, self.TagName, "coil") dialog.SetPreviewFont(self.GetFont()) varlist = [] vars = self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug) @@ -805,17 +816,17 @@ if returntype == "BOOL": varlist.append(self.Controler.GetEditedElementName(self.TagName)) dialog.SetVariables(varlist) - dialog.SetValues({"name":"","type":COIL_NORMAL}) + dialog.SetValues({"name": "", "type": COIL_NORMAL}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() powerrail = right_elements[0].GetParentBlock() index = 0 - for right_element in right_elements: + for right_element in right_elements: index = max(index, powerrail.GetConnectorIndex(right_element)) powerrail.InsertConnector(index + 1) powerrail.RefreshModel() connectors = powerrail.GetConnectors() - + # Create Coil id = self.GetNewId() coil = LD_Coil(self, values["type"], values["name"], id) @@ -825,7 +836,7 @@ rung.SelectElement(coil) self.Controler.AddEditedElementCoil(self.TagName, id) coil_connectors = coil.GetConnectors() - + # Create Wire between LeftPowerRail and Coil wire = Wire(self) connectors["inputs"][index + 1].Connect((wire, 0), False) @@ -835,7 +846,7 @@ self.AddWire(wire) rung.SelectElement(wire) left_elements.reverse() - + for i, left_element in enumerate(left_elements): # Create Wire between LeftPowerRail and Coil new_wire = Wire(self) @@ -844,7 +855,7 @@ left_element.InsertConnect(left_index[i] + 1, (new_wire, -1), False) new_wire.ConnectStartPoint(None, coil_connectors["inputs"][0]) new_wire.ConnectEndPoint(None, left_element) - + self.RefreshPosition(coil) else: left_elements.reverse() @@ -852,7 +863,7 @@ for i, left_element in enumerate(left_elements): for j, right_element in enumerate(right_elements): exist = False - for wire, handle in right_element.GetWires(): + for wire, _handle in right_element.GetWires(): exist |= wire.EndConnected == left_element if not exist: new_wire = Wire(self) @@ -879,22 +890,22 @@ self.RefreshVisibleElements() self.Refresh(False) else: - message = wx.MessageDialog(self, _("The group of block must be coherent!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("The group of block must be coherent!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() else: - message = wx.MessageDialog(self, _("You must select the block or group of blocks around which a branch should be added!"), _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self, _("You must select the block or group of blocks around which a branch should be added!"), _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() def AddLadderBlock(self): - message = wx.MessageDialog(self, _("This option isn't available yet!"), _("Warning"), wx.OK|wx.ICON_EXCLAMATION) + message = wx.MessageDialog(self, _("This option isn't available yet!"), _("Warning"), wx.OK | wx.ICON_EXCLAMATION) message.ShowModal() message.Destroy() -#------------------------------------------------------------------------------- -# Delete element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Delete element functions + # ------------------------------------------------------------------------------- def DeleteContact(self, contact): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -904,8 +915,8 @@ rung = self.Rungs[rungindex] old_bbox = rung.GetBoundingBox() connectors = contact.GetConnectors() - input_wires = [wire for wire, handle in connectors["inputs"][0].GetWires()] - output_wires = [wire for wire, handle in connectors["outputs"][0].GetWires()] + input_wires = [wire for wire, _handle in connectors["inputs"][0].GetWires()] + output_wires = [wire for wire, _handle in connectors["outputs"][0].GetWires()] left_elements = [(wire.EndConnected, wire.EndConnected.GetWireIndex(wire)) for wire in input_wires] right_elements = [(wire.StartConnected, wire.StartConnected.GetWireIndex(wire)) for wire in output_wires] for wire in input_wires: @@ -924,7 +935,7 @@ for left_element, left_index in left_elements: for right_element, right_index in right_elements: wire_removed = [] - for wire, handle in right_element.GetWires(): + for wire, _handle in right_element.GetWires(): if wire.EndConnected == left_element: wire_removed.append(wire) elif isinstance(wire.EndConnected.GetParentBlock(), LD_PowerRail) and powerrail: @@ -961,7 +972,7 @@ def RecursiveDeletion(self, element, rung): connectors = element.GetConnectors() - input_wires = [wire for wire, handle in connectors["inputs"][0].GetWires()] + input_wires = [wire for wire, _handle in connectors["inputs"][0].GetWires()] left_elements = [wire.EndConnected for wire in input_wires] rung.SelectElement(element) element.Clean() @@ -991,7 +1002,7 @@ nbcoils += 1 if nbcoils > 1: connectors = coil.GetConnectors() - output_wires = [wire for wire, handle in connectors["outputs"][0].GetWires()] + output_wires = [wire for wire, _handle in connectors["outputs"][0].GetWires()] right_elements = [wire.StartConnected for wire in output_wires] for wire in output_wires: wire.Clean() @@ -1006,7 +1017,7 @@ right_block.DeleteConnector(index) powerrail_connectors = right_block.GetConnectors() for connector in powerrail_connectors["inputs"]: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): block = wire.EndConnected.GetParentBlock() endpoint = wire.EndConnected.GetPosition(False) startpoint = connector.GetPosition(False) @@ -1071,7 +1082,7 @@ else: connectors = left_block.GetConnectors() for connector in connectors["outputs"]: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): self.RefreshPosition(wire.StartConnected.GetParentBlock()) for right_element in right_elements: self.RefreshPosition(right_element.GetParentBlock()) @@ -1080,16 +1091,16 @@ self.RefreshRungs(new_bbox.height - old_bbox.height, rungindex + 1) self.SelectedElement = None -#------------------------------------------------------------------------------- -# Refresh element position functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Refresh element position functions + # ------------------------------------------------------------------------------- def RefreshPosition(self, element, recursive=True): # If element is LeftPowerRail, no need to update position if isinstance(element, LD_PowerRail) and element.GetType() == LEFTRAIL: element.RefreshModel() return - + # Extract max position of the elements connected to input connectors = element.GetConnectors() position = element.GetPosition() @@ -1097,13 +1108,13 @@ onlyone = [] for connector in connectors["inputs"]: onlyone.append(len(connector.GetWires()) == 1) - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): onlyone[-1] &= len(wire.EndConnected.GetWires()) == 1 leftblock = wire.EndConnected.GetParentBlock() pos = leftblock.GetPosition() size = leftblock.GetSize() maxx = max(maxx, pos[0] + size[0]) - + # Refresh position of element if isinstance(element, LD_Coil): interval = LD_WIRECOIL_SIZE @@ -1114,13 +1125,13 @@ movex = maxx + interval - position[0] element.Move(movex, 0) position = element.GetPosition() - + # Extract blocks connected to inputs blocks = [] for i, connector in enumerate(connectors["inputs"]): - for j, (wire, handle) in enumerate(connector.GetWires()): + for j, (wire, _handle) in enumerate(connector.GetWires()): blocks.append(wire.EndConnected.GetParentBlock()) - + for i, connector in enumerate(connectors["inputs"]): startpoint = connector.GetPosition(False) previous_blocks = [] @@ -1128,7 +1139,7 @@ start_offset = 0 if not onlyone[i]: middlepoint = maxx + LD_WIRE_SIZE - for j, (wire, handle) in enumerate(connector.GetWires()): + for j, (wire, _handle) in enumerate(connector.GetWires()): block = wire.EndConnected.GetParentBlock() if isinstance(element, LD_PowerRail): pos = block.GetPosition() @@ -1168,13 +1179,13 @@ previous_blocks.append(block) blocks.remove(block) ExtractNextBlocks(block, block_list) - + element.RefreshModel(False) if recursive: for connector in connectors["outputs"]: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): self.RefreshPosition(wire.StartConnected.GetParentBlock()) - + def RefreshRungs(self, movey, fromidx): if movey != 0: for i in xrange(fromidx, len(self.Rungs)): @@ -1185,11 +1196,10 @@ if self.IsBlock(element): self.RefreshPosition(element) -#------------------------------------------------------------------------------- -# Edit element content functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Edit element content functions + # ------------------------------------------------------------------------------- def EditPowerRailContent(self, powerrail): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.EditPowerRailContent(self, powerrail) - diff -r c1298e7ffe3a -r 8391c11477f4 editors/ProjectNodeEditor.py --- a/editors/ProjectNodeEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/ProjectNodeEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,60 +22,62 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from controls import ProjectPropertiesPanel, VariablePanel -from EditorPanel import EditorPanel -from ConfTreeNodeEditor import ConfTreeNodeEditor +from editors.ConfTreeNodeEditor import ConfTreeNodeEditor + class ProjectNodeEditor(ConfTreeNodeEditor): - + SHOW_BASE_PARAMS = False ENABLE_REQUIRED = True CONFNODEEDITOR_TABS = [ (_("Config variables"), "_create_VariablePanel"), (_("Project properties"), "_create_ProjectPropertiesPanel")] - + def _create_VariablePanel(self, prnt): self.VariableEditorPanel = VariablePanel(prnt, self, self.Controler, "config", self.Debug) self.VariableEditorPanel.SetTagName(self.TagName) - + return self.VariableEditorPanel - + def _create_ProjectPropertiesPanel(self, prnt): self.ProjectProperties = ProjectPropertiesPanel(prnt, self.Controler, self.ParentWindow, self.ENABLE_REQUIRED) - + return self.ProjectProperties - + def __init__(self, parent, controler, window): configuration = controler.GetProjectMainConfigurationName() if configuration is not None: tagname = controler.ComputeConfigurationName(configuration) else: tagname = "" - + ConfTreeNodeEditor.__init__(self, parent, controler, window, tagname) - + buttons_sizer = self.GenerateMethodButtonSizer() self.MainSizer.InsertSizer(0, buttons_sizer, 0, border=5, flag=wx.ALL) self.MainSizer.Layout() - + self.VariableEditor = self.VariableEditorPanel def GetTagName(self): return self.Controler.CTNName() - + def SetTagName(self, tagname): self.TagName = tagname if self.VariableEditor is not None: self.VariableEditor.SetTagName(tagname) - + def GetTitle(self): fullname = _(self.Controler.CTNName()) if self.Controler.CTNTestModified(): return "~%s~" % fullname return fullname - + def RefreshView(self, variablepanel=True): ConfTreeNodeEditor.RefreshView(self) self.VariableEditorPanel.RefreshView() @@ -83,12 +85,11 @@ def GetBufferState(self): return self.Controler.GetBufferState() - + def Undo(self): self.Controler.LoadPrevious() self.ParentWindow.CloseTabsWithoutModel() - + def Redo(self): self.Controler.LoadNext() self.ParentWindow.CloseTabsWithoutModel() - diff -r c1298e7ffe3a -r 8391c11477f4 editors/ResourceEditor.py --- a/editors/ResourceEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/ResourceEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx import wx.lib.buttons import wx.grid @@ -29,16 +31,22 @@ from graphics.GraphicCommons import REFRESH_HIGHLIGHT_PERIOD, ERROR_HIGHLIGHT from controls import CustomGrid, CustomTable, DurationCellEditor from dialogs.DurationEditorDialog import IEC_TIME_MODEL -from EditorPanel import EditorPanel +from editors.EditorPanel import EditorPanel from util.BitmapLibrary import GetBitmap - -#------------------------------------------------------------------------------- +from util.TranslationCatalogs import NoTranslate +from plcopen.structures import TestIdentifier, IEC_KEYWORDS + + +# ------------------------------------------------------------------------------- # Configuration Editor class -#------------------------------------------------------------------------------- - -[ID_CONFIGURATIONEDITOR, +# ------------------------------------------------------------------------------- + + +[ + ID_CONFIGURATIONEDITOR, ] = [wx.NewId() for _init_ctrls in range(1)] + class ConfigurationEditor(EditorPanel): ID = ID_CONFIGURATIONEDITOR @@ -59,29 +67,36 @@ return self.Controler.GetEditedElement(self.TagName) is None -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Resource Editor class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- def GetTasksTableColnames(): - _ = lambda x : x + _ = NoTranslate return [_("Name"), _("Triggering"), _("Single"), _("Interval"), _("Priority")] + def GetTaskTriggeringOptions(): - _ = lambda x : x + _ = NoTranslate return [_("Interrupt"), _("Cyclic")] + + TASKTRIGGERINGOPTIONS_DICT = dict([(_(option), option) for option in GetTaskTriggeringOptions()]) -SingleCellEditor = lambda *x : wx.grid.GridCellChoiceEditor() + +def SingleCellEditor(*x): + return wx.grid.GridCellChoiceEditor() + def CheckSingle(single, varlist): return single in varlist def GetInstancesTableColnames(): - _ = lambda x : x + _ = NoTranslate return [_("Name"), _("Type"), _("Task")] + class ResourceTable(CustomTable): """ @@ -108,7 +123,7 @@ def GetValue(self, row, col): if row < self.GetNumberRows(): colname = self.GetColLabelValue(col, False) - value = str(self.data[row].get(colname, "")) + value = self.data[row].get(colname, "") if colname == "Triggering": return _(value) return value @@ -154,12 +169,12 @@ if interval != "" and IEC_TIME_MODEL.match(interval.upper()) is None: error = True elif colname == "Single": - editor = SingleCellEditor(self,colname) + editor = SingleCellEditor(self, colname) editor.SetParameters(self.Parent.VariableList) if self.GetValueByName(row, "Triggering") != "Interrupt": grid.SetReadOnly(row, col, True) single = self.GetValueByName(row, colname) - if single != "" and not CheckSingle(single,self.Parent.VariableList): + if single != "" and not CheckSingle(single, self.Parent.VariableList): error = True elif colname == "Triggering": editor = wx.grid.GridCellChoiceEditor() @@ -185,10 +200,9 @@ grid.SetCellTextColour(row, col, highlight_colours[1]) self.ResizeRow(grid, row) - -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def AddHighlight(self, infos, highlight_type): row_highlights = self.Highlights.setdefault(infos[0], {}) @@ -199,7 +213,7 @@ if highlight_type is None: self.Highlights = {} else: - for row, row_highlights in self.Highlights.iteritems(): + for _row, row_highlights in self.Highlights.iteritems(): row_items = row_highlights.items() for col, col_highlights in row_items: if highlight_type in col_highlights: @@ -208,13 +222,12 @@ row_highlights.pop(col) - class ResourceEditor(EditorPanel): VARIABLE_PANEL_TYPE = "resource" def _init_Editor(self, parent): - self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER|wx.TAB_TRAVERSAL) + self.Editor = wx.Panel(parent, style=wx.SUNKEN_BORDER | wx.TAB_TRAVERSAL) main_sizer = wx.FlexGridSizer(cols=1, hgap=0, rows=2, vgap=5) main_sizer.AddGrowableCol(0) @@ -225,7 +238,7 @@ tasks_sizer.AddGrowableCol(0) tasks_sizer.AddGrowableRow(1) main_sizer.AddSizer(tasks_sizer, border=5, - flag=wx.GROW|wx.TOP|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.TOP | wx.LEFT | wx.RIGHT) tasks_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) tasks_buttons_sizer.AddGrowableCol(0) @@ -241,7 +254,9 @@ ("UpTaskButton", "up", _("Move task up")), ("DownTaskButton", "down", _("Move task down"))]: button = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), + style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) tasks_buttons_sizer.AddWindow(button) @@ -254,7 +269,7 @@ instances_sizer.AddGrowableCol(0) instances_sizer.AddGrowableRow(1) main_sizer.AddSizer(instances_sizer, border=5, - flag=wx.GROW|wx.BOTTOM|wx.LEFT|wx.RIGHT) + flag=wx.GROW | wx.BOTTOM | wx.LEFT | wx.RIGHT) instances_buttons_sizer = wx.FlexGridSizer(cols=5, hgap=5, rows=1, vgap=0) instances_buttons_sizer.AddGrowableCol(0) @@ -269,15 +284,15 @@ ("DeleteInstanceButton", "remove_element", _("Remove instance")), ("UpInstanceButton", "up", _("Move instance up")), ("DownInstanceButton", "down", _("Move instance down"))]: - button = wx.lib.buttons.GenBitmapButton(self.Editor, - bitmap=GetBitmap(bitmap), size=wx.Size(28, 28), style=wx.NO_BORDER) + button = wx.lib.buttons.GenBitmapButton( + self.Editor, bitmap=GetBitmap(bitmap), + size=wx.Size(28, 28), style=wx.NO_BORDER) button.SetToolTipString(help) setattr(self, name, button) instances_buttons_sizer.AddWindow(button) self.InstancesGrid = CustomGrid(self.Editor, style=wx.VSCROLL) - self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, - self.OnInstancesGridCellChange) + self.InstancesGrid.Bind(wx.grid.EVT_GRID_CELL_CHANGE, self.OnInstancesGridCellChange) instances_sizer.AddWindow(self.InstancesGrid, flag=wx.GROW) self.Editor.SetSizer(main_sizer) @@ -288,7 +303,7 @@ self.RefreshHighlightsTimer = wx.Timer(self, -1) self.Bind(wx.EVT_TIMER, self.OnRefreshHighlightsTimer, self.RefreshHighlightsTimer) - self.TasksDefaultValue = {"Name" : "", "Triggering" : "", "Single" : "", "Interval" : "", "Priority" : 0} + self.TasksDefaultValue = {"Name": "", "Triggering": "", "Single": "", "Interval": "", "Priority": 0} self.TasksTable = ResourceTable(self, [], GetTasksTableColnames()) self.TasksTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_RIGHT, wx.ALIGN_RIGHT]) self.TasksTable.SetColSizes([200, 100, 100, 150, 100]) @@ -323,7 +338,7 @@ self.TasksTable.ResetView(self.TasksGrid) self.TasksGrid.RefreshButtons() - self.InstancesDefaultValue = {"Name" : "", "Type" : "", "Task" : ""} + self.InstancesDefaultValue = {"Name": "", "Type": "", "Task": ""} self.InstancesTable = ResourceTable(self, [], GetInstancesTableColnames()) self.InstancesTable.SetColAlignements([wx.ALIGN_LEFT, wx.ALIGN_LEFT, wx.ALIGN_LEFT]) self.InstancesTable.SetColSizes([200, 150, 150]) @@ -362,9 +377,11 @@ rows = self.InstancesTable.GetNumberRows() row = self.InstancesGrid.GetGridCursorRow() self.DeleteInstanceButton.Enable(rows > 0) - self.UpInstanceButton.Enable(row > 0 and + self.UpInstanceButton.Enable( + row > 0 and self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row - 1, "Task")) - self.DownInstanceButton.Enable(0 <= row < rows - 1 and + self.DownInstanceButton.Enable( + 0 <= row < rows - 1 and self.InstancesTable.GetValueByName(row, "Task") == self.InstancesTable.GetValueByName(row + 1, "Task")) setattr(self.InstancesGrid, "RefreshButtons", _RefreshInstanceButtons) @@ -381,17 +398,17 @@ self.TypeList = "" blocktypes = self.Controler.GetBlockResource() for blocktype in blocktypes: - self.TypeList += ",%s"%blocktype + self.TypeList += ",%s" % blocktype def RefreshTaskList(self): self.TaskList = "" for row in xrange(self.TasksTable.GetNumberRows()): - self.TaskList += ",%s"%self.TasksTable.GetValueByName(row, "Name") + self.TaskList += ",%s" % self.TasksTable.GetValueByName(row, "Name") def RefreshVariableList(self): self.VariableList = "" for variable in self.Controler.GetEditedResourceVariables(self.TagName): - self.VariableList += ",%s"%variable + self.VariableList += ",%s" % variable def RefreshModel(self): self.Controler.SetEditedResourceInfos(self.TagName, self.TasksTable.GetData(), self.InstancesTable.GetData()) @@ -432,9 +449,28 @@ self.TasksGrid.RefreshButtons() self.InstancesGrid.RefreshButtons() + def ShowErrorMessage(self, message): + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) + dialog.ShowModal() + dialog.Destroy() + def OnTasksGridCellChange(self, event): row, col = event.GetRow(), event.GetCol() if self.TasksTable.GetColLabelValue(col, False) == "Name": + value = self.TasksTable.GetValue(row, col) + message = None + + if not TestIdentifier(value): + message = _("\"%s\" is not a valid identifier!") % value + elif value.upper() in IEC_KEYWORDS: + message = _("\"%s\" is a keyword. It can't be used!") % value + elif value.upper() in [var["Name"].upper() for i, var in enumerate(self.TasksTable.data) if i != row]: + message = _("A task with the same name already exists!") + if message is not None: + event.Veto() + wx.CallAfter(self.ShowErrorMessage, message) + return + tasklist = [name for name in self.TaskList.split(",") if name != ""] for i in xrange(self.TasksTable.GetNumberRows()): task = self.TasksTable.GetValueByName(i, "Name") @@ -454,14 +490,30 @@ event.Skip() def OnInstancesGridCellChange(self, event): + row, col = event.GetRow(), event.GetCol() + if self.InstancesTable.GetColLabelValue(col, False) == "Name": + value = self.InstancesTable.GetValue(row, col) + message = None + + if not TestIdentifier(value): + message = _("\"%s\" is not a valid identifier!") % value + elif value.upper() in IEC_KEYWORDS: + message = _("\"%s\" is a keyword. It can't be used!") % value + elif value.upper() in [var["Name"].upper() for i, var in enumerate(self.InstancesTable.data) if i != row]: + message = _("An instance with the same name already exists!") + if message is not None: + event.Veto() + wx.CallAfter(self.ShowErrorMessage, message) + return + self.RefreshModel() self.ParentWindow.RefreshPouInstanceVariablesPanel() self.InstancesGrid.RefreshButtons() event.Skip() -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() diff -r c1298e7ffe3a -r 8391c11477f4 editors/SFCViewer.py --- a/editors/SFCViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/SFCViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,11 +22,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from types import * import wx -from Viewer import * +from editors.Viewer import * from graphics.SFC_Objects import * from graphics.GraphicCommons import SELECTION_DIVERGENCE, \ SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE, EAST, NORTH, WEST, SOUTH @@ -35,7 +37,7 @@ class SFC_Viewer(Viewer): - + SFC_StandardRules = { # The key of this dict is a block that user try to connect, # and the value is a list of blocks, that can be connected with the current block @@ -61,7 +63,7 @@ SELECTION_DIVERGENCE: [("SFC_Transition", SOUTH)], SELECTION_CONVERGENCE: [("SFC_Step", SOUTH), - ("SFC_Jump", SOUTH)], + ("SFC_Jump", SOUTH)], SIMULTANEOUS_DIVERGENCE: [("SFC_Step", SOUTH)], @@ -82,10 +84,10 @@ "LD_Coil": [("SFC_Transition", WEST)] } - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): Viewer.__init__(self, parent, tagname, window, controler, debug, instancepath) self.CurrentLanguage = "SFC" - + def ConnectConnectors(self, start, end): startpoint = [start.GetPosition(False), start.GetDirection()] endpoint = [end.GetPosition(False), end.GetDirection()] @@ -96,8 +98,8 @@ wire.ConnectStartPoint(None, start) wire.ConnectEndPoint(None, end) return wire - - def CreateTransition(self, connector, next = None): + + def CreateTransition(self, connector, next=None): previous = connector.GetParentBlock() id = self.GetNewId() transition = SFC_Transition(self, "reference", "", 0, id) @@ -124,7 +126,7 @@ next_block.RefreshPosition() transition.RefreshOutputModel(True) return transition - + def RemoveTransition(self, transition): connectors = transition.GetConnectors() input_wires = connectors["input"].GetWires() @@ -146,8 +148,8 @@ self.Controler.RemoveEditedElementInstance(self.TagName, transition.GetId()) wire = self.ConnectConnectors(next, previous) return wire - - def CreateStep(self, name, connector, next = None): + + def CreateStep(self, name, connector, next=None): previous = connector.GetParentBlock() id = self.GetNewId() step = SFC_Step(self, name, False, id) @@ -213,9 +215,9 @@ else: return None -#------------------------------------------------------------------------------- -# Mouse event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Mouse event functions + # ------------------------------------------------------------------------------- def OnViewerLeftDown(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -282,14 +284,20 @@ self.SelectedElement.OnLeftUp(event, self.GetLogicalDC(), self.Scaling) self.SelectedElement.Refresh() wx.CallAfter(self.SetCurrentCursor, 0) - elif self.Mode == MODE_WIRE and self.SelectedElement: - self.SelectedElement.ResetPoints() - self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling) - self.SelectedElement.GeneratePoints() - self.SelectedElement.RefreshModel() - self.SelectedElement.SetSelected(True) + # + # FIXME: + # This code was forgotten by commit + # 9c74d00ce93e from plcopeneditor_history repository + # 'Last bugs on block and wire moving, resizing with cursor fixed' + # + # elif self.Mode == MODE_WIRE and self.SelectedElement: + # self.SelectedElement.ResetPoints() + # self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling) + # self.SelectedElement.GeneratePoints() + # self.SelectedElement.RefreshModel() + # self.SelectedElement.SetSelected(True) event.Skip() - + def OnViewerRightUp(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.OnViewerRightUp(self, event) @@ -307,7 +315,7 @@ self.SelectedElement.Refresh() wx.CallAfter(self.SetCurrentCursor, 0) event.Skip() - + def OnViewerLeftDClick(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.OnViewerLeftDClick(self, event) @@ -315,7 +323,7 @@ self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling) self.Refresh(False) event.Skip() - + def OnViewerMotion(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.OnViewerMotion(self, event) @@ -326,11 +334,17 @@ if not self.IsWire(self.SelectedElement) and not isinstance(self.SelectedElement, Graphic_Group): self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling) self.SelectedElement.Refresh() - elif self.Mode == MODE_WIRE and self.SelectedElement: - self.SelectedElement.ResetPoints() - self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling) - self.SelectedElement.GeneratePoints() - self.SelectedElement.Refresh() + # + # FIXME: + # This code was forgotten by commit + # 9c74d00ce93e from plcopeneditor_history repository + # 'Last bugs on block and wire moving, resizing with cursor fixed' + # + # elif self.Mode == MODE_WIRE and self.SelectedElement: + # self.SelectedElement.ResetPoints() + # self.SelectedElement.OnMotion(event, self.GetLogicalDC(), self.Scaling) + # self.SelectedElement.GeneratePoints() + # self.SelectedElement.Refresh() self.UpdateScrollPos(event) event.Skip() @@ -341,12 +355,12 @@ return blockName # This method check the IEC 61131-3 compatibility between two SFC blocks - def BlockCompatibility(self,startblock = None, endblock = None, direction = None): - if startblock!= None and endblock != None and (isinstance(startblock,SFC_Objects)\ - or isinstance(endblock,SFC_Objects)): + def BlockCompatibility(self, startblock=None, endblock=None, direction=None): + if startblock is not None and endblock is not None and \ + (isinstance(startblock, SFC_Objects) or isinstance(endblock, SFC_Objects)): # Full "SFC_StandardRules" table would be symmetrical and # to avoid duplicate records and minimize the table only upper part is defined. - if (direction == SOUTH or direction == EAST): + if direction == SOUTH or direction == EAST: startblock, endblock = endblock, startblock start = self.GetBlockName(startblock) end = self.GetBlockName(endblock) @@ -356,9 +370,9 @@ return False return True -#------------------------------------------------------------------------------- -# Keyboard event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Keyboard event functions + # ------------------------------------------------------------------------------- def OnChar(self, event): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -424,13 +438,13 @@ self.RefreshRect(self.GetScrolledRect(self.SelectedElement.GetRedrawRect(0, scaling[1])), False) else: event.Skip() - -#------------------------------------------------------------------------------- -# Adding element functions -#------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- + # Adding element functions + # ------------------------------------------------------------------------------- def AddInitialStep(self, pos): - dialog = SFCStepNameDialog(self.ParentWindow, _("Please enter step name"), _("Add a new initial step"), "", wx.OK|wx.CANCEL) + dialog = SFCStepNameDialog(self.ParentWindow, _("Please enter step name"), _("Add a new initial step"), "", wx.OK | wx.CANCEL) dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug)) dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)) dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)]) @@ -452,7 +466,7 @@ def AddStep(self): if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, SFC_Step): - dialog = SFCStepNameDialog(self.ParentWindow, _("Add a new step"), _("Please enter step name"), "", wx.OK|wx.CANCEL) + dialog = SFCStepNameDialog(self.ParentWindow, _("Add a new step"), _("Please enter step name"), "", wx.OK | wx.CANCEL) dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug)) dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)) dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step)]) @@ -503,7 +517,7 @@ self.RefreshScrollBars() self.Refresh(False) dialog.Destroy() - + def AddStepAction(self): if isinstance(self.SelectedElement, SFC_Step): connectors = self.SelectedElement.GetConnectors() @@ -532,10 +546,10 @@ self.RefreshScrollBars() self.Refresh(False) dialog.Destroy() - + def AddDivergence(self): - if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step): - dialog = SFCDivergenceDialog(self.ParentWindow) + if self.SelectedElement in self.Wires or isinstance(self.SelectedElement, Graphic_Group) or isinstance(self.SelectedElement, SFC_Step): + dialog = SFCDivergenceDialog(self.ParentWindow, self.Controler, self.TagName) dialog.SetPreviewFont(self.GetFont()) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetValues() @@ -579,7 +593,7 @@ self.AddBlock(divergence) self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"]) self.RefreshDivergenceModel(divergence) - for index, connector in enumerate(divergence_connectors["outputs"]): + for _index, connector in enumerate(divergence_connectors["outputs"]): if next: wire = self.ConnectConnectors(next, connector) pos = connector.GetPosition(False) @@ -593,7 +607,7 @@ else: transition = self.CreateTransition(connector) transition_connectors = transition.GetConnectors() - step = self.CreateStep("Step", transition_connectors["output"]) + _step = self.CreateStep("Step", transition_connectors["output"]) elif value["type"] == SIMULTANEOUS_DIVERGENCE: if self.SelectedElement in self.Wires and isinstance(self.SelectedElement.EndConnected.GetParentBlock(), SFC_Transition): self.SelectedElement.SetSelectedSegment(None) @@ -637,7 +651,7 @@ self.AddBlock(divergence) self.Controler.AddEditedElementDivergence(self.TagName, id, value["type"]) self.RefreshDivergenceModel(divergence) - for index, connector in enumerate(divergence_connectors["outputs"]): + for _index, connector in enumerate(divergence_connectors["outputs"]): if next: wire = self.ConnectConnectors(next, connector) pos = connector.GetPosition(False) @@ -649,7 +663,7 @@ next_block.RefreshModel() next = None else: - step = self.CreateStep("Step", connector) + _step = self.CreateStep("Step", connector) elif isinstance(self.SelectedElement, Graphic_Group) and len(self.SelectedElement.GetElements()) > 1: next = None for element in self.SelectedElement.GetElements(): @@ -729,7 +743,7 @@ self.RefreshScrollBars() self.Refresh(False) dialog.Destroy() - + def AddDivergenceBranch(self, divergence): if isinstance(divergence, SFC_Divergence): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -746,11 +760,11 @@ previous = transition_connectors["output"] else: previous = divergence_connectors["outputs"][-1] - step = self.CreateStep("Step", previous) + _step = self.CreateStep("Step", previous) self.RefreshBuffer() self.RefreshScrollBars() self.Refresh(False) - + def RemoveDivergenceBranch(self, divergence): if isinstance(divergence, SFC_Divergence): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -759,16 +773,18 @@ self.RefreshBuffer() self.RefreshScrollBars() self.Refresh(False) - + def AddJump(self): if isinstance(self.SelectedElement, SFC_Step) and not self.SelectedElement.Output: choices = [] for block in self.Blocks: if isinstance(block, SFC_Step): choices.append(block.GetName()) - dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Add a new jump"), _("Please choose a target"), - choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog(self.ParentWindow, + _("Add a new jump"), + _("Please choose a target"), + choices, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetStringSelection() self.SelectedElement.AddOutput() @@ -796,7 +812,7 @@ if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.EditStepContent(self, step) else: - dialog = SFCStepNameDialog(self.ParentWindow, _("Edit step name"), _("Please enter step name"), step.GetName(), wx.OK|wx.CANCEL) + dialog = SFCStepNameDialog(self.ParentWindow, _("Edit step name"), _("Please enter step name"), step.GetName(), wx.OK | wx.CANCEL) dialog.SetPouNames(self.Controler.GetProjectPouNames(self.Debug)) dialog.SetVariables(self.Controler.GetEditedElementInterfaceVars(self.TagName, debug=self.Debug)) dialog.SetStepNames([block.GetName() for block in self.Blocks if isinstance(block, SFC_Step) and block.GetName() != step.GetName()]) @@ -812,9 +828,9 @@ self.Refresh(False) dialog.Destroy() -#------------------------------------------------------------------------------- -# Delete element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Delete element functions + # ------------------------------------------------------------------------------- def DeleteStep(self, step): if self.GetDrawingMode() == FREEDRAWING_MODE: @@ -884,7 +900,7 @@ self.DeleteDivergence(previous_block) else: self.RefreshDivergenceModel(previous_block) - + def DeleteTransition(self, transition): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteTransition(self, transition) @@ -919,7 +935,7 @@ self.DeleteDivergence(next_block) else: self.RefreshDivergenceModel(next_block) - + def DeleteDivergence(self, divergence): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteDivergence(self, divergence) @@ -979,8 +995,8 @@ next_pos = next.GetPosition(False) wire_size = GetWireSize(previous_block) previous_block.RefreshOutputPosition((0, previous_pos.y + wire_size - next_pos.y)) - wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), - wx.Point(previous_pos.x, previous_pos.y)]) + wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), + wx.Point(previous_pos.x, previous_pos.y)]) if isinstance(next_block, SFC_Divergence): next_block.RefreshPosition() previous_block.RefreshOutputModel(True) @@ -1009,12 +1025,12 @@ next_pos = next.GetPosition(False) wire_size = GetWireSize(previous_block) previous_block.RefreshOutputPosition((previous_pos.x - next_pos.x, previous_pos.y + wire_size - next_pos.y)) - wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), - wx.Point(previous_pos.x, previous_pos.y)]) + wire.SetPoints([wx.Point(previous_pos.x, previous_pos.y + wire_size), + wx.Point(previous_pos.x, previous_pos.y)]) if isinstance(next_block, SFC_Divergence): next_block.RefreshPosition() previous_block.RefreshOutputModel(True) - + def DeleteJump(self, jump): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteJump(self, jump) @@ -1049,7 +1065,7 @@ self.DeleteDivergence(previous_block) else: previous_block.RefreshModel() - + def DeleteActionBlock(self, actionblock): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteActionBlock(self, actionblock) @@ -1069,14 +1085,14 @@ self.RefreshStepModel(step) step.RefreshOutputPosition() step.RefreshOutputModel(True) - + def DeleteWire(self, wire): if self.GetDrawingMode() == FREEDRAWING_MODE: Viewer.DeleteWire(self, wire) -#------------------------------------------------------------------------------- -# Model update functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model update functions + # ------------------------------------------------------------------------------- def RefreshBlockModel(self, block): blockid = block.GetId() @@ -1087,4 +1103,3 @@ infos["width"], infos["height"] = block.GetSize() infos["connectors"] = block.GetConnectors() self.Controler.SetEditedElementBlockInfos(self.TagName, blockid, infos) - diff -r c1298e7ffe3a -r 8391c11477f4 editors/TextViewer.py --- a/editors/TextViewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/TextViewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import re from types import * @@ -29,13 +31,13 @@ import wx.stc from graphics.GraphicCommons import ERROR_HIGHLIGHT, SEARCH_RESULT_HIGHLIGHT, REFRESH_HIGHLIGHT_PERIOD -from plcopen.structures import ST_BLOCK_START_KEYWORDS, ST_BLOCK_END_KEYWORDS, IEC_BLOCK_START_KEYWORDS, IEC_BLOCK_END_KEYWORDS, LOCATIONDATATYPES -from EditorPanel import EditorPanel -from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos, NAVIGATION_KEYS - -#------------------------------------------------------------------------------- +from plcopen.structures import ST_BLOCK_START_KEYWORDS, IEC_BLOCK_START_KEYWORDS, LOCATIONDATATYPES +from editors.EditorPanel import EditorPanel +from controls.CustomStyledTextCtrl import CustomStyledTextCtrl, faces, GetCursorPos + +# ------------------------------------------------------------------------------- # Textual programs Viewer class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- NEWLINE = "\n" @@ -51,15 +53,16 @@ STC_PLC_EMPTY] = range(11) [SPACE, WORD, NUMBER, STRING, WSTRING, COMMENT, PRAGMA, DPRAGMA] = range(8) -[ID_TEXTVIEWER, ID_TEXTVIEWERTEXTCTRL, +[ + ID_TEXTVIEWER, ID_TEXTVIEWERTEXTCTRL, ] = [wx.NewId() for _init_ctrls in range(2)] re_texts = {} re_texts["letter"] = "[A-Za-z]" re_texts["digit"] = "[0-9]" -re_texts["identifier"] = "((?:%(letter)s|(?:_(?:%(letter)s|%(digit)s)))(?:_?(?:%(letter)s|%(digit)s))*)"%re_texts +re_texts["identifier"] = "((?:%(letter)s|(?:_(?:%(letter)s|%(digit)s)))(?:_?(?:%(letter)s|%(digit)s))*)" % re_texts IDENTIFIER_MODEL = re.compile(re_texts["identifier"]) -LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]"%re_texts) +LABEL_MODEL = re.compile("[ \t\n]%(identifier)s:[ \t\n]" % re_texts) EXTENSIBLE_PARAMETER = re.compile("IN[1-9][0-9]*$") HIGHLIGHT_TYPES = { @@ -67,15 +70,17 @@ SEARCH_RESULT_HIGHLIGHT: STC_PLC_SEARCH_RESULT, } + def LineStartswith(line, symbols): - return reduce(lambda x, y: x or y, map(lambda x: line.startswith(x), symbols), False) + return reduce(lambda x, y: x or y, map(line.startswith, symbols), False) + class TextViewer(EditorPanel): ID = ID_TEXTVIEWER if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -83,7 +88,7 @@ def _init_Editor(self, prnt): self.Editor = CustomStyledTextCtrl(id=ID_TEXTVIEWERTEXTCTRL, - parent=prnt, name="TextViewer", size=wx.Size(0, 0), style=0) + parent=prnt, name="TextViewer", size=wx.Size(0, 0), style=0) self.Editor.ParentWindow = self self.Editor.CmdKeyAssign(ord('+'), wx.stc.STC_SCMOD_CTRL, wx.stc.STC_CMD_ZOOMIN) @@ -136,8 +141,8 @@ self.Editor.SetTabWidth(2) self.Editor.SetUseTabs(0) - self.Editor.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT| - wx.stc.STC_MOD_BEFOREDELETE| + self.Editor.SetModEventMask(wx.stc.STC_MOD_BEFOREINSERT | + wx.stc.STC_MOD_BEFOREDELETE | wx.stc.STC_PERFORMED_USER) self.Bind(wx.stc.EVT_STC_STYLENEEDED, self.OnStyleNeeded, id=ID_TEXTVIEWERTEXTCTRL) @@ -149,7 +154,7 @@ self.Bind(wx.stc.EVT_STC_DO_DROP, self.OnDoDrop, id=ID_TEXTVIEWERTEXTCTRL) self.Bind(wx.stc.EVT_STC_MODIFIED, self.OnModification, id=ID_TEXTVIEWERTEXTCTRL) - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): if tagname != "" and controler is not None: self.VARIABLE_PANEL_TYPE = controler.GetPouType(tagname.split("::")[1]) @@ -223,16 +228,16 @@ def OnModification(self, event): if not self.DisableEvents: mod_type = event.GetModificationType() - if mod_type&wx.stc.STC_MOD_BEFOREINSERT: - if self.CurrentAction == None: + if mod_type & wx.stc.STC_MOD_BEFOREINSERT: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Add" or self.CurrentAction[1] != event.GetPosition() - 1: self.Controler.EndBuffering() self.StartBuffering() self.CurrentAction = ("Add", event.GetPosition()) wx.CallAfter(self.RefreshModel) - elif mod_type&wx.stc.STC_MOD_BEFOREDELETE: - if self.CurrentAction == None: + elif mod_type & wx.stc.STC_MOD_BEFOREDELETE: + if self.CurrentAction is None: self.StartBuffering() elif self.CurrentAction[0] != "Delete" or self.CurrentAction[1] != event.GetPosition() + 1: self.Controler.EndBuffering() @@ -244,7 +249,7 @@ def OnDoDrop(self, event): try: values = eval(event.GetDragText()) - except: + except Exception: values = event.GetDragText() if isinstance(values, tuple): message = None @@ -259,31 +264,31 @@ blockinputs = None if values[1] != "function": if blockname == "": - dialog = wx.TextEntryDialog(self.ParentWindow, _("Block name"), _("Please enter a block name"), "", wx.OK|wx.CANCEL|wx.CENTRE) + dialog = wx.TextEntryDialog(self.ParentWindow, _("Block name"), _("Please enter a block name"), "", wx.OK | wx.CANCEL | wx.CENTRE) if dialog.ShowModal() == wx.ID_OK: blockname = dialog.GetValue() else: - event.SetDragText("") + event.SetDragText("") return dialog.Destroy() if blockname.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%blockname + message = _("\"%s\" pou already exists!") % blockname elif blockname.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: - message = _("\"%s\" element for this pou already exists!")%blockname + message = _("\"%s\" element for this pou already exists!") % blockname else: self.Controler.AddEditedElementPouVar(self.TagName, values[0], blockname) self.RefreshVariablePanel() self.RefreshVariableTree() blockinfo = self.Controler.GetBlockType(blocktype, blockinputs, self.Debug) hint = ',\n '.join( - [ " " + fctdecl[0]+" := (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["inputs"]] + - [ " " + fctdecl[0]+" => (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["outputs"]]) + [" " + fctdecl[0]+" := (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["inputs"]] + + [" " + fctdecl[0]+" => (*"+fctdecl[1]+"*)" for fctdecl in blockinfo["outputs"]]) if values[1] == "function": event.SetDragText(blocktype+"(\n "+hint+")") else: event.SetDragText(blockname+"(\n "+hint+")") elif values[1] == "location": - pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug) + _pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug) if len(values) > 2 and pou_type == "program": var_name = values[3] dlg = wx.TextEntryDialog( @@ -296,16 +301,18 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name else: location = values[0] if not location.startswith("%"): - dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog( + self.ParentWindow, + _("Select a variable class:"), + _("Variable class"), + [_("Input"), _("Output"), _("Memory")], + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: selected = dialog.GetSelection() else: @@ -324,7 +331,8 @@ var_type = values[2] else: var_type = LOCATIONDATATYPES.get(location[2], ["BOOL"])[0] - self.Controler.AddEditedElementPouVar(self.TagName, + self.Controler.AddEditedElementPouVar( + self.TagName, var_type, var_name, location=location, description=values[4]) self.RefreshVariablePanel() @@ -333,7 +341,7 @@ else: event.SetDragText("") elif values[1] == "NamedConstant": - pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug) + _pou_name, pou_type = self.Controler.GetEditedElementType(self.TagName, self.Debug) if pou_type == "program": initval = values[0] var_name = values[3] @@ -347,7 +355,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name else: var_type = values[2] if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: @@ -370,7 +378,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.Controler.GetProjectPouNames(self.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name else: if not var_name.upper() in [name.upper() for name in self.Controler.GetEditedElementVariables(self.TagName, self.Debug)]: self.Controler.AddEditedElementPouExternalVar(self.TagName, values[2], var_name) @@ -386,7 +394,7 @@ else: message = _("Variable don't belong to this POU!") if message is not None: - dialog = wx.MessageDialog(self, message, _("Error"), wx.OK|wx.ICON_ERROR) + dialog = wx.MessageDialog(self, message, _("Error"), wx.OK | wx.ICON_ERROR) dialog.ShowModal() dialog.Destroy() event.SetDragText("") @@ -416,7 +424,6 @@ def RefreshJumpList(self): if self.TextSyntax == "IL": self.Jumps = [jump.upper() for jump in LABEL_MODEL.findall(self.GetText())] - self.Colourise(0, -1) # Buffer the last model state def RefreshBuffer(self): @@ -434,7 +441,7 @@ self.ParentWindow.RefreshEditMenu() def ResetBuffer(self): - if self.CurrentAction != None: + if self.CurrentAction is not None: self.Controler.EndBuffering() self.CurrentAction = None @@ -473,7 +480,7 @@ self.SetText(new_text) new_cursor_pos = GetCursorPos(old_text, new_text) self.Editor.LineScroll(column, line) - if new_cursor_pos != None: + if new_cursor_pos is not None: self.Editor.GotoPos(new_cursor_pos) else: self.Editor.GotoPos(old_cursor_pos) @@ -491,11 +498,11 @@ for blocktype in category["list"]: blockname = blocktype["name"].upper() if blocktype["type"] == "function" and blockname not in self.Keywords and blockname not in self.Variables.keys(): - interface = dict([(name, {}) for name, type, modifier in blocktype["inputs"] + blocktype["outputs"] if name != '']) + interface = dict([(name, {}) for name, _type, _modifier in blocktype["inputs"] + blocktype["outputs"] if name != '']) for param in ["EN", "ENO"]: - if not interface.has_key(param): + if param not in interface: interface[param] = {} - if self.Functions.has_key(blockname): + if blockname in self.Functions: self.Functions[blockname]["interface"].update(interface) self.Functions[blockname]["extensible"] |= blocktype["extensible"] else: @@ -506,12 +513,14 @@ def RefreshVariableTree(self): words = self.TagName.split("::") - self.Variables = self.GenerateVariableTree( - [(variable.Name, variable.Type, variable.Tree) - for variable in self.Controler.GetEditedElementInterfaceVars( - self.TagName, True, self.Debug)]) + self.Variables = self.GenerateVariableTree([ + (variable.Name, variable.Type, variable.Tree) + for variable in + self.Controler.GetEditedElementInterfaceVars( + self.TagName, True, self.Debug) + ]) if self.Controler.GetEditedElementType(self.TagName, self.Debug)[1] == "function" or words[0] == "T" and self.TextSyntax == "IL": - return_type, (var_tree, var_dimension) = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, True, self.Debug) + return_type, (var_tree, _var_dimension) = self.Controler.GetEditedElementInterfaceReturnType(self.TagName, True, self.Debug) if return_type is not None: self.Variables[words[-1].upper()] = self.GenerateVariableTree(var_tree) else: @@ -519,7 +528,7 @@ def GenerateVariableTree(self, list): tree = {} - for var_name, var_type, (var_tree, var_dimension) in list: + for var_name, _var_type, (var_tree, _var_dimension) in list: tree[var_name.upper()] = self.GenerateVariableTree(var_tree) return tree @@ -543,7 +552,7 @@ else: level = self.Editor.GetFoldLevel(line_number - 1) & wx.stc.STC_FOLDLEVELNUMBERMASK if level != wx.stc.STC_FOLDLEVELBASE: - level |= wx.stc.STC_FOLDLEVELWHITEFLAG + level |= wx.stc.STC_FOLDLEVELWHITEFLAG elif LineStartswith(line, self.BlockStartKeywords): level |= wx.stc.STC_FOLDLEVELHEADERFLAG elif LineStartswith(line, self.BlockEndKeywords): @@ -632,7 +641,7 @@ if len(self.CallStack) > 0: current_call = self.CallStack.pop() else: - current_call = None + current_call = None elif state == PRAGMA: if line.endswith("}"): self.SetStyling(current_pos - last_styled_pos, STC_PLC_EMPTY) @@ -812,7 +821,7 @@ self.SearchParams = search_params self.SearchResults = [ (infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT) - for infos, start, end, text in + for infos, start, end, _text in self.Search(search_params)] self.CurrentFindHighlight = None @@ -838,6 +847,7 @@ def RefreshModel(self): self.RefreshJumpList() + self.Colourise(0, -1) self.Controler.SetEditedElementText(self.TagName, self.GetText()) self.ResetSearchResults() @@ -909,9 +919,9 @@ self.Editor.AutoCompCancel() event.Skip() -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() @@ -941,8 +951,8 @@ EditorPanel.RemoveHighlight(self, infos, start, end, highlight_type) highlight_type = HIGHLIGHT_TYPES.get(highlight_type, None) - if (infos[0] == "body" and highlight_type is not None and - (infos[1], start, end, highlight_type) in self.Highlights): + if infos[0] == "body" and highlight_type is not None and \ + (infos[1], start, end, highlight_type) in self.Highlights: self.Highlights.remove((infos[1], start, end, highlight_type)) self.RefreshHighlightsTimer.Start(int(REFRESH_HIGHLIGHT_PERIOD * 1000), oneShot=True) @@ -961,4 +971,3 @@ self.SetStyling(highlight_end_pos - highlight_start_pos, highlight_type) self.StartStyling(highlight_start_pos, 0x00) self.SetStyling(len(self.Editor.GetText()) - highlight_end_pos, wx.stc.STC_STYLE_DEFAULT) - diff -r c1298e7ffe3a -r 8391c11477f4 editors/Viewer.py --- a/editors/Viewer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/editors/Viewer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,7 +22,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import re + +from __future__ import absolute_import import math from time import time as gettime from types import TupleType @@ -32,11 +33,11 @@ from plcopen.structures import * from PLCControler import ITEM_VAR_LOCAL, ITEM_POU, ITEM_PROGRAM, ITEM_FUNCTIONBLOCK - +from graphics.DebugDataConsumer import DebugDataConsumer +from graphics import * from dialogs import * -from graphics import * from editors.DebugViewer import DebugViewer, REFRESH_PERIOD -from EditorPanel import EditorPanel +from editors.EditorPanel import EditorPanel SCROLLBAR_UNIT = 10 WINDOW_BORDER = 10 @@ -45,9 +46,10 @@ CURSORS = None SFC_Objects = (SFC_Step, SFC_ActionBlock, SFC_Transition, SFC_Divergence, SFC_Jump) + def ResetCursors(): global CURSORS - if CURSORS == None: + if CURSORS is None: CURSORS = [wx.NullCursor, wx.StockCursor(wx.CURSOR_HAND), wx.StockCursor(wx.CURSOR_SIZENWSE), @@ -55,26 +57,30 @@ wx.StockCursor(wx.CURSOR_SIZEWE), wx.StockCursor(wx.CURSOR_SIZENS)] + def AppendMenu(parent, help, id, kind, text): if wx.VERSION >= (2, 6, 0): parent.Append(help=help, id=id, kind=kind, text=text) else: parent.Append(helpString=help, id=id, kind=kind, item=text) + if wx.Platform == '__WXMSW__': - faces = { 'times': 'Times New Roman', - 'mono' : 'Courier New', - 'helv' : 'Arial', - 'other': 'Comic Sans MS', - 'size' : 10, - } + faces = { + 'times': 'Times New Roman', + 'mono': 'Courier New', + 'helv': 'Arial', + 'other': 'Comic Sans MS', + 'size': 10, + } else: - faces = { 'times': 'Times', - 'mono' : 'Courier', - 'helv' : 'Helvetica', - 'other': 'new century schoolbook', - 'size' : 12, - } + faces = { + 'times': 'Times', + 'mono': 'Courier', + 'helv': 'Helvetica', + 'other': 'new century schoolbook', + 'size': 12, + } if wx.Platform == '__WXMSW__': MAX_ZOOMIN = 4 @@ -82,49 +88,67 @@ MAX_ZOOMIN = 7 ZOOM_FACTORS = [math.sqrt(2) ** x for x in xrange(-6, MAX_ZOOMIN)] + def GetVariableCreationFunction(variable_type): def variableCreationFunction(viewer, id, specific_values): - return FBD_Variable(viewer, variable_type, - specific_values.name, - specific_values.value_type, - id, - specific_values.execution_order) + return FBD_Variable(viewer, + variable_type, + specific_values.name, + specific_values.value_type, + id, + specific_values.execution_order) return variableCreationFunction + def GetConnectorCreationFunction(connector_type): def connectorCreationFunction(viewer, id, specific_values): - return FBD_Connector(viewer, connector_type, - specific_values.name, id) + return FBD_Connector(viewer, + connector_type, + specific_values.name, + id) return connectorCreationFunction + def commentCreationFunction(viewer, id, specific_values): return Comment(viewer, specific_values.content, id) + def GetPowerRailCreationFunction(powerrail_type): def powerRailCreationFunction(viewer, id, specific_values): - return LD_PowerRail(viewer, powerrail_type, id, - specific_values.connectors) + return LD_PowerRail(viewer, + powerrail_type, + id, + specific_values.connectors) return powerRailCreationFunction -NEGATED_VALUE = lambda x: x if x is not None else False -MODIFIER_VALUE = lambda x: x if x is not None else 'none' + +def NEGATED_VALUE(x): + return x if x is not None else False + + +def MODIFIER_VALUE(x): + return x if x is not None else 'none' + CONTACT_TYPES = {(True, "none"): CONTACT_REVERSE, (False, "rising"): CONTACT_RISING, (False, "falling"): CONTACT_FALLING} + def contactCreationFunction(viewer, id, specific_values): contact_type = CONTACT_TYPES.get((NEGATED_VALUE(specific_values.negated), MODIFIER_VALUE(specific_values.edge)), CONTACT_NORMAL) return LD_Contact(viewer, contact_type, specific_values.name, id) + COIL_TYPES = {(True, "none", "none"): COIL_REVERSE, (False, "none", "set"): COIL_SET, (False, "none", "reset"): COIL_RESET, (False, "rising", "none"): COIL_RISING, (False, "falling", "none"): COIL_FALLING} + def coilCreationFunction(viewer, id, specific_values): coil_type = COIL_TYPES.get((NEGATED_VALUE(specific_values.negated), MODIFIER_VALUE(specific_values.edge), @@ -132,36 +156,47 @@ COIL_NORMAL) return LD_Coil(viewer, coil_type, specific_values.name, id) + def stepCreationFunction(viewer, id, specific_values): - step = SFC_Step(viewer, specific_values.name, - specific_values.initial, id) + step = SFC_Step(viewer, + specific_values.name, + specific_values.initial, + id) if specific_values.action is not None: step.AddAction() connector = step.GetActionConnector() connector.SetPosition(wx.Point(*specific_values.action.position)) return step + def transitionCreationFunction(viewer, id, specific_values): - transition = SFC_Transition(viewer, specific_values.condition_type, - specific_values.condition, - specific_values.priority, id) + transition = SFC_Transition(viewer, + specific_values.condition_type, + specific_values.condition, + specific_values.priority, + id) return transition + divergence_types = [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE, SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE] + def GetDivergenceCreationFunction(divergence_type): def divergenceCreationFunction(viewer, id, specific_values): return SFC_Divergence(viewer, divergence_type, - specific_values.connectors, id) + specific_values.connectors, id) return divergenceCreationFunction + def jumpCreationFunction(viewer, id, specific_values): return SFC_Jump(viewer, specific_values.target, id) + def actionBlockCreationFunction(viewer, id, specific_values): return SFC_ActionBlock(viewer, specific_values.actions, id) + ElementCreationFunctions = { "input": GetVariableCreationFunction(INPUT), "output": GetVariableCreationFunction(OUTPUT), @@ -183,6 +218,7 @@ "actionBlock": actionBlockCreationFunction, } + def sort_blocks(block_infos1, block_infos2): x1, y1 = block_infos1[0].GetPosition() x2, y2 = block_infos2[0].GetPosition() @@ -191,24 +227,27 @@ else: return cmp(y1, y2) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Graphic elements Viewer base class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + # ID Constants for alignment menu items -[ID_VIEWERALIGNMENTMENUITEMS0, ID_VIEWERALIGNMENTMENUITEMS1, - ID_VIEWERALIGNMENTMENUITEMS2, ID_VIEWERALIGNMENTMENUITEMS4, - ID_VIEWERALIGNMENTMENUITEMS5, ID_VIEWERALIGNMENTMENUITEMS6, +[ + ID_VIEWERALIGNMENTMENUITEMS0, ID_VIEWERALIGNMENTMENUITEMS1, + ID_VIEWERALIGNMENTMENUITEMS2, ID_VIEWERALIGNMENTMENUITEMS4, + ID_VIEWERALIGNMENTMENUITEMS5, ID_VIEWERALIGNMENTMENUITEMS6, ] = [wx.NewId() for _init_coll_AlignmentMenu_Items in range(6)] # ID Constants for contextual menu items -[ID_VIEWERCONTEXTUALMENUITEMS0, ID_VIEWERCONTEXTUALMENUITEMS1, - ID_VIEWERCONTEXTUALMENUITEMS2, ID_VIEWERCONTEXTUALMENUITEMS3, - ID_VIEWERCONTEXTUALMENUITEMS5, ID_VIEWERCONTEXTUALMENUITEMS6, - ID_VIEWERCONTEXTUALMENUITEMS8, ID_VIEWERCONTEXTUALMENUITEMS9, - ID_VIEWERCONTEXTUALMENUITEMS11, ID_VIEWERCONTEXTUALMENUITEMS12, - ID_VIEWERCONTEXTUALMENUITEMS14, ID_VIEWERCONTEXTUALMENUITEMS16, - ID_VIEWERCONTEXTUALMENUITEMS17, +[ + ID_VIEWERCONTEXTUALMENUITEMS0, ID_VIEWERCONTEXTUALMENUITEMS1, + ID_VIEWERCONTEXTUALMENUITEMS2, ID_VIEWERCONTEXTUALMENUITEMS3, + ID_VIEWERCONTEXTUALMENUITEMS5, ID_VIEWERCONTEXTUALMENUITEMS6, + ID_VIEWERCONTEXTUALMENUITEMS8, ID_VIEWERCONTEXTUALMENUITEMS9, + ID_VIEWERCONTEXTUALMENUITEMS11, ID_VIEWERCONTEXTUALMENUITEMS12, + ID_VIEWERCONTEXTUALMENUITEMS14, ID_VIEWERCONTEXTUALMENUITEMS16, + ID_VIEWERCONTEXTUALMENUITEMS17, ] = [wx.NewId() for _init_coll_ContextualMenu_Items in range(13)] @@ -229,11 +268,11 @@ message = None try: values = eval(data) - except: - message = _("Invalid value \"%s\" for viewer block")%data + except Exception: + message = _("Invalid value \"%s\" for viewer block") % data values = None if not isinstance(values, TupleType): - message = _("Invalid value \"%s\" for viewer block")%data + message = _("Invalid value \"%s\" for viewer block") % data values = None if values is not None: if values[1] == "debug": @@ -241,13 +280,12 @@ elif values[1] == "program": message = _("Programs can't be used by other POUs!") elif values[1] in ["function", "functionBlock"]: - words = tagname.split("::") if pou_name == values[0]: - message = _("\"%s\" can't use itself!")%pou_name + message = _("\"%s\" can't use itself!") % pou_name elif pou_type == "function" and values[1] != "function": message = _("Function Blocks can't be used in Functions!") elif self.ParentWindow.Controler.PouIsUsedBy(pou_name, values[0], self.ParentWindow.Debug): - message = _("\"{a1}\" is already used by \"{a2}\"!").format(a1 = pou_name, a2 = values[0]) + message = _("\"{a1}\" is already used by \"{a2}\"!").format(a1=pou_name, a2=values[0]) else: blockname = values[2] if len(values) > 3: @@ -257,12 +295,12 @@ if values[1] != "function" and blockname == "": blockname = self.ParentWindow.GenerateNewName(blocktype=values[0]) if blockname.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%blockname + message = _("\"%s\" pou already exists!") % blockname elif blockname.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: - message = _("\"%s\" element for this pou already exists!")%blockname + message = _("\"%s\" element for this pou already exists!") % blockname else: id = self.ParentWindow.GetNewId() - block = FBD_Block(self.ParentWindow, values[0], blockname, id, inputs = blockinputs) + block = FBD_Block(self.ParentWindow, values[0], blockname, id, inputs=blockinputs) width, height = block.GetMinSize() if scaling is not None: x = round(float(x) / float(scaling[0])) * scaling[0] @@ -284,10 +322,12 @@ if pou_type == "program": location = values[0] if not location.startswith("%"): - dialog = wx.SingleChoiceDialog(self.ParentWindow.ParentWindow, - _("Select a variable class:"), _("Variable class"), - [_("Input"), _("Output"), _("Memory")], - wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + dialog = wx.SingleChoiceDialog( + self.ParentWindow.ParentWindow, + _("Select a variable class:"), + _("Variable class"), + [_("Input"), _("Output"), _("Memory")], + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: selected = dialog.GetSelection() else: @@ -312,7 +352,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: if location[1] == "Q": var_class = OUTPUT @@ -327,7 +367,7 @@ self.ParentWindow.ParentWindow.RefreshPouInstanceVariablesPanel() self.ParentWindow.AddVariableBlock(x, y, scaling, var_class, var_name, var_type) else: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name elif values[1] == "NamedConstant": if pou_type == "program": initval = values[0] @@ -342,7 +382,7 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: var_class = INPUT var_type = values[2] @@ -351,7 +391,7 @@ self.ParentWindow.ParentWindow.RefreshPouInstanceVariablesPanel() self.ParentWindow.AddVariableBlock(x, y, scaling, var_class, var_name, var_type) else: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name elif values[1] == "Global": var_name = values[0] dlg = wx.TextEntryDialog( @@ -364,14 +404,14 @@ if var_name is None: return elif var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetProjectPouNames(self.ParentWindow.Debug)]: - message = _("\"%s\" pou already exists!")%var_name + message = _("\"%s\" pou already exists!") % var_name elif not var_name.upper() in [name.upper() for name in self.ParentWindow.Controler.GetEditedElementVariables(tagname, self.ParentWindow.Debug)]: self.ParentWindow.Controler.AddEditedElementPouExternalVar(tagname, values[2], var_name) self.ParentWindow.RefreshVariablePanel() self.ParentWindow.ParentWindow.RefreshPouInstanceVariablesPanel() self.ParentWindow.AddVariableBlock(x, y, scaling, INPUT, var_name, values[2]) else: - message = _("\"%s\" element for this pou already exists!")%var_name + message = _("\"%s\" element for this pou already exists!") % var_name elif values[1] == "Constant": self.ParentWindow.AddVariableBlock(x, y, scaling, INPUT, values[0], None) elif values[3] == tagname: @@ -420,19 +460,76 @@ return AddVariableFunction def ShowMessage(self, message): - message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self.ParentWindow, message, _("Error"), wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() -""" -Class that implements a Viewer based on a wx.ScrolledWindow for drawing and -manipulating graphic elements -""" + +class DebugInstanceName(DebugDataConsumer): + VALUE_TRANSLATION = {True: _("Active"), False: _("Inactive")} + + def __init__(self, parent): + DebugDataConsumer.__init__(self) + self.Parent = parent + self.ActionLastState = None + self.ActionState = None + self.x_offset = 2 + self.y_offset = 2 + + def SetValue(self, value): + self.ActionState = value + if self.ActionState != self.ActionLastState: + self.ActionLastState = self.ActionState + wx.CallAfter(self.Parent.ElementNeedRefresh, self) + + def GetInstanceName(self): + return _("Debug: %s") % self.Parent.InstancePath + + def GetRedrawRect(self): + x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset) + dc = self.Parent.GetLogicalDC() + ipw, _iph = dc.GetTextExtent(self.GetInstanceName()) + vw, vh = 0, 0 + for value in self.VALUE_TRANSLATION.itervalues(): + w, h = dc.GetTextExtent(" (%s)" % value) + vw = max(vw, w) + vh = max(vh, h) + return wx.Rect(ipw + x, y, vw, vh) + + def Draw(self, dc): + scalex, scaley = dc.GetUserScale() + dc.SetUserScale(1, 1) + x, y = self.Parent.CalcUnscrolledPosition(self.x_offset, self.y_offset) + + text = self.GetInstanceName() + if self.ActionState is not None: + text += " (" + + dc.DrawText(text, x, y) + tw, _th = dc.GetTextExtent(text) + if self.ActionState is not None: + + text = self.VALUE_TRANSLATION[self.ActionState] + if self.ActionState: + dc.SetTextForeground(wx.GREEN) + dc.DrawText(text, x + tw, y) + if self.ActionState: + dc.SetTextForeground(wx.BLACK) + tw = tw + dc.GetTextExtent(text)[0] + + text = ")" + dc.DrawText(text, x + tw, y) + dc.SetUserScale(scalex, scaley) + class Viewer(EditorPanel, DebugViewer): + """ + Class that implements a Viewer based on a wx.ScrolledWindow for drawing and + manipulating graphic elements + """ if wx.VERSION < (2, 6, 0): - def Bind(self, event, function, id = None): + def Bind(self, event, function, id=None): if id is not None: event(self, id, function) else: @@ -452,7 +549,7 @@ # Add Block Pin Menu items to the given menu def AddBlockPinMenuItems(self, menu, connector): [ID_NO_MODIFIER, ID_NEGATED, ID_RISING_EDGE, - ID_FALLING_EDGE] = [wx.NewId() for i in xrange(4)] + ID_FALLING_EDGE] = [wx.NewId() for dummy in xrange(4)] # Create menu items self.AddMenuItems(menu, [ @@ -476,9 +573,10 @@ # Add Alignment Menu items to the given menu def AddAlignmentMenuItems(self, menu): - [ID_ALIGN_LEFT, ID_ALIGN_CENTER, ID_ALIGN_RIGHT, - ID_ALIGN_TOP, ID_ALIGN_MIDDLE, ID_ALIGN_BOTTOM, - ] = [wx.NewId() for i in xrange(6)] + [ + ID_ALIGN_LEFT, ID_ALIGN_CENTER, ID_ALIGN_RIGHT, + ID_ALIGN_TOP, ID_ALIGN_MIDDLE, ID_ALIGN_BOTTOM, + ] = [wx.NewId() for dummy in xrange(6)] # Create menu items self.AddMenuItems(menu, [ @@ -492,8 +590,9 @@ # Add Wire Menu items to the given menu def AddWireMenuItems(self, menu, delete=False, replace=False): - [ID_ADD_SEGMENT, ID_DELETE_SEGMENT, ID_REPLACE_WIRE, - ] = [wx.NewId() for i in xrange(3)] + [ + ID_ADD_SEGMENT, ID_DELETE_SEGMENT, ID_REPLACE_WIRE, + ] = [wx.NewId() for dummy in xrange(3)] # Create menu items self.AddMenuItems(menu, [ @@ -506,7 +605,7 @@ # Add Divergence Menu items to the given menu def AddDivergenceMenuItems(self, menu, delete=False): - [ID_ADD_BRANCH, ID_DELETE_BRANCH] = [wx.NewId() for i in xrange(2)] + [ID_ADD_BRANCH, ID_DELETE_BRANCH] = [wx.NewId() for dummy in xrange(2)] # Create menu items self.AddMenuItems(menu, [ @@ -518,7 +617,7 @@ # Add Add Menu items to the given menu def AddAddMenuItems(self, menu): [ID_ADD_BLOCK, ID_ADD_VARIABLE, ID_ADD_CONNECTION, - ID_ADD_COMMENT] = [wx.NewId() for i in xrange(4)] + ID_ADD_COMMENT] = [wx.NewId() for dummy in xrange(4)] # Create menu items self.AddMenuItems(menu, [ @@ -528,8 +627,9 @@ None]) if self.CurrentLanguage != "FBD": - [ID_ADD_POWER_RAIL, ID_ADD_CONTACT, ID_ADD_COIL, - ] = [wx.NewId() for i in xrange(3)] + [ + ID_ADD_POWER_RAIL, ID_ADD_CONTACT, ID_ADD_COIL, + ] = [wx.NewId() for dummy in xrange(3)] # Create menu items self.AddMenuItems(menu, [ @@ -538,14 +638,15 @@ if self.CurrentLanguage != "SFC": self.AddMenuItems(menu, [ - (ID_ADD_COIL, wx.ITEM_NORMAL, _(u'Coil'), '', self.GetAddMenuCallBack(self.AddNewCoil))]) + (ID_ADD_COIL, wx.ITEM_NORMAL, _(u'Coil'), '', self.GetAddMenuCallBack(self.AddNewCoil))]) menu.AppendSeparator() if self.CurrentLanguage == "SFC": - [ID_ADD_INITIAL_STEP, ID_ADD_STEP, ID_ADD_TRANSITION, - ID_ADD_ACTION_BLOCK, ID_ADD_DIVERGENCE, ID_ADD_JUMP, - ] = [wx.NewId() for i in xrange(6)] + [ + ID_ADD_INITIAL_STEP, ID_ADD_STEP, ID_ADD_TRANSITION, + ID_ADD_ACTION_BLOCK, ID_ADD_DIVERGENCE, ID_ADD_JUMP, + ] = [wx.NewId() for dummy in xrange(6)] # Create menu items self.AddMenuItems(menu, [ @@ -558,23 +659,23 @@ None]) self.AddMenuItems(menu, [ - (ID_ADD_COMMENT, wx.ITEM_NORMAL, _(u'Comment'), '', self.GetAddMenuCallBack(self.AddNewComment))]) + (ID_ADD_COMMENT, wx.ITEM_NORMAL, _(u'Comment'), '', self.GetAddMenuCallBack(self.AddNewComment))]) # Add Default Menu items to the given menu def AddDefaultMenuItems(self, menu, edit=False, block=False): if block: - [ID_EDIT_BLOCK, ID_DELETE, ID_ADJUST_BLOCK_SIZE] = [wx.NewId() for i in xrange(3)] + [ID_EDIT_BLOCK, ID_DELETE, ID_ADJUST_BLOCK_SIZE] = [wx.NewId() for dummy in xrange(3)] # Create menu items self.AddMenuItems(menu, [ - (ID_EDIT_BLOCK, wx.ITEM_NORMAL, _(u'Edit Block'), '', self.OnEditBlockMenu), - (ID_ADJUST_BLOCK_SIZE, wx.ITEM_NORMAL, _(u'Adjust Block Size'), '', self.OnAdjustBlockSizeMenu), - (ID_DELETE, wx.ITEM_NORMAL, _(u'Delete'), '', self.OnDeleteMenu)]) + (ID_EDIT_BLOCK, wx.ITEM_NORMAL, _(u'Edit Block'), '', self.OnEditBlockMenu), + (ID_ADJUST_BLOCK_SIZE, wx.ITEM_NORMAL, _(u'Adjust Block Size'), '', self.OnAdjustBlockSizeMenu), + (ID_DELETE, wx.ITEM_NORMAL, _(u'Delete'), '', self.OnDeleteMenu)]) menu.Enable(ID_EDIT_BLOCK, edit) else: - [ID_CLEAR_EXEC_ORDER, ID_RESET_EXEC_ORDER] = [wx.NewId() for i in xrange(2)] + [ID_CLEAR_EXEC_ORDER, ID_RESET_EXEC_ORDER] = [wx.NewId() for dummy in xrange(2)] # Create menu items if self.CurrentLanguage == 'FBD': @@ -589,13 +690,13 @@ menu.AppendSeparator() - [ID_CUT, ID_COPY, ID_PASTE] = [wx.NewId() for i in xrange(3)] + [ID_CUT, ID_COPY, ID_PASTE] = [wx.NewId() for dummy in xrange(3)] # Create menu items self.AddMenuItems(menu, [ - (ID_CUT, wx.ITEM_NORMAL, _(u'Cut'), '', self.GetClipboardCallBack(self.Cut)), - (ID_COPY, wx.ITEM_NORMAL, _(u'Copy'), '', self.GetClipboardCallBack(self.Copy)), - (ID_PASTE, wx.ITEM_NORMAL, _(u'Paste'), '', self.GetAddMenuCallBack(self.Paste))]) + (ID_CUT, wx.ITEM_NORMAL, _(u'Cut'), '', self.GetClipboardCallBack(self.Cut)), + (ID_COPY, wx.ITEM_NORMAL, _(u'Copy'), '', self.GetClipboardCallBack(self.Copy)), + (ID_PASTE, wx.ITEM_NORMAL, _(u'Paste'), '', self.GetAddMenuCallBack(self.Paste))]) menu.Enable(ID_CUT, block) menu.Enable(ID_COPY, block) @@ -603,12 +704,12 @@ def _init_Editor(self, prnt): self.Editor = wx.ScrolledWindow(prnt, name="Viewer", - pos=wx.Point(0, 0), size=wx.Size(0, 0), - style=wx.HSCROLL | wx.VSCROLL) + pos=wx.Point(0, 0), size=wx.Size(0, 0), + style=wx.HSCROLL | wx.VSCROLL) self.Editor.ParentWindow = self # Create a new Viewer - def __init__(self, parent, tagname, window, controler, debug = False, instancepath = ""): + def __init__(self, parent, tagname, window, controler, debug=False, instancepath=""): self.VARIABLE_PANEL_TYPE = controler.GetPouType(tagname.split("::")[1]) EditorPanel.__init__(self, parent, tagname, window, controler, debug) @@ -616,7 +717,7 @@ # Adding a rubberband to Viewer self.rubberBand = RubberBand(viewer=self) - self.Editor.SetBackgroundColour(wx.Colour(255,255,255)) + self.Editor.SetBackgroundColour(wx.Colour(255, 255, 255)) self.Editor.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.ResetView() self.LastClientSize = None @@ -635,6 +736,7 @@ self.InstancePath = instancepath self.StartMousePos = None self.StartScreenPos = None + self.InstanceName = DebugInstanceName(self) # Prevent search for highlighted element to be called too often self.LastHighlightCheckTime = gettime() @@ -662,17 +764,17 @@ self.ElementRefreshList_lock = Lock() dc = wx.ClientDC(self.Editor) - font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["mono"]) + font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["mono"]) dc.SetFont(font) - width, height = dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + width, _height = dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZ") while width > 260: faces["size"] -= 1 - font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["mono"]) + font = wx.Font(faces["size"], wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["mono"]) dc.SetFont(font) - width, height = dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZ") + width, _height = dc.GetTextExtent("ABCDEFGHIJKLMNOPQRSTUVWXYZ") self.SetFont(font) self.MiniTextDC = wx.MemoryDC() - self.MiniTextDC.SetFont(wx.Font(faces["size"] * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName = faces["helv"])) + self.MiniTextDC.SetFont(wx.Font(faces["size"] * 0.75, wx.SWISS, wx.NORMAL, wx.NORMAL, faceName=faces["helv"])) self.CurrentScale = None self.SetScale(ZOOM_FACTORS.index(1.0), False) @@ -709,14 +811,13 @@ def SetCurrentCursor(self, cursor): if self.Mode != MODE_MOTION: - global CURSORS if self.CurrentCursor != cursor: self.CurrentCursor = cursor self.Editor.SetCursor(CURSORS[cursor]) def GetScrolledRect(self, rect): rect.x, rect.y = self.Editor.CalcScrolledPosition(int(rect.x * self.ViewScale[0]), - int(rect.y * self.ViewScale[1])) + int(rect.y * self.ViewScale[1])) rect.width = int(rect.width * self.ViewScale[0]) + 2 rect.height = int(rect.height * self.ViewScale[1]) + 2 return rect @@ -839,9 +940,9 @@ def GetMiniFont(self): return self.MiniTextDC.GetFont() -#------------------------------------------------------------------------------- -# Element management functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Element management functions + # ------------------------------------------------------------------------------- def AddBlock(self, block): self.Blocks[block.GetId()] = block @@ -895,7 +996,7 @@ block.GetName() == name: blocks.append(block) return blocks - + def GetConnectorByName(self, name): for block in self.Blocks.itervalues(): if isinstance(block, FBD_Connector) and\ @@ -904,7 +1005,7 @@ return block return None - def RefreshVisibleElements(self, xp = None, yp = None): + def RefreshVisibleElements(self, xp=None, yp=None): x, y = self.Editor.CalcUnscrolledPosition(0, 0) if xp is not None: x = xp * self.Editor.GetScrollPixelsPerUnit()[0] @@ -931,14 +1032,14 @@ blockname = block.GetName() connectorname = element.GetName() if blockname != "": - iec_path = "%s.%s.%s"%(instance_path, blockname, connectorname) + iec_path = "%s.%s.%s" % (instance_path, blockname, connectorname) else: if connectorname == "": - iec_path = "%s.%s%d"%(instance_path, block.GetType(), block.GetId()) + iec_path = "%s.%s%d" % (instance_path, block.GetType(), block.GetId()) else: - iec_path = "%s.%s%d_%s"%(instance_path, block.GetType(), block.GetId(), connectorname) + iec_path = "%s.%s%d_%s" % (instance_path, block.GetType(), block.GetId(), connectorname) elif isinstance(block, FBD_Variable): - iec_path = "%s.%s"%(instance_path, block.GetName()) + iec_path = "%s.%s" % (instance_path, block.GetName()) elif isinstance(block, FBD_Connector): connection = self.GetConnectorByName(block.GetName()) if connection is not None: @@ -946,14 +1047,14 @@ if len(connector.Wires) == 1: iec_path = self.GetElementIECPath(connector.Wires[0][0]) elif isinstance(element, LD_Contact): - iec_path = "%s.%s"%(instance_path, element.GetName()) + iec_path = "%s.%s" % (instance_path, element.GetName()) elif isinstance(element, SFC_Step): - iec_path = "%s.%s.X"%(instance_path, element.GetName()) + iec_path = "%s.%s.X" % (instance_path, element.GetName()) elif isinstance(element, SFC_Transition): connectors = element.GetConnectors() previous_steps = self.GetPreviousSteps(connectors["inputs"]) next_steps = self.GetNextSteps(connectors["outputs"]) - iec_path = "%s.%s->%s"%(instance_path, ",".join(previous_steps), ",".join(next_steps)) + iec_path = "%s.%s->%s" % (instance_path, ",".join(previous_steps), ",".join(next_steps)) return iec_path def GetWireModifier(self, wire): @@ -972,9 +1073,19 @@ return connector.GetEdge() return "none" -#------------------------------------------------------------------------------- -# Reset functions -#------------------------------------------------------------------------------- + def CorrectElementSize(self, element, width, height): + min_width, min_height = element.GetMinSize() + if width < min_width: + width = min_width + if height < min_height: + height = min_height + if element.Size != (width, height): + element.SetSize(width, height) + element.RefreshModel() + + # ------------------------------------------------------------------------------- + # Reset functions + # ------------------------------------------------------------------------------- # Resets Viewer lists def ResetView(self): @@ -1092,35 +1203,9 @@ self.RefreshVisibleElements() self.Editor.Refresh(False) - -#------------------------------------------------------------------------------- -# Refresh functions -#------------------------------------------------------------------------------- - - VALUE_TRANSLATION = {True: _("Active"), False: _("Inactive")} - - def SetValue(self, value): - if self.Value != value: - self.Value = value - - xstart, ystart = self.GetViewStart() - window_size = self.Editor.GetClientSize() - refresh_rect = self.GetRedrawRect() - if (xstart * SCROLLBAR_UNIT <= refresh_rect.x + refresh_rect.width and - xstart * SCROLLBAR_UNIT + window_size[0] >= refresh_rect.x and - ystart * SCROLLBAR_UNIT <= refresh_rect.y + refresh_rect.height and - ystart * SCROLLBAR_UNIT + window_size[1] >= refresh_rect.y): - self.ElementNeedRefresh(self) - - def GetRedrawRect(self): - dc = self.GetLogicalDC() - ipw, iph = dc.GetTextExtent(_("Debug: %s") % self.InstancePath) - vw, vh = 0, 0 - for value in self.VALUE_TRANSLATION.itervalues(): - w, h = dc.GetTextExtent("(%s)" % value) - vw = max(vw, w) - vh = max(vh, h) - return wx.Rect(ipw + 4, 2, vw, vh) + # ------------------------------------------------------------------------------- + # Refresh functions + # ------------------------------------------------------------------------------- def ElementNeedRefresh(self, element): self.ElementRefreshList_lock.acquire() @@ -1150,9 +1235,6 @@ def RefreshView(self, variablepanel=True, selection=None): EditorPanel.RefreshView(self, variablepanel) - if self.TagName.split("::")[0] == "A" and self.Debug: - self.AddDataConsumer("%s.Q" % self.InstancePath.upper(), self) - if self.ToolTipElement is not None: self.ToolTipElement.DestroyToolTip() self.ToolTipElement = None @@ -1163,20 +1245,22 @@ self.Flush() self.ResetView() self.ResetBuffer() - instance = {} + # List of ids of already loaded blocks - instances = self.Controler.GetEditedElementInstancesInfos(self.TagName, debug = self.Debug) + instances = self.Controler.GetEditedElementInstancesInfos(self.TagName, debug=self.Debug) # Load Blocks until they are all loaded while len(instances) > 0: self.loadInstance(instances.popitem(0)[1], instances, selection) - if (selection is not None and - isinstance(self.SelectedElement, Graphic_Group)): + if selection is not None and isinstance(self.SelectedElement, Graphic_Group): self.SelectedElement.RefreshWireExclusion() self.SelectedElement.RefreshBoundingBox() self.RefreshScrollBars() + if self.TagName.split("::")[0] == "A" and self.Debug: + self.AddDataConsumer("%s.Q" % self.InstancePath.upper(), self.InstanceName) + for wire in self.Wires: if not wire.IsConnectedCompatible(): wire.SetValid(False) @@ -1213,7 +1297,7 @@ def GetPreviousSteps(self, connectors): steps = [] for connector in connectors: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): previous = wire.GetOtherConnected(connector).GetParentBlock() if isinstance(previous, SFC_Step): steps.append(previous.GetName()) @@ -1225,7 +1309,7 @@ def GetNextSteps(self, connectors): steps = [] for connector in connectors: - for wire, handle in connector.GetWires(): + for wire, _handle in connector.GetWires(): next = wire.GetOtherConnected(connector).GetParentBlock() if isinstance(next, SFC_Step): steps.append(next.GetName()) @@ -1256,7 +1340,8 @@ maxy = max(maxy, extent.y + extent.height) maxx = int(maxx * self.ViewScale[0]) maxy = int(maxy * self.ViewScale[1]) - self.Editor.SetScrollbars(SCROLLBAR_UNIT, SCROLLBAR_UNIT, + self.Editor.SetScrollbars( + SCROLLBAR_UNIT, SCROLLBAR_UNIT, round(maxx / SCROLLBAR_UNIT) + width_incr, round(maxy / SCROLLBAR_UNIT) + height_incr, xstart, ystart, True) @@ -1299,7 +1384,7 @@ def loadInstance(self, instance, remaining_instances, selection): self.current_id = max(self.current_id, instance.id) creation_function = ElementCreationFunctions.get(instance.type, None) - connectors = {"inputs" : [], "outputs" : []} + connectors = {"inputs": [], "outputs": []} specific_values = instance.specific_values if creation_function is not None: element = creation_function(self, instance.id, specific_values) @@ -1338,10 +1423,11 @@ connectors["outputs"].pop(0) executionControl = True block_name = specific_values.name if specific_values.name is not None else "" - element = FBD_Block(self, instance.type, block_name, - instance.id, len(connectors["inputs"]), - connectors=connectors, executionControl=executionControl, - executionOrder=specific_values.execution_order) + element = FBD_Block( + self, instance.type, block_name, + instance.id, len(connectors["inputs"]), + connectors=connectors, executionControl=executionControl, + executionOrder=specific_values.execution_order) if isinstance(element, Comment): self.AddComment(element) else: @@ -1353,7 +1439,7 @@ connector_pos = wx.Point(*output_connector.position) if isinstance(element, FBD_Block): connector = element.GetConnector(connector_pos, - output_name = output_connector.name) + output_name=output_connector.name) elif i < len(connectors["outputs"]): connector = connectors["outputs"][i] else: @@ -1369,7 +1455,7 @@ connector_pos = wx.Point(*input_connector.position) if isinstance(element, FBD_Block): connector = element.GetConnector(connector_pos, - input_name = input_connector.name) + input_name=input_connector.name) elif i < len(connectors["inputs"]): connector = connectors["inputs"][i] else: @@ -1384,6 +1470,7 @@ if not self.CreateWires(connector, instance.id, input_connector.links, remaining_instances, selection): element.RefreshModel() element.RefreshConnectors() + self.CorrectElementSize(element, instance.width, instance.height) if selection is not None and selection[0].get(instance.id, False): self.SelectInGroup(element) @@ -1414,20 +1501,18 @@ wire = Wire(self) wire.SetPoints(points) else: - wire = Wire(self, - [wx.Point(*start_connector.GetPosition()), - start_connector.GetDirection()], - [wx.Point(*end_connector.GetPosition()), - end_connector.GetDirection()]) + wire = Wire( + self, + [wx.Point(*start_connector.GetPosition()), start_connector.GetDirection()], + [wx.Point(*end_connector.GetPosition()), end_connector.GetDirection()]) start_connector.Wires.append((wire, 0)) end_connector.Wires.append((wire, -1)) wire.StartConnected = start_connector wire.EndConnected = end_connector connected.RefreshConnectors() self.AddWire(wire) - if selection is not None and (\ - selection[1].get((id, refLocalId), False) or \ - selection[1].get((refLocalId, id), False)): + if selection is not None and (selection[1].get((id, refLocalId), False) or + selection[1].get((refLocalId, id), False)): self.SelectInGroup(wire) else: links_connected = False @@ -1440,12 +1525,12 @@ def IsEndType(self, type): return self.Controler.IsEndType(type) - def GetBlockType(self, type, inputs = None): + def GetBlockType(self, type, inputs=None): return self.Controler.GetBlockType(type, inputs, self.Debug) -#------------------------------------------------------------------------------- -# Search Element functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Search Element functions + # ------------------------------------------------------------------------------- def FindBlock(self, event): dc = self.GetLogicalDC() @@ -1463,7 +1548,7 @@ return wire return None - def FindElement(self, event, exclude_group = False, connectors = True): + def FindElement(self, event, exclude_group=False, connectors=True): dc = self.GetLogicalDC() pos = event.GetLogicalPosition(dc) if self.SelectedElement and not (exclude_group and isinstance(self.SelectedElement, Graphic_Group)): @@ -1474,12 +1559,12 @@ return element return None - def FindBlockConnector(self, pos, direction = None, exclude = None): - result, error = self.FindBlockConnectorWithError(pos, direction, exclude) + def FindBlockConnector(self, pos, direction=None, exclude=None): + result, _error = self.FindBlockConnectorWithError(pos, direction, exclude) return result - def FindBlockConnectorWithError(self, pos, direction = None, exclude = None): - error = False + def FindBlockConnectorWithError(self, pos, direction=None, exclude=None): + error = False startblock = None for block in self.Blocks.itervalues(): connector = block.TestConnector(pos, direction, exclude) @@ -1492,7 +1577,7 @@ error = True return connector, error return None, error - + def FindElementById(self, id): block = self.Blocks.get(id, None) if block is not None: @@ -1516,12 +1601,13 @@ self.SelectedElement.SetElements(self.GetElements()) self.SelectedElement.SetSelected(True) -#------------------------------------------------------------------------------- -# Popup menu functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Popup menu functions + # ------------------------------------------------------------------------------- def GetForceVariableMenuFunction(self, iec_path, element): iec_type = self.GetDataType(iec_path) + def ForceVariableFunction(event): if iec_type is not None: dialog = ForceVariableDialog(self.ParentWindow, iec_type, str(element.GetValue())) @@ -1559,10 +1645,12 @@ menu.Enable(new_id, True) else: menu.Enable(new_id, False) + if self.Editor.HasCapture(): + self.Editor.ReleaseMouse() self.Editor.PopupMenu(menu) menu.Destroy() - def PopupBlockMenu(self, connector = None): + def PopupBlockMenu(self, connector=None): menu = wx.Menu(title='') if connector is not None and connector.IsCompatible("BOOL"): self.AddBlockPinMenuItems(menu, connector) @@ -1613,7 +1701,8 @@ if self.SelectedElement.GetStartConnected() in connected else self.SelectedElement.GetStartConnected()) - self.AddWireMenuItems(menu, delete, + self.AddWireMenuItems( + menu, delete, start_connector.GetDirection() == EAST and not isinstance(start_connector.GetParentBlock(), SFC_Step)) @@ -1646,9 +1735,9 @@ self.Editor.PopupMenu(menu) menu.Destroy() -#------------------------------------------------------------------------------- -# Menu items functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Menu items functions + # ------------------------------------------------------------------------------- def OnAlignLeftMenu(self, event): if self.SelectedElement is not None and isinstance(self.SelectedElement, Graphic_Group): @@ -1722,9 +1811,7 @@ def OnReplaceWireMenu(self, event): # Check that selected element is a wire before applying replace - if (self.SelectedElement is not None and - self.IsWire(self.SelectedElement)): - + if self.SelectedElement is not None and self.IsWire(self.SelectedElement): # Get wire redraw bbox to erase it from screen wire = self.SelectedElement redraw_rect = wire.GetRedrawRect() @@ -1744,7 +1831,7 @@ # Get a new default connection name connection_name = self.Controler.GenerateNewName( - self.TagName, None, "Connection%d", 0) + self.TagName, None, "Connection%d", 0) # Create a connector to connect to wire id = self.GetNewId() @@ -1754,7 +1841,6 @@ # Calculate position of connector at the right of start connector connector = connection.GetConnectors()["inputs"][0] rel_pos = connector.GetRelPosition() - direction = connector.GetDirection() start_point = start_connector.GetPosition(False) end_point = (start_point[0] + LD_WIRE_SIZE, start_point[1]) connection.SetPosition(end_point[0] - rel_pos[0], @@ -1787,7 +1873,6 @@ # Calculate position of connection at the left of end connector connector = connection.GetConnectors()["outputs"][0] rel_pos = connector.GetRelPosition() - direction = connector.GetDirection() end_point = end_connector.GetPosition(False) start_point = (end_point[0] - LD_WIRE_SIZE, end_point[1]) connection.SetPosition(start_point[0] - rel_pos[0], @@ -1795,8 +1880,8 @@ # Add Wire to Viewer and connect it to blocks new_wire = Wire(self, - [wx.Point(*start_point), connector.GetDirection()], - [wx.Point(*end_point), end_connector.GetDirection()]) + [wx.Point(*start_point), connector.GetDirection()], + [wx.Point(*end_point), end_connector.GetDirection()]) self.AddWire(new_wire) connector.Connect((new_wire, 0), False) end_connector.Connect((new_wire, -1), False) @@ -1834,7 +1919,7 @@ def OnEditBlockMenu(self, event): if self.SelectedElement is not None: - self.ParentWindow.EditProjectElement(ITEM_POU, "P::%s"%self.SelectedElement.GetType()) + self.ParentWindow.EditProjectElement(ITEM_POU, "P::%s" % self.SelectedElement.GetType()) def OnAdjustBlockSizeMenu(self, event): if self.SelectedElement is not None: @@ -1867,6 +1952,7 @@ def GetAddToWireMenuCallBack(self, func, *args): args += (self.SelectedElement,) + def AddToWireMenuCallBack(event): func(wx.Rect(0, 0, 0, 0), *args) return AddToWireMenuCallBack @@ -1876,16 +1962,16 @@ wx.CallAfter(func) return ClipboardCallback -#------------------------------------------------------------------------------- -# Mouse event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Mouse event functions + # ------------------------------------------------------------------------------- def OnViewerMouseEvent(self, event): self.ResetBuffer() if event.Leaving() and self.ToolTipElement is not None: self.ToolTipElement.DestroyToolTip() elif (not event.Entering() and - gettime() - self.LastToolTipCheckTime > REFRESH_PERIOD): + gettime() - self.LastToolTipCheckTime > REFRESH_PERIOD): self.LastToolTipCheckTime = gettime() element = None if not event.Leaving() and not event.LeftUp() and not event.LeftDClick(): @@ -1942,7 +2028,7 @@ if element is None or element.TestHandle(event) == (0, 0): connector = self.FindBlockConnector(pos, self.SelectedElement.GetConnectionDirection()) if connector is not None: - event.Dragging = lambda : True + event.Dragging = lambda: True self.SelectedElement.OnMotion(event, dc, self.Scaling) if self.SelectedElement.EndConnected is not None: self.SelectedElement.ResetPoints() @@ -1965,10 +2051,11 @@ WEST: [WEST, EAST], NORTH: [NORTH, SOUTH], SOUTH: [SOUTH, NORTH]}[connector.GetDirection()] - wire = Wire(self, *map(list, zip( - [wx.Point(pos.x, pos.y), - wx.Point(scaled_pos.x, scaled_pos.y)], - directions))) + wire = Wire(self, + *map(list, zip( + [wx.Point(pos.x, pos.y), + wx.Point(scaled_pos.x, scaled_pos.y)], + directions))) wire.oldPos = scaled_pos wire.Handle = (HANDLE_POINT, 0) wire.ProcessDragging(0, 0, event, None) @@ -2081,8 +2168,8 @@ # Popup contextual menu menu = wx.Menu() self.AddMenuItems(menu, - [(wx.NewId(), wx.ITEM_NORMAL, text, '', callback) - for text, callback in items]) + [(wx.NewId(), wx.ITEM_NORMAL, text, '', callback) + for text, callback in items]) self.PopupMenu(menu) self.SelectedElement.StartConnected.HighlightParentBlock(False) @@ -2188,16 +2275,18 @@ "functionBlock": ITEM_FUNCTIONBLOCK, }.get(self.Controler.GetPouType(instance_type)) if pou_type is not None and instance_type in self.Controler.GetProjectPouNames(self.Debug): - self.ParentWindow.OpenDebugViewer(pou_type, - "%s.%s"%(self.GetInstancePath(True), self.SelectedElement.GetName()), + self.ParentWindow.OpenDebugViewer( + pou_type, + "%s.%s" % (self.GetInstancePath(True), self.SelectedElement.GetName()), self.Controler.ComputePouName(instance_type)) else: iec_path = self.GetElementIECPath(self.SelectedElement) if iec_path is not None: if isinstance(self.SelectedElement, Wire): if self.SelectedElement.EndConnected is not None: - self.ParentWindow.OpenDebugViewer(ITEM_VAR_LOCAL, iec_path, - self.SelectedElement.EndConnected.GetType()) + self.ParentWindow.OpenDebugViewer( + ITEM_VAR_LOCAL, iec_path, + self.SelectedElement.EndConnected.GetType()) else: self.ParentWindow.OpenDebugViewer(ITEM_VAR_LOCAL, iec_path, "BOOL") elif event.ControlDown() and not event.ShiftDown(): @@ -2207,8 +2296,9 @@ else: instance_type = None if instance_type in self.Controler.GetProjectPouNames(self.Debug): - self.ParentWindow.EditProjectElement(ITEM_POU, - self.Controler.ComputePouName(instance_type)) + self.ParentWindow.EditProjectElement( + ITEM_POU, + self.Controler.ComputePouName(instance_type)) else: self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling) elif event.ControlDown() and event.ShiftDown(): @@ -2223,7 +2313,6 @@ def OnViewerMotion(self, event): if self.Editor.HasCapture() and not event.Dragging(): return - refresh = False dc = self.GetLogicalDC() pos = GetScaledEventPosition(event, dc, self.Scaling) if event.MiddleIsDown() or self.Mode == MODE_MOTION: @@ -2241,8 +2330,7 @@ self.RefreshScrollBars() self.RefreshVisibleElements() else: - if (not event.Dragging() and - gettime() - self.LastHighlightCheckTime > REFRESH_PERIOD): + if not event.Dragging() and (gettime() - self.LastHighlightCheckTime) > REFRESH_PERIOD: self.LastHighlightCheckTime = gettime() highlighted = self.FindElement(event, connectors=False) if self.HighlightedElement is not None and self.HighlightedElement != highlighted: @@ -2259,8 +2347,8 @@ elif not self.Debug and self.Mode == MODE_SELECTION and self.SelectedElement is not None: if self.DrawingWire: connector, errorHighlight = self.FindBlockConnectorWithError(pos, self.SelectedElement.GetConnectionDirection(), self.SelectedElement.EndConnected) - self.SelectedElement.ErrHighlight = errorHighlight; - if not connector or self.SelectedElement.EndConnected == None: + self.SelectedElement.ErrHighlight = errorHighlight + if not connector or self.SelectedElement.EndConnected is None: self.SelectedElement.ResetPoints() movex, movey = self.SelectedElement.OnMotion(event, dc, self.Scaling) self.SelectedElement.GeneratePoints() @@ -2322,11 +2410,11 @@ elif position.y > window_size[1] - SCROLL_ZONE: move_window.y = 1 if move_window.x != 0 or move_window.y != 0: - self.RefreshVisibleElements(xp = xstart + move_window.x, yp = ystart + move_window.y) + self.RefreshVisibleElements(xp=xstart + move_window.x, yp=ystart + move_window.y) self.Scroll(xstart + move_window.x, ystart + move_window.y) self.RefreshScrollBars(move_window.x, move_window.y) - def BlockCompatibility(self, startblock=None, endblock=None, direction = None): + def BlockCompatibility(self, startblock=None, endblock=None, direction=None): return True def GetPopupMenuItems(self): @@ -2354,29 +2442,29 @@ items.append((_(u'Divergence'), self.GetAddToWireMenuCallBack(self.AddNewDivergence, poss_div_types))) elif start_direction == EAST: + items.extend([ + (_(u'Block'), self.GetAddToWireMenuCallBack(self.AddNewBlock)), + (_(u'Connection'), self.GetAddToWireMenuCallBack(self.AddNewConnection))]) + + if self.CurrentLanguage != "FBD": + items.append((_(u'Contact'), self.GetAddToWireMenuCallBack(self.AddNewContact))) + + if self.CurrentLanguage == "LD": items.extend([ - (_(u'Block'), self.GetAddToWireMenuCallBack(self.AddNewBlock)), - (_(u'Connection'), self.GetAddToWireMenuCallBack(self.AddNewConnection))]) - - if self.CurrentLanguage != "FBD": - items.append((_(u'Contact'), self.GetAddToWireMenuCallBack(self.AddNewContact))) - - if self.CurrentLanguage == "LD": - items.extend([ - (_(u'Coil'), self.GetAddToWireMenuCallBack(self.AddNewCoil)), - (_(u'Power Rail'), self.GetAddToWireMenuCallBack(self.AddNewPowerRail))]) - - if self.CurrentLanguage == "SFC": - items.append( - (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, True))) - else: - items.append( - (_(u'Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True))) + (_(u'Coil'), self.GetAddToWireMenuCallBack(self.AddNewCoil)), + (_(u'Power Rail'), self.GetAddToWireMenuCallBack(self.AddNewPowerRail))]) + + if self.CurrentLanguage == "SFC": + items.append( + (_(u'Transition'), self.GetAddToWireMenuCallBack(self.AddNewTransition, True))) + else: + items.append( + (_(u'Variable'), self.GetAddToWireMenuCallBack(self.AddNewVariable, True))) return items -#------------------------------------------------------------------------------- -# Keyboard event functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Keyboard event functions + # ------------------------------------------------------------------------------- ARROW_KEY_MOVE = { wx.WXK_LEFT: (-1, 0), @@ -2404,7 +2492,7 @@ self.RefreshRect(self.GetScrolledRect(rect), False) elif not self.Debug and keycode == wx.WXK_RETURN and self.SelectedElement is not None: self.SelectedElement.OnLeftDClick(event, self.GetLogicalDC(), self.Scaling) - elif self.ARROW_KEY_MOVE.has_key(keycode): + elif keycode in self.ARROW_KEY_MOVE: move = self.ARROW_KEY_MOVE[keycode] if event.ControlDown() and event.ShiftDown(): self.Scroll({-1: 0, 0: xpos, 1: xmax}[move[0]], @@ -2452,9 +2540,9 @@ else: event.Skip() -#------------------------------------------------------------------------------- -# Model adding functions from Drop Target -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model adding functions from Drop Target + # ------------------------------------------------------------------------------- def AddVariableBlock(self, x, y, scaling, var_class, var_name, var_type): id = self.GetNewId() @@ -2475,9 +2563,9 @@ self.RefreshVisibleElements() self.Editor.Refresh(False) -#------------------------------------------------------------------------------- -# Model adding functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model adding functions + # ------------------------------------------------------------------------------- def GetScaledSize(self, width, height): if self.Scaling is not None: @@ -2522,10 +2610,11 @@ id = self.GetNewId() values = dialog.GetValues() values.setdefault("name", "") - block = FBD_Block(self, values["type"], values["name"], id, - values["extension"], values["inputs"], - executionControl = values["executionControl"], - executionOrder = values["executionOrder"]) + block = FBD_Block( + self, values["type"], values["name"], id, + values["extension"], values["inputs"], + executionControl=values["executionControl"], + executionOrder=values["executionOrder"]) self.Controler.AddEditedElementBlock(self.TagName, id, values["type"], values.get("name", None)) connector = None if wire is not None: @@ -2535,6 +2624,8 @@ connector = input_connector break self.AddNewElement(block, bbox, wire, connector) + self.RefreshVariablePanel() + self.ParentWindow.RefreshPouInstanceVariablesPanel() dialog.Destroy() def AddNewVariable(self, bbox, exclude_input=False, wire=None): @@ -2574,7 +2665,7 @@ dialog = wx.TextEntryDialog(self.ParentWindow, _("Edit comment"), _("Please enter comment text"), - "", wx.OK|wx.CANCEL|wx.TE_MULTILINE) + "", wx.OK | wx.CANCEL | wx.TE_MULTILINE) dialog.SetClientSize(wx.Size(400, 200)) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetValue() @@ -2582,7 +2673,7 @@ comment = Comment(self, value, id) comment.SetPosition(bbox.x, bbox.y) min_width, min_height = comment.GetMinSize() - comment.SetSize(*self.GetScaledSize(max(min_width,bbox.width),max(min_height,bbox.height))) + comment.SetSize(*self.GetScaledSize(max(min_width, bbox.width), max(min_height, bbox.height))) self.AddComment(comment) self.Controler.AddEditedElementComment(self.TagName, id) self.RefreshCommentModel(comment) @@ -2638,11 +2729,11 @@ def AddNewStep(self, bbox, initial=False, wire=None): if wire is not None: values = { - "name": self.Controler.GenerateNewName( - self.TagName, None, "Step%d", 0), - "input": True, + "name": self.Controler.GenerateNewName(self.TagName, None, "Step%d", 0), + "input": True, "output": True, - "action":False} + "action": False + } else: dialog = SFCStepDialog(self.ParentWindow, self.Controler, self.TagName, initial) dialog.SetPreviewFont(self.GetFont()) @@ -2685,7 +2776,7 @@ connector = transition.GetConnectors()["inputs"][0] self.AddNewElement(transition, bbox, wire, connector) - def AddNewDivergence(self, bbox, poss_div_types = None, wire=None): + def AddNewDivergence(self, bbox, poss_div_types=None, wire=None): dialog = SFCDivergenceDialog(self.ParentWindow, self.Controler, self.TagName, poss_div_types) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize((bbox.width, bbox.height)) @@ -2703,8 +2794,10 @@ if isinstance(block, SFC_Step): choices.append(block.GetName()) dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Add a new jump"), _("Please choose a target"), - choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + _("Add a new jump"), + _("Please choose a target"), + choices, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) if dialog.ShowModal() == wx.ID_OK: id = self.GetNewId() jump = SFC_Jump(self, dialog.GetStringSelection(), id) @@ -2724,20 +2817,22 @@ self.AddNewElement(actionblock, bbox, wire) dialog.Destroy() -#------------------------------------------------------------------------------- -# Edit element content functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Edit element content functions + # ------------------------------------------------------------------------------- def EditBlockContent(self, block): dialog = FBDBlockDialog(self.ParentWindow, self.Controler, self.TagName) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(block.GetSize()) - old_values = {"name" : block.GetName(), - "type" : block.GetType(), - "extension" : block.GetExtension(), - "inputs" : block.GetInputTypes(), - "executionControl" : block.GetExecutionControl(), - "executionOrder" : block.GetExecutionOrder()} + old_values = { + "name": block.GetName(), + "type": block.GetType(), + "extension": block.GetExtension(), + "inputs": block.GetInputTypes(), + "executionControl": block.GetExecutionControl(), + "executionOrder": block.GetExecutionOrder() + } dialog.SetValues(old_values) if dialog.ShowModal() == wx.ID_OK: new_values = dialog.GetValues() @@ -2747,7 +2842,7 @@ else: block.SetName("") block.SetSize(*self.GetScaledSize(new_values["width"], new_values["height"])) - block.SetType(new_values["type"], new_values["extension"], executionControl = new_values["executionControl"]) + block.SetType(new_values["type"], new_values["extension"], executionControl=new_values["executionControl"]) block.SetExecutionOrder(new_values["executionOrder"]) rect = rect.Union(block.GetRedrawRect()) self.RefreshBlockModel(block) @@ -2766,8 +2861,11 @@ dialog = FBDVariableDialog(self.ParentWindow, self.Controler, self.TagName) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(variable.GetSize()) - old_values = {"expression" : variable.GetName(), "class" : variable.GetType(), - "executionOrder" : variable.GetExecutionOrder()} + old_values = { + "expression": variable.GetName(), + "class": variable.GetType(), + "executionOrder": variable.GetExecutionOrder() + } dialog.SetValues(old_values) if dialog.ShowModal() == wx.ID_OK: new_values = dialog.GetValues() @@ -2795,7 +2893,7 @@ dialog = ConnectionDialog(self.ParentWindow, self.Controler, self.TagName, True) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(connection.GetSize()) - values = {"name" : connection.GetName(), "type" : connection.GetType()} + values = {"name": connection.GetName(), "type": connection.GetType()} dialog.SetValues(values) result = dialog.ShowModal() dialog.Destroy() @@ -2827,8 +2925,8 @@ dialog = LDElementDialog(self.ParentWindow, self.Controler, self.TagName, "contact") dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(contact.GetSize()) - dialog.SetValues({"variable" : contact.GetName(), - "modifier" : contact.GetType()}) + dialog.SetValues({"variable": contact.GetName(), + "modifier": contact.GetType()}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = contact.GetRedrawRect(1, 1) @@ -2847,8 +2945,8 @@ dialog = LDElementDialog(self.ParentWindow, self.Controler, self.TagName, "coil") dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(coil.GetSize()) - dialog.SetValues({"variable" : coil.GetName(), - "modifier" : coil.GetType()}) + dialog.SetValues({"variable": coil.GetName(), + "modifier": coil.GetType()}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = coil.GetRedrawRect(1, 1) @@ -2871,7 +2969,7 @@ dialog.SetValues({ "type": powerrail.GetType(), "pin_number": len(powerrail.GetConnectors()[ - ("outputs" if powerrail_type == LEFTRAIL else "inputs")])}) + ("outputs" if powerrail_type == LEFTRAIL else "inputs")])}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = powerrail.GetRedrawRect(1, 1) @@ -2895,10 +2993,10 @@ dialog.SetMinElementSize(step.GetSize()) connectors = step.GetConnectors() dialog.SetValues({ - "name" : step.GetName(), + "name": step.GetName(), "input": len(connectors["inputs"]) > 0, "output": len(connectors["outputs"]) > 0, - "action": step.GetActionConnector() != None}) + "action": step.GetActionConnector() is not None}) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = step.GetRedrawRect(1, 1) @@ -2915,7 +3013,7 @@ rect = rect.Union(block.GetRedrawRect()) block.Refresh(rect) step.SetName(new_name) - + if values["input"]: step.AddInput() else: @@ -2940,11 +3038,15 @@ dialog = SFCTransitionDialog(self.ParentWindow, self.Controler, self.TagName, self.GetDrawingMode() == FREEDRAWING_MODE) dialog.SetPreviewFont(self.GetFont()) dialog.SetMinElementSize(transition.GetSize()) - dialog.SetValues({"type":transition.GetType(),"value":transition.GetCondition(), "priority":transition.GetPriority()}) + dialog.SetValues({ + "type": transition.GetType(), + "value": transition.GetCondition(), + "priority": transition.GetPriority() + }) if dialog.ShowModal() == wx.ID_OK: values = dialog.GetValues() rect = transition.GetRedrawRect(1, 1) - transition.SetType(values["type"],values["value"]) + transition.SetType(values["type"], values["value"]) transition.SetPriority(values["priority"]) rect = rect.Union(transition.GetRedrawRect()) self.RefreshTransitionModel(transition) @@ -2960,8 +3062,10 @@ if isinstance(block, SFC_Step): choices.append(block.GetName()) dialog = wx.SingleChoiceDialog(self.ParentWindow, - _("Edit jump target"), _("Please choose a target"), - choices, wx.DEFAULT_DIALOG_STYLE|wx.OK|wx.CANCEL) + _("Edit jump target"), + _("Please choose a target"), + choices, + wx.DEFAULT_DIALOG_STYLE | wx.OK | wx.CANCEL) try: indx = choices.index(jump.GetTarget()) dialog.SetSelection(indx) @@ -3004,8 +3108,10 @@ _("Edit comment"), _("Please enter comment text"), comment.GetContent(), - wx.OK|wx.CANCEL|wx.TE_MULTILINE) - dialog.SetClientSize(wx.Size(400, 200)) + wx.OK | wx.CANCEL | wx.TE_MULTILINE) + width, height = comment.GetSize() + dialogSize = wx.Size(max(width + 30, 400), max(height + 60, 200)) + dialog.SetClientSize(dialogSize) if dialog.ShowModal() == wx.ID_OK: value = dialog.GetValue() rect = comment.GetRedrawRect(1, 1) @@ -3019,9 +3125,9 @@ comment.Refresh(rect) dialog.Destroy() -#------------------------------------------------------------------------------- -# Model update functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Model update functions + # ------------------------------------------------------------------------------- def RefreshBlockModel(self, block): blockid = block.GetId() @@ -3168,11 +3274,9 @@ infos["connector"] = actionblock.GetConnector() self.Controler.SetEditedElementActionBlockInfos(self.TagName, actionblockid, infos) - -#------------------------------------------------------------------------------- -# Model delete functions -#------------------------------------------------------------------------------- - + # ------------------------------------------------------------------------------- + # Model delete functions + # ------------------------------------------------------------------------------- def DeleteBlock(self, block): elements = [] @@ -3319,10 +3423,9 @@ self.RemoveBlock(actionblock) self.Controler.RemoveEditedElementInstance(self.TagName, actionblock.GetId()) - -#------------------------------------------------------------------------------- -# Editing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Editing functions + # ------------------------------------------------------------------------------- def Cut(self): if not self.Debug and (self.IsBlock(self.SelectedElement) or self.IsComment(self.SelectedElement) or isinstance(self.SelectedElement, Graphic_Group)): @@ -3365,7 +3468,7 @@ self.RefreshVariablePanel() self.ParentWindow.RefreshPouInstanceVariablesPanel() else: - message = wx.MessageDialog(self.Editor, result, "Error", wx.OK|wx.ICON_ERROR) + message = wx.MessageDialog(self.Editor, result, "Error", wx.OK | wx.ICON_ERROR) message.ShowModal() message.Destroy() @@ -3380,7 +3483,7 @@ return True return False - def GenerateNewName(self, element=None, blocktype=None, exclude={}): + def GenerateNewName(self, element=None, blocktype=None, exclude=None): if element is not None and isinstance(element, SFC_Step): format = "Step%d" else: @@ -3453,9 +3556,9 @@ self.Controler.AddEditedElementActionBlock(self.TagName, block.GetId()) self.RefreshActionBlockModel(block) -#------------------------------------------------------------------------------- -# Find and Replace functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Find and Replace functions + # ------------------------------------------------------------------------------- def Find(self, direction, search_params): if self.SearchParams != search_params: @@ -3464,7 +3567,7 @@ self.SearchParams = search_params self.SearchResults = [] blocks = [] - for infos, start, end, text in self.Controler.SearchInPou(self.TagName, search_params, self.Debug): + for infos, start, end, _text in self.Controler.SearchInPou(self.TagName, search_params, self.Debug): if (infos[0] == self.TagName or self.TagName.split("::")[0] in ['A', 'T']) and infos[1] is not 'name': if infos[1] in ["var_local", "var_input", "var_output", "var_inout"]: self.SearchResults.append((infos[1:], start, end, SEARCH_RESULT_HIGHLIGHT)) @@ -3496,9 +3599,9 @@ self.RemoveHighlight(*self.CurrentFindHighlight) self.CurrentFindHighlight = None -#------------------------------------------------------------------------------- -# Highlights showing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Highlights showing functions + # ------------------------------------------------------------------------------- def OnRefreshHighlightsTimer(self, event): self.RefreshView() @@ -3537,9 +3640,9 @@ if block is not None: block.AddHighlight(infos[2:], start, end, highlight_type) -#------------------------------------------------------------------------------- -# Drawing functions -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Drawing functions + # ------------------------------------------------------------------------------- def OnScrollWindow(self, event): if self.Editor.HasCapture() and self.StartMousePos is not None: @@ -3549,9 +3652,9 @@ self.Editor.Freeze() wx.CallAfter(self.Editor.Thaw) elif event.GetOrientation() == wx.HORIZONTAL: - self.RefreshVisibleElements(xp = event.GetPosition()) + self.RefreshVisibleElements(xp=event.GetPosition()) else: - self.RefreshVisibleElements(yp = event.GetPosition()) + self.RefreshVisibleElements(yp=event.GetPosition()) # Handle scroll in debug to fully redraw area and ensuring # instance path is fully draw without flickering @@ -3574,16 +3677,15 @@ if event.ShiftDown(): x, y = self.GetViewStart() xp = max(0, min(x - rotation * 3, self.Editor.GetVirtualSize()[0] / self.Editor.GetScrollPixelsPerUnit()[0])) - self.RefreshVisibleElements(xp = xp) + self.RefreshVisibleElements(xp=xp) self.Scroll(xp, y) elif event.ControlDown(): - dc = self.GetLogicalDC() - self.SetScale(self.CurrentScale + rotation, mouse_event = event) + self.SetScale(self.CurrentScale + rotation, mouse_event=event) self.ParentWindow.RefreshDisplayMenu() else: x, y = self.GetViewStart() yp = max(0, min(y - rotation * 3, self.Editor.GetVirtualSize()[1] / self.Editor.GetScrollPixelsPerUnit()[1])) - self.RefreshVisibleElements(yp = yp) + self.RefreshVisibleElements(yp=yp) self.Scroll(x, yp) def OnMoveWindow(self, event): @@ -3594,7 +3696,7 @@ self.RefreshVisibleElements() event.Skip() - def DoDrawing(self, dc, printing = False): + def DoDrawing(self, dc, printing=False): if printing: if getattr(dc, "printing", False): font = wx.Font(self.GetFont().GetPointSize(), wx.MODERN, wx.NORMAL, wx.NORMAL) @@ -3631,11 +3733,11 @@ comment.Draw(dc) for wire in self.Wires.iterkeys(): if wire != self.SelectedElement and (wire.IsVisible() or printing): - if not self.Debug or wire.GetValue() != True: + if not self.Debug or not wire.GetValue(): wire.Draw(dc) if self.Debug: for wire in self.Wires.iterkeys(): - if wire != self.SelectedElement and (wire.IsVisible() or printing) and wire.GetValue() == True: + if wire != self.SelectedElement and (wire.IsVisible() or printing) and wire.GetValue(): wire.Draw(dc) for block in self.Blocks.itervalues(): if block != self.SelectedElement and (block.IsVisible() or printing): @@ -3646,28 +3748,7 @@ if not printing: if self.Debug: - scalex, scaley = dc.GetUserScale() - dc.SetUserScale(1, 1) - - is_action = self.TagName.split("::")[0] == "A" - text = _("Debug: %s") % self.InstancePath - if is_action and self.Value is not None: - text += " (" - text_offset_x, text_offset_y = self.CalcUnscrolledPosition(2, 2) - dc.DrawText(text, text_offset_x, text_offset_y) - if is_action and self.Value is not None: - value_text = self.VALUE_TRANSLATION[self.Value] - tw, th = dc.GetTextExtent(text) - if self.Value: - dc.SetTextForeground(wx.GREEN) - dc.DrawText(value_text, text_offset_x + tw, text_offset_y) - if self.Value: - dc.SetTextForeground(wx.BLACK) - vw, vh = dc.GetTextExtent(value_text) - dc.DrawText(")", text_offset_x + tw + vw + 2, text_offset_y) - - dc.SetUserScale(scalex, scaley) - + self.InstanceName.Draw(dc) if self.rubberBand.IsShown(): self.rubberBand.Draw(dc) dc.EndDrawing() @@ -3679,5 +3760,3 @@ if self.Debug: DebugViewer.RefreshNewData(self) event.Skip() - - diff -r c1298e7ffe3a -r 8391c11477f4 graphics/DebugDataConsumer.py --- a/graphics/DebugDataConsumer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/DebugDataConsumer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,11 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import datetime -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Date and Time conversion function -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- SECOND = 1000000 # Number of microseconds in one second MINUTE = 60 * SECOND # Number of microseconds in one minute @@ -36,6 +39,7 @@ # Date corresponding to Epoch (1970 January the first) DATE_ORIGIN = datetime.datetime(1970, 1, 1) + def get_microseconds(value): """ Function converting time duration expressed in day, second and microseconds @@ -43,10 +47,10 @@ @param value: Time duration to convert @return: Time duration expressed in microsecond """ - return float(value.days * DAY + \ - value.seconds * SECOND + \ + return float(value.days * DAY + + value.seconds * SECOND + value.microseconds) - return + def generate_time(value): """ @@ -56,40 +60,41 @@ @return: IEC 61131 TIME literal """ microseconds = get_microseconds(value) - + # Get absolute microseconds value and save if it was negative negative = microseconds < 0 microseconds = abs(microseconds) - + # TIME literal prefix data = "T#" if negative: data += "-" - + # In TIME literal format, it isn't mandatory to indicate null values # if no greater non-null values are available. This variable is used to # inhibit formatting until a non-null value is found not_null = False - + for val, format in [ - (int(microseconds) / DAY, "%dd"), # Days - ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours - ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes - ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds - - # Add value to TIME literal if value is non-null or another non-null + (int(microseconds) / DAY, "%dd"), # Days + ((int(microseconds) % DAY) / HOUR, "%dh"), # Hours + ((int(microseconds) % HOUR) / MINUTE, "%dm"), # Minutes + ((int(microseconds) % MINUTE) / SECOND, "%ds")]: # Seconds + + # Add value to TIME literal if value is non-null or another non-null # value have already be found if val > 0 or not_null: data += format % val - + # Update non-null variable not_null = True - - # In any case microseconds have to be added to TIME literal + + # In any case microseconds have to be added to TIME literal data += "%gms" % (microseconds % SECOND / 1000.) - + return data + def generate_date(value): """ Function converting time duration expressed in day, second and microseconds @@ -99,6 +104,7 @@ """ return (DATE_ORIGIN + value).strftime("DATE#%Y-%m-%d") + def generate_datetime(value): """ Function converting time duration expressed in day, second and microseconds @@ -108,6 +114,7 @@ """ return (DATE_ORIGIN + value).strftime("DT#%Y-%m-%d-%H:%M:%S.%f") + def generate_timeofday(value): """ Function converting time duration expressed in day, second and microseconds @@ -116,22 +123,23 @@ @return: IEC 61131 TIME_OF_DAY literal """ microseconds = get_microseconds(value) - + # TIME_OF_DAY literal prefix data = "TOD#" - + for val, format in [ (int(microseconds) / HOUR, "%2.2d:"), # Hours ((int(microseconds) % HOUR) / MINUTE, "%2.2d:"), # Minutes ((int(microseconds) % MINUTE) / SECOND, "%2.2d."), # Seconds (microseconds % SECOND, "%6.6d")]: # Microseconds - + # Add value to TIME_OF_DAY literal data += format % val - + return data -# Dictionary of translation functions from value send by debugger to IEC + +# Dictionary of translation functions from value send by debugger to IEC # literal stored by type TYPE_TRANSLATOR = { "TIME": generate_time, @@ -143,18 +151,19 @@ "REAL": lambda v: "%.6g" % v, "LREAL": lambda v: "%.6g" % v} -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Debug Data Consumer Class -#------------------------------------------------------------------------------- - -""" -Class that implements an element that consumes debug values -Value update can be inhibited during the time the associated Debug Viewer is -refreshing -""" - -class DebugDataConsumer: - +# ------------------------------------------------------------------------------- + + +class DebugDataConsumer(object): + """ + Class that implements an element that consumes debug values + Value update can be inhibited during the time the associated Debug Viewer is + refreshing + """ + def __init__(self): """ Constructor @@ -162,17 +171,17 @@ # Debug value and forced flag self.Value = None self.Forced = False - + # Store debug value and forced flag when value update is inhibited self.LastValue = None self.LastForced = False - + # Value IEC data type self.DataType = None - + # Flag that value update is inhibited self.Inhibited = False - + def Inhibit(self, inhibit): """ Set flag to inhibit or activate value update @@ -180,23 +189,23 @@ """ # Save inhibit flag self.Inhibited = inhibit - + # When reactivated update value and forced flag with stored values if not inhibit and self.LastValue is not None: self.SetForced(self.LastForced) self.SetValue(self.LastValue) - + # Reset stored values self.LastValue = None self.LastForced = False - + def SetDataType(self, data_type): """ Set value IEC data type @param data_type: Value IEC data type """ self.DataType = data_type - + def NewValues(self, tick, values, raw="BOOL"): """ Function called by debug thread when a new debug value is available @@ -206,21 +215,21 @@ @param raw: Data type of values not translated (default: 'BOOL') """ value, forced = values - + # Translate value to IEC literal if self.DataType != raw: value = TYPE_TRANSLATOR.get(self.DataType, str)(value) - + # Store value and forced flag when value update is inhibited if self.Inhibited: self.LastValue = value self.LastForced = forced - + # Update value and forced flag in any other case else: self.SetForced(forced) self.SetValue(value) - + def SetValue(self, value): """ Update value. @@ -228,14 +237,14 @@ @param value: New value """ self.Value = value - + def GetValue(self): """ Return current value @return: Current value """ return self.Value - + def SetForced(self, forced): """ Update Forced flag. @@ -243,7 +252,7 @@ @param forced: New forced flag """ self.Forced = forced - + def IsForced(self): """ Indicate if current value is forced diff -r c1298e7ffe3a -r 8391c11477f4 graphics/FBD_Objects.py --- a/graphics/FBD_Objects.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/FBD_Objects.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,26 +22,30 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.GraphicCommons import * from plcopen.structures import * -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Function Block Diagram Block -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a function block -""" +# ------------------------------------------------------------------------------- + def TestConnectorName(name, block_type): return name in ["OUT", "MN", "MX"] or name.startswith("IN") and (block_type, name) != ("EXPT", "IN2") + class FBD_Block(Graphic_Element): - + """ + Class that implements the graphic representation of a function block + """ + # Create a new block - def __init__(self, parent, type, name, id = None, extension = 0, inputs = None, connectors = {}, executionControl = False, executionOrder = 0): + def __init__(self, parent, type, name, id=None, extension=0, inputs=None, connectors=None, executionControl=False, executionOrder=0): Graphic_Element.__init__(self, parent) self.Type = None self.Description = None @@ -56,9 +60,9 @@ self.Pen = MiterPen(wx.BLACK) self.SetType(type, extension, inputs, connectors, executionControl) self.Highlights = {} - + # Make a clone of this FBD_Block - def Clone(self, parent, id = None, name = "", pos = None): + def Clone(self, parent, id=None, name="", pos=None): if self.Name != "" and name == "": name = self.Name block = FBD_Block(parent, self.Type, name, id, self.Extension) @@ -70,10 +74,10 @@ block.Inputs = [input.Clone(block) for input in self.Inputs] block.Outputs = [output.Clone(block) for output in self.Outputs] return block - + def GetConnectorTranslation(self, element): return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) - + def Flush(self): for input in self.Inputs: input.Flush() @@ -81,9 +85,9 @@ for output in self.Outputs: output.Flush() self.Outputs = [] - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: for input in self.Inputs: @@ -93,26 +97,26 @@ if output.IsConnected(): rect = rect.Union(output.GetConnectedRedrawRect(movex, movey)) return rect - + # Delete this block by calling the appropriate method def Delete(self): self.Parent.DeleteBlock(self) - + # Unconnect all inputs and outputs def Clean(self): for input in self.Inputs: - input.UnConnect(delete = True) + input.UnConnect(delete=True) for output in self.Outputs: - output.UnConnect(delete = True) - + output.UnConnect(delete=True) + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Refresh the size of text for execution order def RefreshExecutionOrderSize(self): self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder)) - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if self.Name != "": @@ -121,7 +125,7 @@ test_text = False test_block = self.GetBlockBoundingBox(connectors).InsideXY(pt.x, pt.y) return test_text or test_block - + # Returns the bounding box of the name outside the block def GetTextBoundingBox(self): # Calculate the size of the name outside the block @@ -130,7 +134,7 @@ self.Pos.y - (text_height + 2), text_width, text_height) - + # Returns the bounding box of function block without name outside def GetBlockBoundingBox(self, connectors=True): bbx_x, bbx_y = self.Pos.x, self.Pos.y @@ -143,13 +147,13 @@ bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0]) bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2) return wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Refresh the block bounding box def RefreshBoundingBox(self): self.BoundingBox = self.GetBlockBoundingBox() if self.Name != "": self.BoundingBox.Union(self.GetTextBoundingBox()) - + # Refresh the positions of the block connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -170,16 +174,16 @@ self.Outputs[i].SetPosition(wx.Point(self.Size[0], ypos)) position += linesize self.RefreshConnected() - + # Refresh the positions of wires connected to inputs and outputs - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): for input in self.Inputs: input.MoveConnected(exclude) for output in self.Outputs: output.MoveConnected(exclude) - - # Returns the block connector that starts with the point given if it exists - def GetConnector(self, position, output_name = None, input_name = None): + + # Returns the block connector that starts with the point given if it exists + def GetConnector(self, position, output_name=None, input_name=None): if input_name is not None: # Test each input connector for input in self.Inputs: @@ -193,14 +197,14 @@ if input_name is None and output_name is None: return self.FindNearestConnector(position, self.Inputs + self.Outputs) return None - + def GetInputTypes(self): return tuple([input.GetType(True) for input in self.Inputs if input.GetName() != "EN"]) - + def SetOutputValues(self, values): for output in self.Outputs: - output.SetValue(values.get(ouput.getName(), None)) - + output.SetValue(values.get(output.getName(), None)) + def GetConnectionResultType(self, connector, connectortype): if not TestConnectorName(connector.GetName(), self.Type): return connectortype @@ -216,13 +220,13 @@ if resulttype is None or outputtype is not None and self.IsOfType(outputtype, resulttype): resulttype = outputtype return resulttype - + # Returns all the block connectors def GetConnectors(self): - return {"inputs" : self.Inputs, "outputs" : self.Outputs} - + return {"inputs": self.Inputs, "outputs": self.Outputs} + # Test if point given is on one of the block connectors - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): # Test each input connector for input in self.Inputs: if input.TestPoint(pt, direction, exclude): @@ -232,10 +236,10 @@ if output.TestPoint(pt, direction, exclude): return output return None - + # Changes the block type - def SetType(self, type, extension, inputs = None, connectors = {}, executionControl = False): - if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl: + def SetType(self, type, extension, inputs=None, connectors=None, executionControl=False): + if type != self.Type or self.Extension != extension or executionControl != self.ExecutionControl: if type != self.Type: self.Type = type self.TypeSize = self.Parent.GetTextExtent(self.Type) @@ -250,25 +254,26 @@ outputs = [output for output in blocktype["outputs"]] if blocktype["extensible"]: start = int(inputs[-1][0].replace("IN", "")) - for i in xrange(self.Extension - len(blocktype["inputs"])): + for dummy in xrange(self.Extension - len(blocktype["inputs"])): start += 1 - inputs.append(("IN%d"%start, inputs[-1][1], inputs[-1][2])) + inputs.append(("IN%d" % start, inputs[-1][1], inputs[-1][2])) comment = blocktype["comment"] self.Description = _(comment) + blocktype.get("usage", "") else: self.Colour = wx.RED + connectors = {} if connectors is None else connectors inputs = connectors.get("inputs", []) outputs = connectors.get("outputs", []) self.Description = None if self.ExecutionControl: - inputs.insert(0, ("EN","BOOL","none")) - outputs.insert(0, ("ENO","BOOL","none")) + inputs.insert(0, ("EN", "BOOL", "none")) + outputs.insert(0, ("ENO", "BOOL", "none")) self.Pen = MiterPen(self.Colour) - + # Extract the inputs properties and create or modify the corresponding connector input_connectors = [] for input_name, input_type, input_modifier in inputs: - connector = Connector(self, input_name, input_type, wx.Point(0, 0), WEST, onlyone = True) + connector = Connector(self, input_name, input_type, wx.Point(0, 0), WEST, onlyone=True) if input_modifier == "negated": connector.SetNegated(True) elif input_modifier != "none": @@ -282,9 +287,9 @@ break input_connectors.append(connector) for input in self.Inputs: - input.UnConnect(delete = True) + input.UnConnect(delete=True) self.Inputs = input_connectors - + # Extract the outputs properties and create or modify the corresponding connector output_connectors = [] for output_name, output_type, output_modifier in outputs: @@ -302,100 +307,100 @@ break output_connectors.append(connector) for output in self.Outputs: - output.UnConnect(delete = True) + output.UnConnect(delete=True) self.Outputs = output_connectors - + self.RefreshMinSize() self.RefreshConnectors() for output in self.Outputs: output.RefreshWires() self.RefreshBoundingBox() - + # Returns the block type def GetType(self): return self.Type - + # Changes the block name def SetName(self, name): self.Name = name self.RefreshNameSize() - + # Returs the block name def GetName(self): return self.Name - + # Changes the extension name def SetExtension(self, extension): self.Extension = extension - + # Returs the extension name def GetExtension(self): return self.Extension - + # Changes the execution order def SetExecutionOrder(self, executionOrder): self.ExecutionOrder = executionOrder self.RefreshExecutionOrderSize() - + # Returs the execution order def GetExecutionOrder(self): return self.ExecutionOrder - + # Returs the execution order def GetExecutionControl(self): return self.ExecutionControl - + # Refresh the block minimum size def RefreshMinSize(self): # Calculate the inputs maximum width max_input = 0 for input in self.Inputs: - w, h = input.GetNameSize() + w, _h = input.GetNameSize() max_input = max(max_input, w) # Calculate the outputs maximum width max_output = 0 for output in self.Outputs: - w, h = output.GetNameSize() + w, _h = output.GetNameSize() max_output = max(max_output, w) width = max(self.TypeSize[0] + 10, max_input + max_output + 15) height = (max(len(self.Inputs), len(self.Outputs)) + 1) * BLOCK_LINE_SIZE self.MinSize = width, height - + # Returns the block minimum size def GetMinSize(self): return self.MinSize - + # Changes the negated property of the connector handled def SetConnectorNegated(self, negated): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.SetNegated(negated) self.RefreshModel(False) - + # Changes the edge property of the connector handled def SetConnectorEdge(self, edge): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.SetEdge(edge) self.RefreshModel(False) - -## # Method called when a Motion event have been generated -## def OnMotion(self, event, dc, scaling): -## if not event.Dragging(): -## pos = event.GetLogicalPosition(dc) -## for input in self.Inputs: -## rect = input.GetRedrawRect() -## if rect.InsideXY(pos.x, pos.y): -## print "Find input" -## tip = wx.TipWindow(self.Parent, "Test") -## tip.SetBoundingRect(rect) -## return Graphic_Element.OnMotion(self, event, dc, scaling) - + +# # Method called when a Motion event have been generated +# def OnMotion(self, event, dc, scaling): +# if not event.Dragging(): +# pos = event.GetLogicalPosition(dc) +# for input in self.Inputs: +# rect = input.GetRedrawRect() +# if rect.InsideXY(pos.x, pos.y): +# print "Find input" +# tip = wx.TipWindow(self.Parent, "Test") +# tip.SetBoundingRect(rect) +# return Graphic_Element.OnMotion(self, event, dc, scaling) + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the block properties self.Parent.EditBlockContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -406,7 +411,7 @@ self.Parent.PopupBlockMenu(connector) else: self.Parent.PopupBlockMenu() - + # Refreshes the block model def RefreshModel(self, move=True): self.Parent.RefreshBlockModel(self) @@ -414,12 +419,12 @@ if move: for output in self.Outputs: output.RefreshWires() - + def GetToolTipValue(self): return self.Description - + # Adds an highlight to the block - def AddHighlight(self, infos, start, end ,highlight_type): + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] in ["type", "name"] and start[0] == 0 and end[0] == 0: highlights = self.Highlights.setdefault(infos[0], []) AddHighlight(highlights, (start, end, highlight_type)) @@ -427,7 +432,7 @@ self.Inputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type) elif infos[0] == "output" and infos[1] < len(self.Outputs): self.Outputs[infos[1]].AddHighlight(infos[2:], start, end, highlight_type) - + # Removes an highlight from the block def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] in ["type", "name"]: @@ -438,7 +443,7 @@ self.Inputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type) elif infos[0] == "output" and infos[1] < len(self.Outputs): self.Outputs[infos[1]].RemoveHighlight(infos[2:], start, end, highlight_type) - + # Removes all the highlights of one particular type from the block def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -453,14 +458,14 @@ input.ClearHighlights(highlight_type) for output in self.Outputs: output.ClearHighlights(highlight_type) - + # Draws block def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(self.Pen) dc.SetBrush(wx.WHITE_BRUSH) dc.SetTextForeground(self.Colour) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) type_size = dc.GetTextExtent(self.Type) @@ -469,7 +474,7 @@ name_size = self.NameSize type_size = self.TypeSize executionorder_size = self.ExecutionOrderSize - + # Draw a rectangle with the block size dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) # Draw block name and block type @@ -487,25 +492,25 @@ if self.ExecutionOrder != 0: # Draw block execution order dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0], - self.Pos.y + self.Size[1] + 2) - + self.Pos.y + self.Size[1] + 2) + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights.get("name", []), name_pos[0], name_pos[1]) DrawHighlightedText(dc, self.Type, self.Highlights.get("type", []), type_pos[0], type_pos[1]) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Function Block Diagram Variable -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a variable -""" +# ------------------------------------------------------------------------------- + class FBD_Variable(Graphic_Element): + """ + Class that implements the graphic representation of a variable + """ # Create a new variable - def __init__(self, parent, type, name, value_type, id = None, executionOrder = 0): + def __init__(self, parent, type, name, value_type, id=None, executionOrder=0): Graphic_Element.__init__(self, parent) self.Type = None self.ValueType = None @@ -516,9 +521,9 @@ self.Output = None self.SetType(type, value_type) self.Highlights = [] - + # Make a clone of this FBD_Variable - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): variable = FBD_Variable(parent, self.Type, self.Name, self.ValueType, id) variable.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -530,7 +535,7 @@ if self.Output: variable.Output = self.Output.Clone(variable) return variable - + def GetConnectorTranslation(self, element): connectors = {} if self.Input is not None: @@ -538,7 +543,7 @@ if self.Output is not None: connectors[self.Output] = element.Output return connectors - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -546,9 +551,9 @@ if self.Output is not None: self.Output.Flush() self.Output = None - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: if self.Input and self.Input.IsConnected(): @@ -556,26 +561,26 @@ if self.Output and self.Output.IsConnected(): rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey)) return rect - + # Unconnect connector def Clean(self): if self.Input: - self.Input.UnConnect(delete = True) + self.Input.UnConnect(delete=True) if self.Output: - self.Output.UnConnect(delete = True) - + self.Output.UnConnect(delete=True) + # Delete this variable by calling the appropriate method def Delete(self): self.Parent.DeleteVariable(self) - + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Refresh the size of text for execution order def RefreshExecutionOrderSize(self): self.ExecutionOrderSize = self.Parent.GetTextExtent(str(self.ExecutionOrder)) - + # Refresh the variable bounding box def RefreshBoundingBox(self): if self.Type in (OUTPUT, INOUT): @@ -594,7 +599,7 @@ bbx_width = max(bbx_width, bbx_width + self.Pos.x + self.ExecutionOrderSize[0] - bbx_x - self.Size[0]) bbx_height = bbx_height + (self.ExecutionOrderSize[1] + 2) self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width + 1, bbx_height + 1) - + # Refresh the position of the variable connector def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -607,28 +612,28 @@ if self.Output: self.Output.SetPosition(wx.Point(self.Size[0], position)) self.RefreshConnected() - + # Refresh the position of wires connected to connector - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): if self.Input: self.Input.MoveConnected(exclude) if self.Output: self.Output.MoveConnected(exclude) - + # Test if point given is on the variable connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): if self.Input and self.Input.TestPoint(pt, direction, exclude): return self.Input if self.Output and self.Output.TestPoint(pt, direction, exclude): return self.Output return None - - # Returns the block connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the block connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector if they exists - #if self.Input and name == self.Input.GetName(): + # if self.Input and name == self.Input.GetName(): # return self.Input if self.Output and name == self.Output.GetName(): return self.Output @@ -640,8 +645,8 @@ if self.Output: connectors.append(self.Output) return self.FindNearestConnector(position, connectors) - - # Returns all the block connectors + + # Returns all the block connectors def GetConnectors(self): connectors = {"inputs": [], "outputs": []} if self.Input: @@ -649,14 +654,14 @@ if self.Output: connectors["outputs"].append(self.Output) return connectors - + # Changes the negated property of the variable connector if handled def SetConnectorNegated(self, negated): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.SetNegated(negated) self.RefreshModel(False) - + # Changes the variable type def SetType(self, type, value_type): if type != self.Type: @@ -664,15 +669,15 @@ # Create an input or output connector according to variable type if self.Type != INPUT: if self.Input is None: - self.Input = Connector(self, "", value_type, wx.Point(0, 0), WEST, onlyone = True) + self.Input = Connector(self, "", value_type, wx.Point(0, 0), WEST, onlyone=True) elif self.Input: - self.Input.UnConnect(delete = True) + self.Input.UnConnect(delete=True) self.Input = None if self.Type != OUTPUT: if self.Output is None: self.Output = Connector(self, "", value_type, wx.Point(0, 0), EAST) elif self.Output: - self.Output.UnConnect(delete = True) + self.Output.UnConnect(delete=True) self.Output = None self.RefreshConnectors() self.RefreshBoundingBox() @@ -680,38 +685,38 @@ if self.Input: self.Input.SetType(value_type) if self.Output: - self.Output.SetType(value_type) - + self.Output.SetType(value_type) + # Returns the variable type def GetType(self): return self.Type - + # Returns the variable value type def GetValueType(self): return self.ValueType - + # Changes the variable name def SetName(self, name): self.Name = name self.RefreshNameSize() - + # Returns the variable name def GetName(self): return self.Name - + # Changes the execution order def SetExecutionOrder(self, executionOrder): self.ExecutionOrder = executionOrder self.RefreshExecutionOrderSize() - + # Returs the execution order def GetExecutionOrder(self): return self.ExecutionOrder - + # Returns the variable minimum size def GetMinSize(self): return self.NameSize[0] + 10, self.NameSize[1] + 10 - + # Set size of the variable to the minimum size def SetBestSize(self, scaling): if self.Type == INPUT: @@ -720,22 +725,22 @@ return Graphic_Element.SetBestSize(self, scaling, x_factor=0.) else: return Graphic_Element.SetBestSize(self, scaling) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): if event.ControlDown(): - # Change variable type + # Change variable type types = [INPUT, OUTPUT, INOUT] - self.Parent.ChangeVariableType(self, - types[(types.index(self.Type) + 1) % len(types)]) + self.Parent.ChangeVariableType( + self, types[(types.index(self.Type) + 1) % len(types)]) else: # Edit the variable properties self.Parent.EditVariableContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): self.Parent.PopupVariableMenu() - + # Refreshes the variable model def RefreshModel(self, move=True): self.Parent.RefreshVariableModel(self) @@ -744,35 +749,35 @@ if move and self.Type != OUTPUT: if self.Output: self.Output.RefreshWires() - + # Adds an highlight to the variable def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "expression" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the variable def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "expression": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the variable def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws variable def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) executionorder_size = dc.GetTextExtent(str(self.ExecutionOrder)) else: name_size = self.NameSize executionorder_size = self.ExecutionOrderSize - - text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, + + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y + (self.Size[1] - name_size[1]) / 2) # Draw a rectangle with the variable size dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) @@ -781,27 +786,28 @@ # Draw connectors if self.Input: self.Input.Draw(dc) - if self.Output: + if self.Output: self.Output.Draw(dc) if self.ExecutionOrder != 0: # Draw variable execution order dc.DrawText(str(self.ExecutionOrder), self.Pos.x + self.Size[0] - executionorder_size[0], - self.Pos.y + self.Size[1] + 2) + self.Pos.y + self.Size[1] + 2) if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1]) - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Function Block Diagram Connector -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a connection -""" +# ------------------------------------------------------------------------------- + class FBD_Connector(Graphic_Element): + """ + Class that implements the graphic representation of a connection + """ # Create a new connection - def __init__(self, parent, type, name, id = None): + def __init__(self, parent, type, name, id=None): Graphic_Element.__init__(self, parent) self.Type = type self.Id = id @@ -811,27 +817,27 @@ self.Highlights = [] # Create an input or output connector according to connection type if self.Type == CONNECTOR: - self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone = True) + self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone=True) else: self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST) self.RefreshConnectors() self.RefreshNameSize() - + def Flush(self): if self.Connector: self.Connector.Flush() self.Connector = None - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: if self.Connector and self.Connector.IsConnected(): rect = rect.Union(self.Connector.GetConnectedRedrawRect(movex, movey)) return rect - + # Make a clone of this FBD_Connector - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): connection = FBD_Connector(parent, self.Type, self.Name, id) connection.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -840,23 +846,23 @@ connection.SetPosition(self.Pos.x, self.Pos.y) connection.Connector = self.Connector.Clone(connection) return connection - + def GetConnectorTranslation(self, element): - return {self.Connector : element.Connector} + return {self.Connector: element.Connector} # Unconnect connector def Clean(self): if self.Connector: - self.Connector.UnConnect(delete = True) - + self.Connector.UnConnect(delete=True) + # Delete this connection by calling the appropriate method def Delete(self): self.Parent.DeleteConnection(self) - + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Refresh the connection bounding box def RefreshBoundingBox(self): if self.Type == CONNECTOR: @@ -865,7 +871,7 @@ bbx_x = self.Pos.x bbx_width = self.Size[0] + CONNECTOR_SIZE self.BoundingBox = wx.Rect(bbx_x, self.Pos.y, bbx_width, self.Size[1]) - + # Refresh the position of the connection connector def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -878,23 +884,23 @@ else: self.Connector.SetPosition(wx.Point(self.Size[0], position)) self.RefreshConnected() - + # Refresh the position of wires connected to connector - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): if self.Connector: self.Connector.MoveConnected(exclude) - + # Test if point given is on the connection connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): if self.Connector and self.Connector.TestPoint(pt, direction, exclude): return self.Connector return None - + # Returns the connection connector - def GetConnector(self, position = None, name = None): + def GetConnector(self, position=None, name=None): return self.Connector - - # Returns all the block connectors + + # Returns all the block connectors def GetConnectors(self): connectors = {"inputs": [], "outputs": []} if self.Type == CONNECTOR: @@ -918,50 +924,50 @@ self.Clean() # Create an input or output connector according to connection type if self.Type == CONNECTOR: - self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone = True) + self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), WEST, onlyone=True) else: self.Connector = Connector(self, "", "ANY", wx.Point(0, 0), EAST) self.RefreshConnectors() self.RefreshBoundingBox() - + # Returns the connection type def GetType(self): return self.Type - + def GetConnectionResultType(self, connector, connectortype): if self.Type == CONTINUATION: connector = self.Parent.GetConnectorByName(self.Name) if connector is not None: return connector.Connector.GetConnectedType() return connectortype - + # Changes the connection name def SetName(self, name): self.Name = name self.RefreshNameSize() - + # Returns the connection name def GetName(self): return self.Name - + # Set size of the variable to the minimum size def SetBestSize(self, scaling): if self.Type == CONTINUATION: return Graphic_Element.SetBestSize(self, scaling, x_factor=1.) else: return Graphic_Element.SetBestSize(self, scaling, x_factor=0.) - + # Returns the connection minimum size def GetMinSize(self): text_width, text_height = self.NameSize if text_height % 2 == 1: text_height += 1 return text_width + text_height + 20, text_height + 10 - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): if event.ControlDown(): - # Change connection type + # Change connection type if self.Type == CONNECTOR: self.Parent.ChangeConnectionType(self, CONTINUATION) else: @@ -969,12 +975,12 @@ else: # Edit the connection properties self.Parent.EditConnectionContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupConnectionMenu() - + # Refreshes the connection model def RefreshModel(self, move=True): self.Parent.RefreshConnectionModel(self) @@ -983,51 +989,50 @@ if move and self.Type == CONTINUATION: if self.Connector: self.Connector.RefreshWires() - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "name" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "name": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws connection def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) else: name_size = self.NameSize - + # Draw a rectangle with the connection size with arrows inside dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) arrowsize = min(self.Size[1] / 2, (self.Size[0] - name_size[0] - 10) / 2) - dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize, - self.Pos.y + self.Size[1] / 2) - dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2, - self.Pos.x, self.Pos.y + self.Size[1]) - dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y, - self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2) - dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2, - self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x, self.Pos.y, self.Pos.x + arrowsize, + self.Pos.y + self.Size[1] / 2) + dc.DrawLine(self.Pos.x + arrowsize, self.Pos.y + self.Size[1] / 2, + self.Pos.x, self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x + self.Size[0] - arrowsize, self.Pos.y, + self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2) + dc.DrawLine(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1] / 2, + self.Pos.x + self.Size[0] - arrowsize, self.Pos.y + self.Size[1]) # Draw connection name - text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, + text_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y + (self.Size[1] - name_size[1]) / 2) dc.DrawText(self.Name, text_pos[0], text_pos[1]) # Draw connector if self.Connector: self.Connector.Draw(dc) - + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights, text_pos[0], text_pos[1]) - diff -r c1298e7ffe3a -r 8391c11477f4 graphics/GraphicCommons.py --- a/graphics/GraphicCommons.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/GraphicCommons.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,22 +22,20 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx + +from __future__ import absolute_import from math import * from types import * -import datetime -from threading import Lock,Timer - + +import wx from graphics.ToolTipProducer import ToolTipProducer from graphics.DebugDataConsumer import DebugDataConsumer -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Common constants -#------------------------------------------------------------------------------- - -""" -Definition of constants for dimensions of graphic elements -""" +# +# Definition of constants for dimensions of graphic elements +# ------------------------------------------------------------------------------- # FBD and SFC constants MIN_MOVE = 5 # Minimum move before starting a element dragging @@ -78,17 +76,17 @@ [HANDLE_MOVE, HANDLE_RESIZE, HANDLE_POINT, HANDLE_SEGMENT, HANDLE_CONNECTOR] = range(5) # List of value for resize handle that are valid -VALID_HANDLES = [(1,1), (1,2), (1,3), (2,3), (3,3), (3,2), (3,1), (2,1)] +VALID_HANDLES = [(1, 1), (1, 2), (1, 3), (2, 3), (3, 3), (3, 2), (3, 1), (2, 1)] # Contants for defining the direction of a connector -[EAST, NORTH, WEST, SOUTH] = [(1,0), (0,-1), (-1,0), (0,1)] - -# Contants for defining which mode is selected for each view -[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, - MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, +[EAST, NORTH, WEST, SOUTH] = [(1, 0), (0, -1), (-1, 0), (0, 1)] + +# Contants for defining which mode is selected for each view +[MODE_SELECTION, MODE_BLOCK, MODE_VARIABLE, MODE_CONNECTION, MODE_COMMENT, + MODE_COIL, MODE_CONTACT, MODE_POWERRAIL, MODE_INITIALSTEP, MODE_STEP, MODE_TRANSITION, MODE_DIVERGENCE, MODE_JUMP, MODE_ACTION, MODE_MOTION] = range(15) -# Contants for defining alignment types for graphic group +# Contants for defining alignment types for graphic group [ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, ALIGN_TOP, ALIGN_MIDDLE, ALIGN_BOTTOM] = range(6) # Contants for defining which drawing mode is selected for app @@ -105,16 +103,17 @@ REFRESH_HIGHLIGHT_PERIOD = 0.1 HANDLE_CURSORS = { - (1, 1) : 2, - (3, 3) : 2, - (1, 3) : 3, - (3, 1) : 3, - (1, 2) : 4, - (3, 2) : 4, - (2, 1) : 5, - (2, 3) : 5 + (1, 1): 2, + (3, 3): 2, + (1, 3): 3, + (3, 1): 3, + (1, 2): 4, + (3, 2): 4, + (2, 1): 5, + (2, 3): 5 } + def round_scaling(x, n, constraint=0): fraction = float(x) / float(n) if constraint == -1: @@ -122,26 +121,35 @@ else: xround = round(fraction) if constraint == 1 and xround < fraction: - xround += 1 + xround += 1 return int(xround * n) -""" -Basic vector operations for calculate wire points -""" - -# Create a vector from two points and define if vector must be normal -def vector(p1, p2, normal = True): + +# ------------------------------------------------------------------------------- +# Basic vector operations for calculate wire points +# ------------------------------------------------------------------------------- + +def vector(p1, p2, normal=True): + """ + Create a vector from two points and define if vector must be normal + """ vector = (p2.x - p1.x, p2.y - p1.y) if normal: return normalize(vector) return vector -# Calculate the norm of a given vector + def norm(v): + """ + Calculate the norm of a given vector + """ return sqrt(v[0] * v[0] + v[1] * v[1]) -# Normalize a given vector + def normalize(v): + """ + Normalize a given vector + """ v_norm = norm(v) # Verifie if it is not a null vector if v_norm > 0: @@ -149,24 +157,32 @@ else: return v -# Calculate the scalar product of two vectors + def is_null_vector(v): + """ + Calculate the scalar product of two vectors + """ return v == (0, 0) -# Calculate the scalar product of two vectors + def add_vectors(v1, v2): + """ + Calculate the scalar product of two vectors + """ return (v1[0] + v2[0], v1[1] + v2[1]) -# Calculate the scalar product of two vectors + def product(v1, v2): + """ + Calculate the scalar product of two vectors + """ return v1[0] * v2[0] + v1[1] * v2[1] -""" -Function that calculates the nearest point of the grid defined by scaling for the given point -""" - def GetScaledEventPosition(event, dc, scaling): + """ + Function that calculates the nearest point of the grid defined by scaling for the given point + """ pos = event.GetLogicalPosition(dc) if scaling: pos.x = round(float(pos.x) / float(scaling[0])) * scaling[0] @@ -174,11 +190,10 @@ return pos -""" -Function that choose a direction during the wire points generation -""" - def DirectionChoice(v_base, v_target, dir_target): + """ + Function that choose a direction during the wire points generation + """ dir_product = product(v_base, v_target) if dir_product < 0: return (-v_base[0], -v_base[1]) @@ -186,37 +201,43 @@ return dir_target return v_base + def MiterPen(colour, width=1, style=wx.SOLID): pen = wx.Pen(colour, width, style) pen.SetJoin(wx.JOIN_MITER) pen.SetCap(wx.CAP_PROJECTING) return pen -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Helpers for highlighting text -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def AddHighlight(highlights, infos): RemoveHighlight(highlights, infos) highlights.append(infos) + def RemoveHighlight(highlights, infos): if infos in highlights: highlights.remove(infos) return True return False -def ClearHighlight(highlights, highlight_type=None): + +def ClearHighlights(highlights, highlight_type=None): if highlight_type is not None: return [highlight for highlight in highlights if highlight[2] != highlight_type] return [] + def DrawHighlightedText(dc, text, highlights, x, y): current_pen = dc.GetPen() dc.SetPen(wx.TRANSPARENT_PEN) for start, end, highlight_type in highlights: dc.SetBrush(wx.Brush(highlight_type[0])) - offset_width, offset_height = dc.GetTextExtent(text[:start[1]]) + offset_width, _offset_height = dc.GetTextExtent(text[:start[1]]) part = text[start[1]:end[1] + 1] part_width, part_height = dc.GetTextExtent(part) dc.DrawRectangle(x + offset_width, y, part_width, part_height) @@ -224,26 +245,27 @@ dc.DrawText(part, x + offset_width, y) dc.SetPen(current_pen) dc.SetTextForeground(wx.BLACK) - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Graphic element base class -#------------------------------------------------------------------------------- - -""" -Class that implements a generic graphic element -""" +# ------------------------------------------------------------------------------- + class Graphic_Element(ToolTipProducer): - + """ + Class that implements a generic graphic element + """ + # Create a new graphic element - def __init__(self, parent, id = None): + def __init__(self, parent, id=None): ToolTipProducer.__init__(self, parent) self.Parent = parent self.Id = id self.oldPos = None self.StartPos = None self.CurrentDrag = None - self.Handle = (None,None) + self.Handle = (None, None) self.Dragging = False self.Selected = False self.Highlighted = False @@ -251,22 +273,22 @@ self.Size = wx.Size(0, 0) self.BoundingBox = wx.Rect(0, 0, 0, 0) self.Visible = False - + def GetDefinition(self): return [self.Id], [] - + def TestVisible(self, screen): self.Visible = self.Selected or self.GetRedrawRect().Intersects(screen) - + def IsVisible(self): return self.Visible - + def SpreadCurrent(self): pass - + def GetConnectorTranslation(self, element): return {} - + def FindNearestConnector(self, position, connectors): distances = [] for connector in connectors: @@ -278,20 +300,20 @@ if len(distances) > 0: return distances[0][1] return None - + def IsOfType(self, type, reference): return self.Parent.IsOfType(type, reference) - + def IsEndType(self, type): return self.Parent.IsEndType(type) - + def GetDragging(self): return self.Dragging - + # Make a clone of this element def Clone(self, parent): return Graphic_Element(parent, self.Id) - + # Changes the block position def SetPosition(self, x, y): self.Pos.x = x @@ -302,7 +324,7 @@ # Returns the block position def GetPosition(self): return self.Pos.x, self.Pos.y - + # Changes the element size def SetSize(self, width, height): self.Size.SetWidth(width) @@ -313,11 +335,11 @@ # Returns the element size def GetSize(self): return self.Size.GetWidth(), self.Size.GetHeight() - + # Returns the minimum element size def GetMinSize(self): return 0, 0 - + # Set size of the element to the minimum size def SetBestSize(self, scaling, x_factor=0.5, y_factor=0.5): width, height = self.GetSize() @@ -336,31 +358,27 @@ height = round_scaling(height, scaling[1], 1) self.SetSize(width, height) return self.Pos.x - posx, self.Pos.y - posy - - # Refresh the element Bounding Box - def RefreshBoundingBox(self): - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]) - + # Refresh the element connectors position def RefreshConnectors(self): pass - + # Refresh the position of wires connected to element inputs and outputs - def RefreshConnected(self): + def RefreshConnected(self, exclude=None): pass - + # Change the parent def SetParent(self, parent): self.Parent = parent - + # Override this method for defining the method to call for deleting this element def Delete(self): pass - + # Returns the Id def GetId(self): return self.Id - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if connectors: @@ -368,21 +386,21 @@ else: rect = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0], self.Size[1]) return rect.InsideXY(pt.x, pt.y) - + # Returns if the point given is in the bounding box def IsInSelection(self, rect): return rect.InsideXY(self.BoundingBox.x, self.BoundingBox.y) and rect.InsideXY(self.BoundingBox.x + self.BoundingBox.width, self.BoundingBox.y + self.BoundingBox.height) - + # Override this method for refreshing the bounding box def RefreshBoundingBox(self): pass - + # Returns the bounding box def GetBoundingBox(self): return self.BoundingBox - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): scalex, scaley = self.Parent.GetViewScale() rect = wx.Rect() rect.x = self.BoundingBox.x - int(HANDLE_SIZE / scalex) - 3 - abs(movex) @@ -390,42 +408,42 @@ rect.width = self.BoundingBox.width + 2 * (int(HANDLE_SIZE / scalex) + abs(movex) + 1) + 4 rect.height = self.BoundingBox.height + 2 * (int(HANDLE_SIZE / scaley) + abs(movey) + 1) + 4 return rect - - def Refresh(self, rect = None): + + def Refresh(self, rect=None): if self.Visible: if rect is not None: self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False) else: self.Parent.RefreshRect(self.Parent.GetScrolledRect(self.GetRedrawRect()), False) - + # Change the variable that indicates if this element is selected def SetSelected(self, selected): self.Selected = selected self.Refresh() - + # Change the variable that indicates if this element is highlighted def SetHighlighted(self, highlighted): self.Highlighted = highlighted self.Refresh() - + # Test if the point is on a handle of this element def TestHandle(self, event): dc = self.Parent.GetLogicalDC() scalex, scaley = dc.GetUserScale() pos = event.GetPosition() pt = wx.Point(*self.Parent.CalcUnscrolledPosition(pos.x, pos.y)) - + left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2 right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex - + top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2 bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley - + extern_rect = wx.Rect(left, top, right + HANDLE_SIZE - left, bottom + HANDLE_SIZE - top) intern_rect = wx.Rect(left + HANDLE_SIZE, top + HANDLE_SIZE, right - left - HANDLE_SIZE, bottom - top - HANDLE_SIZE) - + # Verify that this element is selected if self.Selected and extern_rect.InsideXY(pt.x, pt.y) and not intern_rect.InsideXY(pt.x, pt.y): # Find if point is on a handle horizontally @@ -450,10 +468,9 @@ if (handle_x, handle_y) in VALID_HANDLES: return handle_x, handle_y return 0, 0 - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): - pos = event.GetLogicalPosition(dc) # Test if an handle have been clicked handle = self.TestHandle(event) # Find which type of handle have been clicked, @@ -469,7 +486,7 @@ self.oldPos = GetScaledEventPosition(event, dc, scaling) self.StartPos = wx.Point(self.Pos.x, self.Pos.y) self.CurrentDrag = wx.Point(0, 0) - + # Method called when a LeftUp event have been generated def OnLeftUp(self, event, dc, scaling): # If a dragging have been initiated @@ -498,7 +515,7 @@ # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): pass - + # Method called when a Motion event have been generated def OnMotion(self, event, dc, scaling): # If the cursor is dragging and the element have been clicked @@ -532,19 +549,19 @@ return 0, 0 # Moves the element - def Move(self, dx, dy, exclude = []): + def Move(self, dx, dy, exclude=None): self.Pos.x += max(-self.BoundingBox.x, dx) self.Pos.y += max(-self.BoundingBox.y, dy) self.RefreshConnected(exclude) self.RefreshBoundingBox() - + # Resizes the element from position and size given def Resize(self, x, y, width, height): self.Move(x, y) self.SetSize(width, height) - + # Refreshes the element state according to move defined and handle selected - def ProcessDragging(self, movex, movey, event, scaling, width_fac = 1, height_fac = 1): + def ProcessDragging(self, movex, movey, event, scaling, width_fac=1, height_fac=1): handle_type, handle = self.Handle # If it is a resize handle, calculate the values from resizing if handle_type == HANDLE_RESIZE: @@ -618,27 +635,27 @@ movey = self.StartPos.y - self.Pos.y else: movex = self.StartPos.x - self.Pos.x - movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y + movey = self.StartPos.y + self.CurrentDrag.y - self.Pos.y self.Move(movex, movey) return movex, movey return 0, 0 - + # Override this method for defining the method to call for adding an highlight to this element def AddHighlight(self, infos, start, end, highlight_type): pass - + # Override this method for defining the method to call for removing an highlight from this element def RemoveHighlight(self, infos, start, end, highlight_type): pass - + # Override this method for defining the method to call for removing all the highlights of one particular type from this element def ClearHighlight(self, highlight_type=None): pass - + # Override this method for defining the method to call for refreshing the model of this element def RefreshModel(self, move=True): pass - + # Draws the highlightment of this element if it is highlighted (can be overwritten) def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -646,13 +663,13 @@ dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) - dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, - int(round((self.Pos.y - 1) * scaley)) - 2, - int(round((self.Size.width + 3) * scalex)) + 5, + dc.DrawRectangle(int(round((self.Pos.x - 1) * scalex)) - 2, + int(round((self.Pos.y - 1) * scaley)) - 2, + int(round((self.Size.width + 3) * scalex)) + 5, int(round((self.Size.height + 3) * scaley)) + 5) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws the handles of this element if it is selected def Draw(self, dc): if not getattr(dc, "printing", False): @@ -663,55 +680,55 @@ dc.SetUserScale(1, 1) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + left = (self.BoundingBox.x - 2) * scalex - HANDLE_SIZE center = (self.BoundingBox.x + self.BoundingBox.width / 2) * scalex - HANDLE_SIZE / 2 right = (self.BoundingBox.x + self.BoundingBox.width + 2) * scalex - + top = (self.BoundingBox.y - 2) * scaley - HANDLE_SIZE middle = (self.BoundingBox.y + self.BoundingBox.height / 2) * scaley - HANDLE_SIZE / 2 bottom = (self.BoundingBox.y + self.BoundingBox.height + 2) * scaley - + for x, y in [(left, top), (center, top), (right, top), (left, middle), (right, middle), (left, bottom), (center, bottom), (right, bottom)]: dc.DrawRectangle(x, y, HANDLE_SIZE, HANDLE_SIZE) - + dc.SetUserScale(scalex, scaley) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Group of graphic elements -#------------------------------------------------------------------------------- - -""" -Class that implements a group of graphic elements -""" +# ------------------------------------------------------------------------------- + class Graphic_Group(Graphic_Element): - + """ + Class that implements a group of graphic elements + """ + # Create a new group of graphic elements def __init__(self, parent): Graphic_Element.__init__(self, parent) self.Elements = [] self.RefreshWireExclusion() self.RefreshBoundingBox() - + # Destructor def __del__(self): self.Elements = [] - + def GetDefinition(self): - blocks = [] + blocks = [] wires = [] for element in self.Elements: block, wire = element.GetDefinition() blocks.extend(block) wires.extend(wire) return blocks, wires - + # Make a clone of this element - def Clone(self, parent, pos = None): + def Clone(self, parent, pos=None): group = Graphic_Group(parent) connectors = {} exclude_names = {} @@ -729,9 +746,9 @@ if parent.IsNamedElement(element): name = parent.GenerateNewName(element, exclude_names) exclude_names[name.upper()] = True - new_element = element.Clone(parent, newid, name, pos = new_pos) + new_element = element.Clone(parent, newid, name, pos=new_pos) else: - new_element = element.Clone(parent, newid, pos = new_pos) + new_element = element.Clone(parent, newid, pos=new_pos) new_element.SetBestSize(parent.Scaling) else: new_element = element.Clone(parent) @@ -751,20 +768,20 @@ if not isinstance(element, Wire): parent.AddBlockInModel(element) return group - + def CanAddBlocks(self, parent): valid = True for element in self.Elements: if not isinstance(element, Wire): valid &= parent.CanAddElement(element) return valid - + def IsVisible(self): for element in self.Elements: if element.IsVisible(): return True return False - + # Refresh the list of wire excluded def RefreshWireExclusion(self): self.WireExcluded = [] @@ -774,9 +791,9 @@ endblock = element.EndConnected.GetParentBlock() if startblock in self.Elements and endblock in self.Elements: self.WireExcluded.append(element) - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = None for element in self.Elements: if rect is None: @@ -784,41 +801,41 @@ else: rect = rect.Union(element.GetRedrawRect(movex, movey)) return rect - + # Clean this group of elements def Clean(self): # Clean all the elements of the group for element in self.Elements: element.Clean() - + # Delete this group of elements def Delete(self): # Delete all the elements of the group for element in self.Elements: element.Delete() self.WireExcluded = [] - + # Returns if the point given is in the bounding box of one of the elements of this group def HitTest(self, pt, connectors=True): result = False for element in self.Elements: result |= element.HitTest(pt, connectors) return result - + # Returns if the element given is in this group def IsElementIn(self, element): return element in self.Elements - + # Change the elements of the group def SetElements(self, elements): self.Elements = elements self.RefreshWireExclusion() self.RefreshBoundingBox() - + # Returns the elements of the group def GetElements(self): return self.Elements - + # Align the group elements def AlignElements(self, horizontally, vertically): minx = self.BoundingBox.x + self.BoundingBox.width @@ -854,11 +871,11 @@ element.Move(movex, movey) element.RefreshModel() self.RefreshBoundingBox() - + # Add the given element to the group of elements def AddElement(self, element): self.Elements.append(element) - + # Remove or select the given element if it is or not in the group def SelectElement(self, element): if element in self.Elements: @@ -867,7 +884,7 @@ self.Elements.append(element) self.RefreshWireExclusion() self.RefreshBoundingBox() - + # Move this group of elements def Move(self, movex, movey): movex = max(-self.BoundingBox.x, movex) @@ -879,7 +896,7 @@ elif element in self.WireExcluded: element.Move(movex, movey, True) self.RefreshBoundingBox() - + # Refreshes the bounding box of this group of elements def RefreshBoundingBox(self): if len(self.Elements) > 0: @@ -900,9 +917,9 @@ self.Size = wx.Size(self.BoundingBox.width, self.BoundingBox.height) # Forbids to change the group position - def SetPosition(x, y): + def SetPosition(self, x, y): pass - + # Returns the position of this group def GetPosition(self, exclude_wires=False): if exclude_wires: @@ -919,15 +936,15 @@ return 0, 0 return posx, posy return self.BoundingBox.x, self.BoundingBox.y - + # Forbids to change the group size - def SetSize(width, height): + def SetSize(self, width, height): pass - + # Returns the size of this group def GetSize(self): return self.BoundingBox.width, self.BoundingBox.height - + # Set size of the group elements to their minimum size def SetBestSize(self, scaling): max_movex = max_movey = 0 @@ -936,10 +953,10 @@ max_movex = max(max_movex, movex) max_movey = max(max_movey, movey) return max_movex, max_movey - + # Refreshes the group elements to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): - handle_type, handle = self.Handle + handle_type, _handle = self.Handle # If it is a move handle, Move this group elements if handle_type == HANDLE_MOVE: movex = max(-self.BoundingBox.x, movex) @@ -960,17 +977,17 @@ self.Move(movex, movey) return movex, movey return 0, 0 - + # Change the variable that indicates if this element is highlighted def SetHighlighted(self, highlighted): for element in self.Elements: element.SetHighlighted(highlighted) - + def HighlightPoint(self, pos): for element in self.Elements: if isinstance(element, Wire): element.HighlightPoint(pos) - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): Graphic_Element.OnLeftDown(self, event, dc, scaling) @@ -998,18 +1015,19 @@ for element in self.Elements: element.Draw(dc) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Connector for all types of blocks -#------------------------------------------------------------------------------- - -""" -Class that implements a connector for any type of block -""" +# ------------------------------------------------------------------------------- + class Connector(DebugDataConsumer, ToolTipProducer): - + """ + Class that implements a connector for any type of block + """ + # Create a new connector - def __init__(self, parent, name, type, position, direction, negated = False, edge = "none", onlyone = False): + def __init__(self, parent, name, type, position, direction, negated=False, edge="none", onlyone=False): DebugDataConsumer.__init__(self) ToolTipProducer.__init__(self, parent.Parent) self.ParentBlock = parent @@ -1033,15 +1051,15 @@ self.Selected = False self.Highlights = [] self.RefreshNameSize() - + def Flush(self): self.ParentBlock = None - for wire, handle in self.Wires: + for wire, _handle in self.Wires: wire.Flush() self.Wires = [] - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): parent_pos = self.ParentBlock.GetPosition() x = min(parent_pos[0] + self.Pos.x, parent_pos[0] + self.Pos.x + self.Direction[0] * CONNECTOR_SIZE) y = min(parent_pos[1] + self.Pos.y, parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE) @@ -1060,43 +1078,44 @@ if self.Edge == "rising" and self.Direction[1] == 1: y -= 5 height += 5 - rect = wx.Rect(x - abs(movex), y - abs(movey), width + 2 * abs(movex), height + 2 * abs(movey)) + rect = wx.Rect(x - abs(movex), y - abs(movey), width + 2 * abs(movex), height + 2 * abs(movey)) if self.ValueSize is None and isinstance(self.ComputedValue, (StringType, UnicodeType)): self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize - rect = rect.Union(wx.Rect( - parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + \ - width * (self.Direction[0] - 1) / 2, - parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + \ - height * (self.Direction[1] - 1), + rect = rect.Union( + wx.Rect( + parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + + width * (self.Direction[0] - 1) / 2, + parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + + height * (self.Direction[1] - 1), width, height)) return rect - + # Change the connector selection def SetSelected(self, selected): self.Selected = selected - + # Make a clone of the connector - def Clone(self, parent = None): + def Clone(self, parent=None): if parent is None: parent = self.ParentBlock return Connector(parent, self.Name, self.Type, wx.Point(self.Pos[0], self.Pos[1]), - self.Direction, self.Negated) - + self.Direction, self.Negated) + # Returns the connector parent block def GetParentBlock(self): return self.ParentBlock - + # Returns the connector type - def GetType(self, raw = False): + def GetType(self, raw=False): if self.ParentBlock.IsEndType(self.Type) or raw: return self.Type elif (self.Negated or self.Edge != "none") and self.ParentBlock.IsOfType("BOOL", self.Type): return "BOOL" else: return self.ParentBlock.GetConnectionResultType(self, self.Type) - + # Returns the connector type def GetConnectedType(self): if self.ParentBlock.IsEndType(self.Type): @@ -1104,32 +1123,32 @@ elif len(self.Wires) == 1: return self.Wires[0][0].GetOtherConnectedType(self.Wires[0][1]) return self.Type - + # Returns the connector type def GetConnectedRedrawRect(self, movex, movey): rect = None - for wire, handle in self.Wires: + for wire, _handle in self.Wires: if rect is None: rect = wire.GetRedrawRect() else: rect = rect.Union(wire.GetRedrawRect()) return rect - + # Returns if connector type is compatible with type given def IsCompatible(self, type): reference = self.GetType() return self.ParentBlock.IsOfType(type, reference) or self.ParentBlock.IsOfType(reference, type) - + # Changes the connector name def SetType(self, type): self.Type = type - for wire, handle in self.Wires: + for wire, _handle in self.Wires: wire.SetValid(wire.IsConnectedCompatible()) - + # Returns the connector name def GetName(self): return self.Name - + # Changes the connector name def SetName(self, name): self.Name = name @@ -1140,15 +1159,15 @@ self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def GetComputedValue(self): if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType): return self.Value return None - + def GetToolTipValue(self): return self.GetComputedValue() - + def SetValue(self, value): if self.Value != value: self.Value = value @@ -1161,59 +1180,59 @@ self.ValueSize = None if self.ParentBlock.Visible: self.ParentBlock.Parent.ElementNeedRefresh(self) - + def RefreshForced(self): self.Forced = False - for wire, handle in self.Wires: + for wire, _handle in self.Wires: self.Forced |= wire.IsForced() def RefreshValue(self): self.Value = self.ReceivingCurrent() - + def RefreshValid(self): self.Valid = True - for wire, handle in self.Wires: + for wire, _handle in self.Wires: self.Valid &= wire.GetValid() - + def ReceivingCurrent(self): current = False - for wire, handle in self.Wires: + for wire, _handle in self.Wires: value = wire.GetValue() if current != "undefined" and isinstance(value, BooleanType): current |= wire.GetValue() elif value == "undefined": current = "undefined" return current - + def SpreadCurrent(self, spreading): - for wire, handle in self.Wires: + for wire, _handle in self.Wires: wire.SetValue(spreading) - + # Changes the connector name size def RefreshNameSize(self): if self.Name != "": self.NameSize = self.ParentBlock.Parent.GetTextExtent(self.Name) else: self.NameSize = 0, 0 - + # Returns the connector name size def GetNameSize(self): return self.NameSize - + # Returns the wires connected to the connector def GetWires(self): return self.Wires - + # Returns the parent block Id def GetBlockId(self): return self.ParentBlock.GetId() - + # Returns the connector relative position def GetRelPosition(self): return self.Pos - + # Returns the connector absolute position - def GetPosition(self, size = True): + def GetPosition(self, size=True): parent_pos = self.ParentBlock.GetPosition() # If the position of the end of the connector is asked if size: @@ -1223,25 +1242,25 @@ x = parent_pos[0] + self.Pos.x y = parent_pos[1] + self.Pos.y return wx.Point(x, y) - + # Change the connector relative position def SetPosition(self, pos): self.Pos = pos - + # Returns the connector direction def GetDirection(self): return self.Direction - + # Change the connector direction def SetDirection(self, direction): self.Direction = direction - + # Connect a wire to this connector at the last place - def Connect(self, wire, refresh = True): + def Connect(self, wire, refresh=True): self.InsertConnect(len(self.Wires), wire, refresh) - + # Connect a wire to this connector at the place given - def InsertConnect(self, idx, wire, refresh = True): + def InsertConnect(self, idx, wire, refresh=True): if wire not in self.Wires: self.Wires.insert(idx, wire) if wire[1] == 0: @@ -1250,16 +1269,16 @@ wire[0].ConnectEndPoint(None, self) if refresh: self.ParentBlock.RefreshModel(False) - + # Returns the index of the wire given in the list of connected def GetWireIndex(self, wire): - for i, (tmp_wire, handle) in enumerate(self.Wires): + for i, (tmp_wire, _handle) in enumerate(self.Wires): if tmp_wire == wire: return i return None - + # Unconnect a wire or all wires connected to the connector - def UnConnect(self, wire = None, unconnect = True, delete = False): + def UnConnect(self, wire=None, unconnect=True, delete=False): i = 0 found = False while i < len(self.Wires) and not found: @@ -1281,13 +1300,13 @@ if not delete: self.RefreshValid() self.ParentBlock.RefreshModel(False) - + # Returns if connector has one or more wire connected def IsConnected(self): return len(self.Wires) > 0 - + # Move the wires connected - def MoveConnected(self, exclude = []): + def MoveConnected(self, exclude=None): if len(self.Wires) > 0: # Calculate the new position of the end point parent_pos = self.ParentBlock.GetPosition() @@ -1295,26 +1314,26 @@ y = parent_pos[1] + self.Pos.y + self.Direction[1] * CONNECTOR_SIZE # Move the corresponding point on all the wires connected for wire, index in self.Wires: - if wire not in exclude: + if (exclude is None) or (wire not in exclude): if index == 0: wire.MoveStartPoint(wx.Point(x, y)) else: wire.MoveEndPoint(wx.Point(x, y)) - + # Refreshes the model of all the wires connected def RefreshWires(self): for wire in self.Wires: wire[0].RefreshModel() - + # Refreshes the parent block model def RefreshParentBlock(self): self.ParentBlock.RefreshModel(False) - + # Highlight the parent block def HighlightParentBlock(self, highlight): self.ParentBlock.SetHighlighted(highlight) self.ParentBlock.Refresh() - + # Returns all the blocks connected to this connector def GetConnectedBlocks(self): blocks = [] @@ -1330,44 +1349,44 @@ if block not in blocks: blocks.append(block) return blocks - + # Returns the connector negated property def IsNegated(self): return self.Negated - + # Changes the connector negated property def SetNegated(self, negated): if self.ParentBlock.IsOfType("BOOL", self.Type): self.Negated = negated self.Edge = "none" - + # Returns the connector edge property def GetEdge(self): return self.Edge - + # Changes the connector edge property def SetEdge(self, edge): if self.ParentBlock.IsOfType("BOOL", self.Type): - self.Edge = edge + self.Edge = edge self.Negated = False - + # assume that pointer is already inside of this connector def ConnectionAvailable(self, direction=None, exclude=True): wire_nums = len(self.Wires) - - connector_free = (wire_nums<= 0) + + connector_free = (wire_nums <= 0) connector_max_used = ((wire_nums > 0) and self.OneConnected) if (self.Parent.CurrentLanguage in ["SFC", "LD"]) and (self.Type == "BOOL"): - connector_max_used = False; + connector_max_used = False # connector is available for new connection - connect = connector_free or not connector_max_used + connect = connector_free or not connector_max_used return connect, connector_max_used - + # Tests if the point given is near from the end point of this connector def TestPoint(self, pt, direction=None, exclude=True): - inside = False; - check_point = (not exclude) and (direction is None or self.Direction == direction); + inside = False + check_point = (not exclude) and (direction is None or self.Direction == direction) if check_point: # Calculate a square around the end point of this connector @@ -1377,10 +1396,10 @@ width = ANCHOR_DISTANCE * 2 + abs(self.Direction[0]) * CONNECTOR_SIZE height = ANCHOR_DISTANCE * 2 + abs(self.Direction[1]) * CONNECTOR_SIZE rect = wx.Rect(x, y, width, height) - inside = rect.InsideXY(pt.x, pt.y); - + inside = rect.InsideXY(pt.x, pt.y) + return inside - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -1391,9 +1410,7 @@ dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) parent_pos = self.ParentBlock.GetPosition() - posx = parent_pos[0] + self.Pos.x - posy = parent_pos[1] + self.Pos.y - xstart = parent_pos[0] + self.Pos.x + xstart = parent_pos[0] + self.Pos.x ystart = parent_pos[1] + self.Pos.y if self.Direction[0] < 0: xstart += 1 @@ -1401,18 +1418,18 @@ ystart += 1 xend = xstart + CONNECTOR_SIZE * self.Direction[0] yend = ystart + CONNECTOR_SIZE * self.Direction[1] - dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), + dc.DrawLine(round((xstart + self.Direction[0]) * scalex), round((ystart + self.Direction[1]) * scaley), round(xend * scalex), round(yend * scaley)) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Adds an highlight to the connector def AddHighlight(self, infos, start, end, highlight_type): if highlight_type == ERROR_HIGHLIGHT: - for wire, handle in self.Wires: + for wire, _handle in self.Wires: wire.SetValid(False) AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the connector def RemoveHighlight(self, infos, start, end, highlight_type): error = False @@ -1423,9 +1440,9 @@ error |= highlight == ERROR_HIGHLIGHT self.Highlights = highlights if not error: - for wire, handle in self.Wires: + for wire, _handle in self.Wires: wire.SetValid(wire.IsConnectedCompatible()) - + # Removes all the highlights of one particular type from the connector def ClearHighlight(self, highlight_type=None): error = False @@ -1439,15 +1456,15 @@ error |= highlight == ERROR_HIGHLIGHT self.Highlights = highlights if not error: - for wire, handle in self.Wires: + for wire, _handle in self.Wires: wire.SetValid(wire.IsConnectedCompatible()) - + # Draws the connector def Draw(self, dc): if self.Selected: dc.SetPen(MiterPen(wx.BLUE, 3)) dc.SetBrush(wx.WHITE_BRUSH) - #elif len(self.Highlights) > 0: + # elif len(self.Highlights) > 0: # dc.SetPen(MiterPen(self.Highlights[-1][1])) # dc.SetBrush(wx.Brush(self.Highlights[-1][0])) else: @@ -1466,19 +1483,19 @@ dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) parent_pos = self.ParentBlock.GetPosition() - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) else: name_size = self.NameSize - + if self.Negated: # If connector is negated, draw a circle xcenter = parent_pos[0] + self.Pos.x + (CONNECTOR_SIZE * self.Direction[0]) / 2 ycenter = parent_pos[1] + self.Pos.y + (CONNECTOR_SIZE * self.Direction[1]) / 2 dc.DrawCircle(xcenter, ycenter, CONNECTOR_SIZE / 2) else: - xstart = parent_pos[0] + self.Pos.x + xstart = parent_pos[0] + self.Pos.x ystart = parent_pos[1] + self.Pos.y if self.Edge == "rising": # If connector has a rising edge, draw a right arrow @@ -1524,26 +1541,27 @@ self.ValueSize = self.ParentBlock.Parent.GetMiniTextExtent(self.ComputedValue) if self.ValueSize is not None: width, height = self.ValueSize - dc.DrawText(self.ComputedValue, - parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + \ - width * (self.Direction[0] - 1) / 2, - parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + \ - height * (self.Direction[1] - 1)) + dc.DrawText(self.ComputedValue, + parent_pos[0] + self.Pos.x + CONNECTOR_SIZE * self.Direction[0] + + width * (self.Direction[0] - 1) / 2, + parent_pos[1] + self.Pos.y + CONNECTOR_SIZE * self.Direction[1] + + height * (self.Direction[1] - 1)) dc.SetFont(self.ParentBlock.Parent.GetFont()) dc.SetTextForeground(wx.BLACK) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Common Wire Element -#------------------------------------------------------------------------------- - -""" -Class that implements a wire for connecting two blocks -""" +# ------------------------------------------------------------------------------- + class Wire(Graphic_Element, DebugDataConsumer): - + """ + Class that implements a wire for connecting two blocks + """ + # Create a new wire - def __init__(self, parent, start = None, end = None): + def __init__(self, parent, start=None, end=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.StartPoint = start @@ -1568,20 +1586,20 @@ self.ComputingType = False self.Font = parent.GetMiniFont() self.ErrHighlight = False - + def GetDefinition(self): if self.StartConnected is not None and self.EndConnected is not None: startblock = self.StartConnected.GetParentBlock() endblock = self.EndConnected.GetParentBlock() return [], [(startblock.GetId(), endblock.GetId())] return [], [] - + def Flush(self): self.StartConnected = None self.EndConnected = None - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.StartConnected: rect = rect.Union(self.StartConnected.GetRedrawRect(movex, movey)) @@ -1607,8 +1625,9 @@ y = self.Points[middle].y - height rect = rect.Union(wx.Rect(x, y, width, height)) return rect - - def Clone(self, parent, connectors = {}, dx = 0, dy = 0): + + def Clone(self, parent, connectors=None, dx=0, dy=0): + connectors = {} if connectors is None else connectors start_connector = connectors.get(self.StartConnected, None) end_connector = connectors.get(self.EndConnected, None) if start_connector is not None and end_connector is not None: @@ -1620,18 +1639,15 @@ wire.ConnectEndPoint(end_connector.GetPosition(), end_connector) return wire return None - + # Forbids to change the wire position - def SetPosition(x, y): + def SetPosition(self, x, y): pass - + # Forbids to change the wire size - def SetSize(width, height): + def SetSize(self, width, height): pass - - # Forbids to et size of the group elements to their minimum size - pass - + # Moves and Resizes the element for fitting scaling def SetBestSize(self, scaling): if scaling is not None: @@ -1664,11 +1680,11 @@ point.y += movey return movex_max, movey_max return 0, 0 - + # Returns connector to which start point is connected def GetStartConnected(self): return self.StartConnected - + # Returns connector to which start point is connected def GetStartConnectedType(self): if self.StartConnected and not self.ComputingType: @@ -1677,11 +1693,11 @@ self.ComputingType = False return computed_type return None - + # Returns connector to which end point is connected def GetEndConnected(self): return self.EndConnected - + # Returns connector to which end point is connected def GetEndConnectedType(self): if self.EndConnected and not self.ComputingType: @@ -1690,7 +1706,7 @@ self.ComputingType = False return computed_type return None - + def GetConnectionDirection(self): if self.StartConnected is None and self.EndConnected is None: return None @@ -1707,26 +1723,26 @@ else: return (-self.StartPoint[1][0], -self.StartPoint[1][1]) return None - + def GetOtherConnected(self, connector): if self.StartConnected == connector: return self.EndConnected else: return self.StartConnected - + def GetOtherConnectedType(self, handle): if handle == 0: return self.GetEndConnectedType() else: return self.GetStartConnectedType() - + def IsConnectedCompatible(self): if self.StartConnected: return self.StartConnected.IsCompatible(self.GetEndConnectedType()) elif self.EndConnected: return True return False - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced @@ -1741,7 +1757,7 @@ if self.Value is not None and self.Value != "undefined" and not isinstance(self.Value, BooleanType): return self.Value return None - + def GetToolTipValue(self): return self.GetComputedValue() @@ -1773,25 +1789,25 @@ if isinstance(value, BooleanType) and self.StartConnected is not None: block = self.StartConnected.GetParentBlock() block.SpreadCurrent() - + # Unconnect the start and end points def Clean(self): if self.StartConnected: self.UnConnectStartPoint() if self.EndConnected: self.UnConnectEndPoint() - + # Delete this wire by calling the corresponding method def Delete(self): self.Parent.DeleteWire(self) - + # Select a segment and not the whole wire. It's useful for Ladder Diagram def SetSelectedSegment(self, segment): # The last segment is indicated if segment == -1: segment = len(self.Segments) - 1 # The selected segment is reinitialised - if segment == None: + if segment is None: if self.StartConnected: self.StartConnected.SetSelected(False) if self.EndConnected: @@ -1814,17 +1830,17 @@ self.EndConnected.SetSelected(True) self.SelectedSegment = segment self.Refresh() - + def SetValid(self, valid): self.Valid = valid if self.StartConnected: self.StartConnected.RefreshValid() if self.EndConnected: self.EndConnected.RefreshValid() - + def GetValid(self): return self.Valid - + # Reinitialize the wire points def ResetPoints(self): if self.StartPoint and self.EndPoint: @@ -1833,7 +1849,7 @@ else: self.Points = [] self.Segments = [] - + # Refresh the wire bounding box def RefreshBoundingBox(self): if len(self.Points) > 0: @@ -1862,7 +1878,7 @@ self.Pos.x, self.Pos.y = minx, miny self.Size = wx.Size(maxx - minx, maxy - miny) self.BoundingBox = wx.Rect(minbbxx, minbbxy, maxbbxx - minbbxx + 1, maxbbxy - minbbxy + 1) - + # Refresh the realpoints that permits to keep the proportionality in wire during resizing def RefreshRealPoints(self): if len(self.Points) > 0: @@ -1870,8 +1886,8 @@ # Calculate float relative position of each point with the minimum point for point in self.Points: self.RealPoints.append([float(point.x - self.Pos.x), float(point.y - self.Pos.y)]) - - # Returns the wire minimum size + + # Returns the wire minimum size def GetMinSize(self): width = 1 height = 1 @@ -1893,7 +1909,7 @@ width = MIN_SEGMENT_SIZE height = MIN_SEGMENT_SIZE return width + 1, height + 1 - + # Returns if the point given is on one of the wire segments def HitTest(self, pt, connectors=True): test = False @@ -1903,7 +1919,7 @@ x1 = self.Points[i].x - self.Segments[0][0] * CONNECTOR_SIZE y1 = self.Points[i].y - self.Segments[0][1] * CONNECTOR_SIZE else: - x1, y1 = self.Points[i].x, self.Points[i].y + x1, y1 = self.Points[i].x, self.Points[i].y if i == len(self.Points) - 2 and self.EndConnected is not None: x2 = self.Points[i + 1].x + self.Segments[-1][0] * CONNECTOR_SIZE y2 = self.Points[i + 1].y + self.Segments[-1][1] * CONNECTOR_SIZE @@ -1911,25 +1927,25 @@ x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y # Calculate a rectangle around the segment rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE, - abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) - test |= rect.InsideXY(pt.x, pt.y) + abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) + test |= rect.InsideXY(pt.x, pt.y) return test - - # Returns the wire start or end point if the point given is on one of them + + # Returns the wire start or end point if the point given is on one of them def TestPoint(self, pt): # Test the wire start point rect = wx.Rect(self.Points[0].x - ANCHOR_DISTANCE, self.Points[0].y - ANCHOR_DISTANCE, - 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) + 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) if rect.InsideXY(pt.x, pt.y): return 0 # Test the wire end point if len(self.Points) > 1: rect = wx.Rect(self.Points[-1].x - ANCHOR_DISTANCE, self.Points[-1].y - ANCHOR_DISTANCE, - 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) + 2 * ANCHOR_DISTANCE, 2 * ANCHOR_DISTANCE) if rect.InsideXY(pt.x, pt.y): return -1 return None - + # Returns the wire segment if the point given is on it def TestSegment(self, pt, all=False): for i in xrange(len(self.Segments)): @@ -1939,11 +1955,11 @@ x2, y2 = self.Points[i + 1].x, self.Points[i + 1].y # Calculate a rectangle around the segment rect = wx.Rect(min(x1, x2) - ANCHOR_DISTANCE, min(y1, y2) - ANCHOR_DISTANCE, - abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) + abs(x1 - x2) + 2 * ANCHOR_DISTANCE, abs(y1 - y2) + 2 * ANCHOR_DISTANCE) if rect.InsideXY(pt.x, pt.y): return i, self.Segments[i] return None - + # Define the wire points def SetPoints(self, points, verify=True): if len(points) > 1: @@ -1952,10 +1968,10 @@ self.StartPoint = [None, vector(self.Points[0], self.Points[1])] self.EndPoint = [None, vector(self.Points[-1], self.Points[-2])] # Calculate the start and end points - self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], - self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1]) - self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], - self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1]) + self.StartPoint[0] = wx.Point(self.Points[0].x + CONNECTOR_SIZE * self.StartPoint[1][0], + self.Points[0].y + CONNECTOR_SIZE * self.StartPoint[1][1]) + self.EndPoint[0] = wx.Point(self.Points[-1].x + CONNECTOR_SIZE * self.EndPoint[1][0], + self.Points[-1].y + CONNECTOR_SIZE * self.EndPoint[1][1]) self.Points[0] = self.StartPoint[0] self.Points[-1] = self.EndPoint[0] # Calculate the segments directions @@ -1965,7 +1981,7 @@ if verify and 0 < i < len(self.Points) - 2 and \ self.Points[i] == self.Points[i + 1] and \ self.Segments[-1] == vector(self.Points[i + 1], self.Points[i + 2]): - for j in xrange(2): + for dummy in xrange(2): self.Points.pop(i) else: segment = vector(self.Points[i], self.Points[i + 1]) @@ -1979,37 +1995,37 @@ i += 1 self.RefreshBoundingBox() self.RefreshRealPoints() - + # Returns the position of the point indicated def GetPoint(self, index): if index < len(self.Points): return self.Points[index].x, self.Points[index].y return None - + # Returns a list of the position of all wire points - def GetPoints(self, invert = False): + def GetPoints(self, invert=False): points = self.VerifyPoints() - points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], - points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1]) - points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], - points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1]) + points[0] = wx.Point(points[0].x - CONNECTOR_SIZE * self.StartPoint[1][0], + points[0].y - CONNECTOR_SIZE * self.StartPoint[1][1]) + points[-1] = wx.Point(points[-1].x - CONNECTOR_SIZE * self.EndPoint[1][0], + points[-1].y - CONNECTOR_SIZE * self.EndPoint[1][1]) # An inversion of the list is asked if invert: points.reverse() return points - + # Returns the position of the two selected segment points def GetSelectedSegmentPoints(self): - if self.SelectedSegment != None and len(self.Points) > 1: + if self.SelectedSegment is not None and len(self.Points) > 1: return self.Points[self.SelectedSegment:self.SelectedSegment + 2] return [] - + # Returns if the selected segment is the first and/or the last of the wire def GetSelectedSegmentConnections(self): - if self.SelectedSegment != None and len(self.Points) > 1: + if self.SelectedSegment is not None and len(self.Points) > 1: return self.SelectedSegment == 0, self.SelectedSegment == len(self.Segments) - 1 return (True, True) - + # Returns the connectors on which the wire is connected def GetConnected(self): connected = [] @@ -2018,7 +2034,7 @@ if self.EndConnected and self.EndPoint[1] == WEST: connected.append(self.EndConnected) return connected - + # Returns the id of the block connected to the first or the last wire point def GetConnectedInfos(self, index): if index == 0 and self.StartConnected: @@ -2026,15 +2042,15 @@ elif index == -1 and self.EndConnected: return self.EndConnected.GetBlockId(), self.EndConnected.GetName() return None - + # Update the wire points position by keeping at most possible the current positions - def GeneratePoints(self, realpoints = True): + def GeneratePoints(self, realpoints=True): i = 0 # Calculate the start enad end points with the minimum segment size in the right direction end = wx.Point(self.EndPoint[0].x + self.EndPoint[1][0] * MIN_SEGMENT_SIZE, - self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE) - start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, - self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE) + self.EndPoint[0].y + self.EndPoint[1][1] * MIN_SEGMENT_SIZE) + start = wx.Point(self.StartPoint[0].x + self.StartPoint[1][0] * MIN_SEGMENT_SIZE, + self.StartPoint[0].y + self.StartPoint[1][1] * MIN_SEGMENT_SIZE) # Evaluate the point till it's the last while i < len(self.Points) - 1: # The next point is the last @@ -2046,15 +2062,17 @@ # If the end point is not in the start direction, a point is added if v_end != self.Segments[0] or v_end == self.EndPoint[1]: self.Points.insert(1, wx.Point(start.x, start.y)) - self.Segments.insert(1, DirectionChoice((self.Segments[0][1], - self.Segments[0][0]), v_end, self.EndPoint[1])) + self.Segments.insert(1, DirectionChoice( + (self.Segments[0][1], + self.Segments[0][0]), v_end, self.EndPoint[1])) # The current point is the second elif i == 1: # The previous direction and the target direction are mainly opposed, a point is added if product(v_end, self.Segments[0]) < 0: self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y)) - self.Segments.insert(2, DirectionChoice((self.Segments[1][1], - self.Segments[1][0]), v_end, self.EndPoint[1])) + self.Segments.insert(2, DirectionChoice( + (self.Segments[1][1], + self.Segments[1][0]), v_end, self.EndPoint[1])) # The previous direction and the end direction are the same or they are # perpendiculars and the end direction points towards current segment elif product(self.Segments[0], self.EndPoint[1]) >= 0 and product(self.Segments[1], self.EndPoint[1]) <= 0: @@ -2066,8 +2084,9 @@ # If the previous direction and the end direction are the same, a point is added if product(self.Segments[0], self.EndPoint[1]) > 0: self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y)) - self.Segments.insert(2, DirectionChoice((self.Segments[1][1], - self.Segments[1][0]), v_end, self.EndPoint[1])) + self.Segments.insert(2, DirectionChoice( + (self.Segments[1][1], + self.Segments[1][0]), v_end, self.EndPoint[1])) else: # Current point is positioned in the middle of start point # and end point on the current direction and a point is added @@ -2076,8 +2095,9 @@ if self.Segments[0][1] != 0: self.Points[1].y = (end.y + start.y) / 2 self.Points.insert(2, wx.Point(self.Points[1].x, self.Points[1].y)) - self.Segments.insert(2, DirectionChoice((self.Segments[1][1], - self.Segments[1][0]), v_end, self.EndPoint[1])) + self.Segments.insert(2, DirectionChoice( + (self.Segments[1][1], + self.Segments[1][0]), v_end, self.EndPoint[1])) else: # The previous direction and the end direction are perpendiculars if product(self.Segments[i - 1], self.EndPoint[1]) == 0: @@ -2093,7 +2113,7 @@ self.Segments[i - 1] = (-self.Segments[i - 1][0], -self.Segments[i - 1][1]) else: test = True - # If the current point is the third, test if the second + # If the current point is the third, test if the second # point can be aligned with the end point if i == 2: test_point = wx.Point(self.Points[1].x, self.Points[1].y) @@ -2119,8 +2139,12 @@ if self.Segments[1][1] != 0: self.Points[2].y = (self.Points[1].y + end.y) / 2 self.Points.insert(3, wx.Point(self.Points[2].x, self.Points[2].y)) - self.Segments.insert(3, DirectionChoice((self.Segments[2][1], - self.Segments[2][0]), v_end, self.EndPoint[1])) + self.Segments.insert( + 3, + DirectionChoice((self.Segments[2][1], + self.Segments[2][0]), + v_end, + self.EndPoint[1])) else: # Current point is aligned with end point if self.Segments[i - 1][0] != 0: @@ -2140,16 +2164,22 @@ self.Points[i].y = (end.y + self.Points[i - 1].y) / 2 # A point is added self.Points.insert(i + 1, wx.Point(self.Points[i].x, self.Points[i].y)) - self.Segments.insert(i + 1, DirectionChoice((self.Segments[i][1], - self.Segments[i][0]), v_end, self.EndPoint[1])) + self.Segments.insert( + i + 1, + DirectionChoice((self.Segments[i][1], + self.Segments[i][0]), v_end, self.EndPoint[1])) else: # Current point is the first, and second is not mainly in the first direction if i == 0 and product(vector(start, self.Points[1]), self.Segments[0]) < 0: - # If first and second directions aren't perpendiculars, a point is added + # If first and second directions aren't perpendiculars, a point is added if product(self.Segments[0], self.Segments[1]) != 0: self.Points.insert(1, wx.Point(start.x, start.y)) - self.Segments.insert(1, DirectionChoice((self.Segments[0][1], - self.Segments[0][0]), vector(start, self.Points[1]), self.Segments[1])) + self.Segments.insert( + 1, + DirectionChoice((self.Segments[0][1], + self.Segments[0][0]), + vector(start, self.Points[1]), + self.Segments[1])) else: self.Points[1].x, self.Points[1].y = start.x, start.y else: @@ -2165,7 +2195,7 @@ self.RefreshBoundingBox() if realpoints: self.RefreshRealPoints() - + # Verify that two consecutive points haven't the same position def VerifyPoints(self): points = [point for point in self.Points] @@ -2173,7 +2203,7 @@ i = 1 while i < len(points) - 1: if points[i] == points[i + 1] and segments[i - 1] == segments[i + 1]: - for j in xrange(2): + for dummy in xrange(2): points.pop(i) segments.pop(i) else: @@ -2185,9 +2215,9 @@ self.RefreshBoundingBox() self.RefreshRealPoints() return points - + # Moves all the wire points except the first and the last if they are connected - def Move(self, dx, dy, endpoints = False): + def Move(self, dx, dy, endpoints=False): for i, point in enumerate(self.Points): if endpoints or not (i == 0 and self.StartConnected) and not (i == len(self.Points) - 1 and self.EndConnected): point.x += dx @@ -2195,7 +2225,7 @@ self.StartPoint[0] = self.Points[0] self.EndPoint[0] = self.Points[-1] self.GeneratePoints() - + # Resize the wire from position and size given def Resize(self, x, y, width, height): if len(self.Points) > 1: @@ -2212,9 +2242,9 @@ else: dir = (0, 0) pointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0] * width / float(max(lastwidth, 1)))), - width - dir[0] * MIN_SEGMENT_SIZE)) + width - dir[0] * MIN_SEGMENT_SIZE)) pointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1] * height / float(max(lastheight, 1)))), - height - dir[1] * MIN_SEGMENT_SIZE)) + height - dir[1] * MIN_SEGMENT_SIZE)) self.Points[i] = wx.Point(minx + x + pointx, miny + y + pointy) self.StartPoint[0] = self.Points[0] self.EndPoint[0] = self.Points[-1] @@ -2246,33 +2276,35 @@ dir = self.EndPoint[1] else: dir = (0, 0) - realpointx = max(-dir[0] * MIN_SEGMENT_SIZE, min(int(round(point[0])), - width - dir[0] * MIN_SEGMENT_SIZE)) - realpointy = max(-dir[1] * MIN_SEGMENT_SIZE, min(int(round(point[1])), - height - dir[1] * MIN_SEGMENT_SIZE)) + realpointx = max(-dir[0] * MIN_SEGMENT_SIZE, + min(int(round(point[0])), + width - dir[0] * MIN_SEGMENT_SIZE)) + realpointy = max(-dir[1] * MIN_SEGMENT_SIZE, + min(int(round(point[1])), + height - dir[1] * MIN_SEGMENT_SIZE)) self.Points[i] = wx.Point(minx + x + realpointx, miny + y + realpointy) self.StartPoint[0] = self.Points[0] self.EndPoint[0] = self.Points[-1] self.GeneratePoints(False) - + # Moves the wire start point and update the wire points def MoveStartPoint(self, point): if len(self.Points) > 1: self.StartPoint[0] = point self.Points[0] = point self.GeneratePoints() - + # Changes the wire start direction and update the wire points def SetStartPointDirection(self, dir): if len(self.Points) > 1: self.StartPoint[1] = dir self.Segments[0] = dir self.GeneratePoints() - + # Rotates the wire start direction by an angle of 90 degrees anticlockwise def RotateStartPoint(self): self.SetStartPointDirection((self.StartPoint[1][1], -self.StartPoint[1][0])) - + # Connects wire start point to the connector given and moves wire start point # to given point def ConnectStartPoint(self, point, connector): @@ -2280,17 +2312,17 @@ self.MoveStartPoint(point) self.StartConnected = connector self.RefreshBoundingBox() - + # Unconnects wire start point - def UnConnectStartPoint(self, delete = False): + def UnConnectStartPoint(self, delete=False): if delete: self.StartConnected = None self.Delete() elif self.StartConnected: - self.StartConnected.UnConnect(self, unconnect = False) + self.StartConnected.UnConnect(self, unconnect=False) self.StartConnected = None self.RefreshBoundingBox() - + # Moves the wire end point and update the wire points def MoveEndPoint(self, point): if len(self.Points) > 1: @@ -2303,7 +2335,7 @@ if len(self.Points) > 1: self.EndPoint[1] = dir self.GeneratePoints() - + # Rotates the wire end direction by an angle of 90 degrees anticlockwise def RotateEndPoint(self): self.SetEndPointDirection((self.EndPoint[1][1], -self.EndPoint[1][0])) @@ -2315,17 +2347,17 @@ self.MoveEndPoint(point) self.EndConnected = connector self.RefreshBoundingBox() - + # Unconnects wire end point - def UnConnectEndPoint(self, delete = False): + def UnConnectEndPoint(self, delete=False): if delete: self.EndConnected = None self.Delete() elif self.EndConnected: - self.EndConnected.UnConnect(self, unconnect = False) + self.EndConnected.UnConnect(self, unconnect=False) self.EndConnected = None self.RefreshBoundingBox() - + # Moves the wire segment given by its index def MoveSegment(self, idx, movex, movey, scaling): if 0 < idx < len(self.Segments) - 1: @@ -2356,7 +2388,7 @@ if start_y != self.Points[idx].y: return 0, self.Points[idx].y - start_y return 0, 0 - + # Adds two points in the middle of the handled segment def AddSegment(self): handle_type, handle = self.Handle @@ -2391,30 +2423,30 @@ self.Points.insert(segment + 4, wx.Point(p2x, p2y)) self.Segments.insert(segment + 4, dir) self.GeneratePoints() - + # Delete the handled segment by removing the two segment points def DeleteSegment(self): handle_type, handle = self.Handle if handle_type == HANDLE_SEGMENT: - segment, dir = handle - for i in xrange(2): + segment, _dir = handle + for dummy in xrange(2): self.Points.pop(segment) self.Segments.pop(segment) self.GeneratePoints() self.RefreshModel() - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) # Test if a point have been handled - #result = self.TestPoint(pos) - #if result != None: + # result = self.TestPoint(pos) + # if result != None: # self.Handle = (HANDLE_POINT, result) # wx.CallAfter(self.Parent.SetCurrentCursor, 1) - #else: + # else: # Test if a segment have been handled result = self.TestSegment(pos) - if result != None: + if result is not None: if result[1] in (NORTH, SOUTH): wx.CallAfter(self.Parent.SetCurrentCursor, 4) elif result[1] in (EAST, WEST): @@ -2424,20 +2456,20 @@ else: Graphic_Element.OnLeftDown(self, event, dc, scaling) self.oldPos = pos - + # Method called when a RightUp event has been generated def OnRightUp(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) # Test if a segment has been handled result = self.TestSegment(pos, True) - if result != None: + if result is not None: self.Handle = (HANDLE_SEGMENT, result) # Popup the menu with special items for a wire self.Parent.PopupWireMenu(0 < result[0] < len(self.Segments) - 1) else: # Execute the default method for a graphic element Graphic_Element.OnRightUp(self, event, dc, scaling) - + # Method called when a LeftDClick event has been generated def OnLeftDClick(self, event, dc, scaling): rect = self.GetRedrawRect() @@ -2492,7 +2524,7 @@ self.Parent.RefreshBuffer() rect.Union(self.GetRedrawRect()) self.Parent.RefreshRect(self.Parent.GetScrolledRect(rect), False) - + # Method called when a Motion event has been generated def OnMotion(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -2511,7 +2543,7 @@ else: # Execute the default method for a graphic element return Graphic_Element.OnMotion(self, event, dc, scaling) - + # Refreshes the wire state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): handle_type, handle = self.Handle @@ -2572,14 +2604,14 @@ # Execute the default method for a graphic element else: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - + # Refreshes the wire model def RefreshModel(self, move=True): if self.StartConnected and self.StartPoint[1] in [WEST, NORTH]: self.StartConnected.RefreshParentBlock() if self.EndConnected and self.EndPoint[1] in [WEST, NORTH]: self.EndConnected.RefreshParentBlock() - + # Change the variable that indicates if this element is highlighted def SetHighlighted(self, highlighted): self.Highlighted = highlighted @@ -2587,28 +2619,27 @@ self.OverStart = False self.OverEnd = False self.Refresh() - + def HighlightPoint(self, pos): - refresh = False start, end = self.OverStart, self.OverEnd self.OverStart = False self.OverEnd = False # Test if a point has been handled result = self.TestPoint(pos) - if result != None: + if result is not None: if result == 0 and self.StartConnected is not None: self.OverStart = True elif result != 0 and self.EndConnected is not None: self.OverEnd = True if start != self.OverStart or end != self.OverEnd: self.Refresh() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() dc.SetUserScale(1, 1) # If user trying to connect wire with wrong input, highlight will become red. - if self.ErrHighlight == True and not (self.EndConnected): + if self.ErrHighlight and not self.EndConnected: highlightcolor = wx.RED else: highlightcolor = HIGHLIGHTCOLOR @@ -2617,31 +2648,31 @@ dc.SetLogicalFunction(wx.AND) # Draw the start and end points if they are not connected or the mouse is over them if len(self.Points) > 0 and (not self.StartConnected or self.OverStart): - dc.DrawCircle(round(self.Points[0].x * scalex), - round(self.Points[0].y * scaley), + dc.DrawCircle(round(self.Points[0].x * scalex), + round(self.Points[0].y * scaley), (POINT_RADIUS + 1) * scalex + 2) if len(self.Points) > 1 and (not self.EndConnected or self.OverEnd): dc.DrawCircle(self.Points[-1].x * scalex, self.Points[-1].y * scaley, (POINT_RADIUS + 1) * scalex + 2) # Draw the wire lines and the last point (it seems that DrawLines stop before the last point) if len(self.Points) > 1: - points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), + points = [wx.Point(round((self.Points[0].x - self.Segments[0][0]) * scalex), round((self.Points[0].y - self.Segments[0][1]) * scaley))] points.extend([wx.Point(round(point.x * scalex), round(point.y * scaley)) for point in self.Points[1:-1]]) - points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), + points.append(wx.Point(round((self.Points[-1].x + self.Segments[-1][0]) * scalex), round((self.Points[-1].y + self.Segments[-1][1]) * scaley))) else: points = [] dc.DrawLines(points) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + if self.StartConnected is not None: self.StartConnected.DrawHighlightment(dc) self.StartConnected.Draw(dc) if self.EndConnected is not None: self.EndConnected.DrawHighlightment(dc) self.EndConnected.Draw(dc) - + # Draws the wire lines and points def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -2712,9 +2743,10 @@ dc.SetTextForeground(wx.BLACK) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Graphic comment element -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def FilterHighlightsByRow(highlights, row, length): _highlights = [] @@ -2727,6 +2759,7 @@ _highlights.append((start, end, highlight_type)) return _highlights + def FilterHighlightsByColumn(highlights, start_col, end_col): _highlights = [] for start, end, highlight_type in highlights: @@ -2736,41 +2769,41 @@ _highlights.append((start, end, highlight_type)) return _highlights -""" -Class that implements a comment -""" class Comment(Graphic_Element): + """ + Class that implements a comment + """ # Create a new comment - def __init__(self, parent, content, id = None): + def __init__(self, parent, content, id=None): Graphic_Element.__init__(self, parent) self.Id = id self.Content = content self.Pos = wx.Point(0, 0) self.Size = wx.Size(0, 0) self.Highlights = [] - + # Make a clone of this comment - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): comment = Comment(parent, self.Content, id) if pos is not None: comment.SetPosition(pos.x, pos.y) comment.SetSize(self.Size[0], self.Size[1]) return comment - + # Method for keeping compatibility with others def Clean(self): pass - + # Delete this comment by calling the corresponding method def Delete(self): self.Parent.DeleteComment(self) - + # Refresh the comment bounding box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Changes the comment size def SetSize(self, width, height): self.Size.SetWidth(width) @@ -2780,7 +2813,7 @@ # Returns the comment size def GetSize(self): return self.Size.GetWidth(), self.Size.GetHeight() - + # Returns the comment minimum size def GetMinSize(self): dc = wx.ClientDC(self.Parent) @@ -2793,7 +2826,7 @@ min_width = max(min_width, wordwidth) min_height = max(min_height, wordheight) return min_width + 20, min_height + 20 - + # Changes the comment position def SetPosition(self, x, y): self.Pos.x = x @@ -2815,51 +2848,51 @@ # Returns the comment position def GetPosition(self): return self.Pos.x, self.Pos.y - + # Moves the comment - def Move(self, dx, dy, connected = True): + def Move(self, dx, dy, connected=True): self.Pos.x += dx self.Pos.y += dy self.RefreshBoundingBox() - + # Resizes the comment with the position and the size given def Resize(self, x, y, width, height): self.Move(x, y) self.SetSize(width, height) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the wire state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE and self.Parent.CurrentLanguage == "LD": movex = movey = 0 return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - + # Refreshes the comment model def RefreshModel(self, move=True): self.Parent.RefreshCommentModel(self) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the comment content self.Parent.EditCommentContent(self) - + # Adds an highlight to the comment def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "content": AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the comment def RemoveHighlight(self, infos, start, end, highlight_type): RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the comment def ClearHighlight(self, highlight_type=None): self.Highlights = ClearHighlights(self.Highlights, highlight_type) - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -2867,29 +2900,29 @@ dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) - + left = (self.Pos.x - 1) * scalex - 2 right = (self.Pos.x + self.Size[0] + 1) * scalex + 2 top = (self.Pos.y - 1) * scaley - 2 bottom = (self.Pos.y + self.Size[1] + 1) * scaley + 2 angle_top = (self.Pos.x + self.Size[0] - 9) * scalex + 2 angle_right = (self.Pos.y + 9) * scaley - 2 - + polygon = [wx.Point(left, top), wx.Point(angle_top, top), wx.Point(right, angle_right), wx.Point(right, bottom), wx.Point(left, bottom)] dc.DrawPolygon(polygon) - + dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws the comment and its content def Draw(self, dc): Graphic_Element.Draw(self, dc) dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) # Draws the comment shape - polygon = [wx.Point(self.Pos.x, self.Pos.y), + polygon = [wx.Point(self.Pos.x, self.Pos.y), wx.Point(self.Pos.x + self.Size[0] - 10, self.Pos.y), wx.Point(self.Pos.x + self.Size[0], self.Pos.y + 10), wx.Point(self.Pos.x + self.Size[0], self.Pos.y + self.Size[1]), @@ -2951,4 +2984,3 @@ y += wordheight + 5 if y + wordheight > self.Pos.y + self.Size[1] - 10: break - diff -r c1298e7ffe3a -r 8391c11477f4 graphics/LD_Objects.py --- a/graphics/LD_Objects.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/LD_Objects.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,22 +22,25 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.GraphicCommons import * from graphics.DebugDataConsumer import DebugDataConsumer from plcopen.structures import * -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Ladder Diagram PowerRail -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a power rail -""" +# ------------------------------------------------------------------------------- + class LD_PowerRail(Graphic_Element): - + """ + Class that implements the graphic representation of a power rail + """ + # Create a new power rail def __init__(self, parent, type, id=None, connectors=1): Graphic_Element.__init__(self, parent) @@ -47,14 +50,14 @@ self.Id = id self.Extensions = [LD_LINE_SIZE / 2, LD_LINE_SIZE / 2] self.SetType(type, connectors) - + def Flush(self): for connector in self.Connectors: connector.Flush() self.Connectors = [] - + # Make a clone of this LD_PowerRail - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): powerrail = LD_PowerRail(parent, self.Type, id) powerrail.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -65,13 +68,13 @@ for connector in self.Connectors: powerrail.Connectors.append(connector.Clone(powerrail)) return powerrail - + def GetConnectorTranslation(self, element): return dict(zip([connector for connector in self.Connectors], [connector for connector in element.Connectors])) - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) for connector in self.Connectors: rect = rect.Union(connector.GetRedrawRect(movex, movey)) @@ -80,7 +83,7 @@ if connector.IsConnected(): rect = rect.Union(connector.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to change the power rail size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: @@ -88,47 +91,47 @@ else: Graphic_Element.SetSize(self, LD_POWERRAIL_WIDTH, height) self.RefreshConnectors() - + # Forbids to select a power rail def HitTest(self, pt, connectors=True): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: - return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) != None + return Graphic_Element.HitTest(self, pt, connectors) or self.TestConnector(pt, exclude=False) is not None return False - + # Forbids to select a power rail def IsInSelection(self, rect): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: return Graphic_Element.IsInSelection(self, rect) return False - + # Deletes this power rail by calling the appropriate method def Delete(self): self.Parent.DeletePowerRail(self) - + # Unconnect all connectors def Clean(self): for connector in self.Connectors: - connector.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + connector.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the power rail bounding box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Refresh the power rail size def RefreshSize(self): self.Size = wx.Size(LD_POWERRAIL_WIDTH, max(LD_LINE_SIZE * len(self.Connectors), self.Size[1])) self.RefreshBoundingBox() - + # Returns the block minimum size def GetMinSize(self, default=False): height = (LD_LINE_SIZE * (len(self.Connectors) - 1) if default else 0) return LD_POWERRAIL_WIDTH, height + self.Extensions[0] + self.Extensions[1] - + # Add a connector or a blank to this power rail at the last place def AddConnector(self): self.InsertConnector(len(self.Connectors)) - + # Add a connector or a blank to this power rail at the place given def InsertConnector(self, idx): if self.Type == LEFTRAIL: @@ -138,7 +141,7 @@ self.Connectors.insert(idx, connector) self.RefreshSize() self.RefreshConnectors() - + # Moves the divergence connector given def MoveConnector(self, connector, movey): position = connector.GetRelPosition() @@ -163,19 +166,19 @@ self.Size[1] = max(maxy + self.Extensions[1], self.Size[1]) connector.MoveConnected() self.RefreshBoundingBox() - + # Returns the index in connectors list for the connector given def GetConnectorIndex(self, connector): if connector in self.Connectors: return self.Connectors.index(connector) return None - + # Delete the connector or blank from connectors list at the index given def DeleteConnector(self, idx): self.Connectors.pop(idx) self.RefreshConnectors() self.RefreshSize() - + # Refresh the positions of the power rail connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -193,14 +196,14 @@ elif self.Type == RIGHTRAIL: connector.SetPosition(wx.Point(0, position)) self.RefreshConnected() - + # Refresh the position of wires connected to power rail - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): for connector in self.Connectors: connector.MoveConnected(exclude) - - # Returns the power rail connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the power rail connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test each connector if it exists @@ -208,22 +211,22 @@ if name == connector.GetName(): return connector return self.FindNearestConnector(position, [connector for connector in self.Connectors if connector is not None]) - - # Returns all the power rail connectors + + # Returns all the power rail connectors def GetConnectors(self): connectors = [connector for connector in self.Connectors if connector] if self.Type == LEFTRAIL: return {"inputs": [], "outputs": connectors} else: return {"inputs": connectors, "outputs": []} - + # Test if point given is on one of the power rail connectors - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): for connector in self.Connectors: if connector.TestPoint(pt, direction, exclude): return connector return None - + # Returns the power rail type def SetType(self, type, connectors): if type != self.Type or len(self.Connectors) != connectors: @@ -232,14 +235,14 @@ self.Type = type self.Clean() self.Connectors = [] - for connector in xrange(connectors): + for dummy in xrange(connectors): self.AddConnector() self.RefreshSize() - + # Returns the power rail type def GetType(self): return self.Type - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): self.RealConnectors = [] @@ -249,16 +252,16 @@ position = connector.GetRelPosition() self.RealConnectors.append(max(0., min(float(position.y - self.Extensions[0]) / float(height), 1.))) elif len(self.Connectors) > 1: - self.RealConnectors = map(lambda x : x * 1 / (len(self.Connectors) - 1), xrange(len(self.Connectors))) + self.RealConnectors = map(lambda x: x * 1 / (len(self.Connectors) - 1), xrange(len(self.Connectors))) else: self.RealConnectors = [0.5] Graphic_Element.OnLeftDown(self, event, dc, scaling) - + # Method called when a LeftUp event have been generated def OnLeftUp(self, event, dc, scaling): Graphic_Element.OnLeftUp(self, event, dc, scaling) self.RealConnectors = None - + # Method called when a LeftDown event have been generated def OnRightDown(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -272,12 +275,12 @@ self.oldPos = GetScaledEventPosition(event, dc, scaling) else: Graphic_Element.OnRightDown(self, event, dc, scaling) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the powerrail properties self.Parent.EditPowerRailContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): handle_type, handle = self.Handle @@ -292,7 +295,7 @@ Graphic_Element.OnRightUp(self, event, dc, scaling) else: self.Parent.PopupDefaultMenu() - + def Resize(self, x, y, width, height): self.Move(x, y) if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: @@ -314,16 +317,16 @@ elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) return 0, 0 - + # Refreshes the power rail model def RefreshModel(self, move=True): self.Parent.RefreshPowerRailModel(self) - # If power rail has moved and power rail is of type LEFT, refresh the model + # If power rail has moved and power rail is of type LEFT, refresh the model # of wires connected to connectors if move and self.Type == LEFTRAIL: for connector in self.Connectors: connector.RefreshWires() - + # Draws power rail def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -337,20 +340,20 @@ # Draw connectors for connector in self.Connectors: connector.Draw(dc) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Ladder Diagram Contact -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a contact -""" +# ------------------------------------------------------------------------------- + class LD_Contact(Graphic_Element, DebugDataConsumer): - + """ + Class that implements the graphic representation of a contact + """ + # Create a new contact - def __init__(self, parent, type, name, id = None): + def __init__(self, parent, type, name, id=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.Type = type @@ -365,7 +368,7 @@ self.PreviousSpreading = False self.RefreshNameSize() self.RefreshTypeSize() - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -373,13 +376,13 @@ if self.Output is not None: self.Output.Flush() self.Output = None - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def SetValue(self, value): if self.Type == CONTACT_RISING: refresh = self.Value and not self.PreviousValue @@ -393,7 +396,7 @@ if self.Visible: self.Parent.ElementNeedRefresh(self) self.SpreadCurrent() - + def SpreadCurrent(self): if self.Parent.Debug: if self.Value is None: @@ -414,9 +417,9 @@ elif not spreading and self.PreviousSpreading: self.Output.SpreadCurrent(False) self.PreviousSpreading = spreading - + # Make a clone of this LD_Contact - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): contact = LD_Contact(parent, self.Type, self.Name, id) contact.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -426,12 +429,12 @@ contact.Input = self.Input.Clone(contact) contact.Output = self.Output.Clone(contact) return contact - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input, self.Output : element.Output} - + return {self.Input: element.Input, self.Output: element.Output} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) @@ -445,30 +448,30 @@ def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: movex = movey = 0 - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac=2) + # Forbids to change the contact size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) self.RefreshConnectors() - + # Delete this contact by calling the appropriate method def Delete(self): self.Parent.DeleteContact(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for name def RefreshNameSize(self): if self.Name != "": self.NameSize = self.Parent.GetTextExtent(self.Name) else: self.NameSize = 0, 0 - + # Refresh the size of text for type def RefreshTypeSize(self): typetext = "" @@ -482,7 +485,7 @@ self.TypeSize = self.Parent.GetTextExtent(typetext) else: self.TypeSize = 0, 0 - + # Refresh the contact bounding box def RefreshBoundingBox(self): # Calculate the size of the name outside the contact @@ -499,33 +502,33 @@ bbx_y = self.Pos.y bbx_height = self.Size[1] self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Returns the block minimum size def GetMinSize(self): return LD_ELEMENT_SIZE - + # Refresh the position of wire connected to contact - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): self.Input.MoveConnected(exclude) self.Output.MoveConnected(exclude) - - # Returns the contact connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the contact connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector - #if name == self.Input.GetName(): + # if name == self.Input.GetName(): # return self.Input if name == self.Output.GetName(): return self.Output return self.FindNearestConnector(position, [self.Input, self.Output]) - - # Returns input and output contact connectors + + # Returns input and output contact connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": [self.Output]} - + # Test if point given is on contact input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -561,24 +564,24 @@ # Returns the contact type def GetType(self): return self.Type - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the contact properties self.Parent.EditContactContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the contact model def RefreshModel(self, move=True): self.Parent.RefreshContactModel(self) # If contact has moved, refresh the model of wires connected to output if move: self.Output.RefreshWires() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -592,12 +595,12 @@ top = (self.Pos.y - 1) * scaley - 2 width = 4 * scalex + 5 height = (self.Size[1] + 3) * scaley + 5 - + dc.DrawRectangle(left_left, top, width, height) dc.DrawRectangle(right_left, top, width, height) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.setdefault(infos[0], []) @@ -606,13 +609,13 @@ AddHighlight(highlights, (start, end, highlight_type)) else: AddHighlight(highlights, ((0, 0), (0, 1), highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.get(infos[0], []) if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: self.Highlights.pop(infos[0]) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -620,14 +623,14 @@ else: highlight_items = self.Highlights.items() for name, highlights in highlight_items: - highlights = ClearHighlights(highlight, highlight_type) + highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: self.Highlights.pop(name) - + # Draws contact def Draw(self, dc): Graphic_Element.Draw(self, dc) - if self.Value is not None: + if self.Value is not None: if self.Type == CONTACT_NORMAL and self.Value or \ self.Type == CONTACT_REVERSE and not self.Value or \ self.Type == CONTACT_RISING and self.Value and not self.PreviousValue or \ @@ -643,7 +646,7 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + # Compiling contact type modifier symbol typetext = "" if self.Type == CONTACT_REVERSE: @@ -652,7 +655,7 @@ typetext = "P" elif self.Type == CONTACT_FALLING: typetext = "N" - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) if typetext != "": @@ -661,7 +664,7 @@ name_size = self.NameSize if typetext != "": type_size = self.TypeSize - + # Draw two rectangles for representing the contact dc.DrawRectangle(self.Pos.x, self.Pos.y, 2, self.Size[1] + 1) dc.DrawRectangle(self.Pos.x + self.Size[0] - 1, self.Pos.y, 2, self.Size[1] + 1) @@ -677,7 +680,7 @@ # Draw input and output connectors self.Input.Draw(dc) self.Output.Draw(dc) - + if not getattr(dc, "printing", False): for name, highlights in self.Highlights.iteritems(): if name == "reference": @@ -685,18 +688,19 @@ elif typetext != "": DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Ladder Diagram Coil -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a coil -""" +# ------------------------------------------------------------------------------- + class LD_Coil(Graphic_Element): - + """ + Class that implements the graphic representation of a coil + """ + # Create a new coil - def __init__(self, parent, type, name, id = None): + def __init__(self, parent, type, name, id=None): Graphic_Element.__init__(self, parent) self.Type = type self.Name = name @@ -710,7 +714,7 @@ self.PreviousValue = False self.RefreshNameSize() self.RefreshTypeSize() - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -718,7 +722,7 @@ if self.Output is not None: self.Output.Flush() self.Output = None - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value @@ -729,9 +733,9 @@ self.Output.SpreadCurrent(False) if self.Value != self.PreviousValue and self.Visible: self.Parent.ElementNeedRefresh(self) - + # Make a clone of this LD_Coil - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): coil = LD_Coil(parent, self.Type, self.Name, id) coil.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -741,12 +745,12 @@ coil.Input = self.Input.Clone(coil) coil.Output = self.Output.Clone(coil) return coil - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input, self.Output : element.Output} - + return {self.Input: element.Input, self.Output: element.Output} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) @@ -756,34 +760,34 @@ if self.Output.IsConnected(): rect = rect.Union(self.Output.GetConnectedRedrawRect(movex, movey)) return rect - + def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: movex = movey = 0 - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, height_fac=2) + # Forbids to change the Coil size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) self.RefreshConnectors() - + # Delete this coil by calling the appropriate method def Delete(self): self.Parent.DeleteCoil(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for name def RefreshNameSize(self): if self.Name != "": self.NameSize = self.Parent.GetTextExtent(self.Name) else: self.NameSize = 0, 0 - + # Refresh the size of text for type def RefreshTypeSize(self): typetext = "" @@ -801,7 +805,7 @@ self.TypeSize = self.Parent.GetTextExtent(typetext) else: self.TypeSize = 0, 0 - + # Refresh the coil bounding box def RefreshBoundingBox(self): # Calculate the size of the name outside the coil @@ -818,33 +822,33 @@ bbx_y = self.Pos.y bbx_height = self.Size[1] self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Returns the block minimum size def GetMinSize(self): return LD_ELEMENT_SIZE - + # Refresh the position of wire connected to coil - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): self.Input.MoveConnected(exclude) self.Output.MoveConnected(exclude) - - # Returns the coil connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the coil connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector - #if self.Input and name == self.Input.GetName(): + # if self.Input and name == self.Input.GetName(): # return self.Input if self.Output and name == self.Output.GetName(): return self.Output return self.FindNearestConnector(position, [self.Input, self.Output]) - - # Returns input and output coil connectors + + # Returns input and output coil connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": [self.Output]} - + # Test if point given is on coil input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -852,7 +856,7 @@ if self.Output.TestPoint(pt, direction, exclude): return self.Output return None - + # Refresh the positions of the block connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -862,7 +866,7 @@ self.Input.SetPosition(wx.Point(0, position)) self.Output.SetPosition(wx.Point(self.Size[0], position)) self.RefreshConnected() - + # Changes the coil name def SetName(self, name): self.Name = name @@ -871,33 +875,33 @@ # Returns the coil name def GetName(self): return self.Name - + # Changes the coil type def SetType(self, type): self.Type = type self.RefreshTypeSize() - + # Returns the coil type def GetType(self): return self.Type - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the coil properties self.Parent.EditCoilContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the coil model def RefreshModel(self, move=True): self.Parent.RefreshCoilModel(self) # If coil has moved, refresh the model of wires connected to output if move: self.Output.RefreshWires() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -906,19 +910,19 @@ dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetLogicalFunction(wx.AND) # Draw a two circle arcs for representing the coil - dc.DrawEllipticArc(round(self.Pos.x * scalex), - round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), - round(self.Size[0] * scalex), + dc.DrawEllipticArc(round(self.Pos.x * scalex), + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), + round(self.Size[0] * scalex), round((int(self.Size[1] * sqrt(2)) - 1) * scaley), 135, 225) - dc.DrawEllipticArc(round(self.Pos.x * scalex), - round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), - round(self.Size[0] * scalex), + dc.DrawEllipticArc(round(self.Pos.x * scalex), + round((self.Pos.y - int(self.Size[1] * (sqrt(2) - 1.) / 2.) + 1) * scaley), + round(self.Size[0] * scalex), round((int(self.Size[1] * sqrt(2)) - 1) * scaley), -45, 45) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.setdefault(infos[0], []) @@ -927,13 +931,13 @@ AddHighlight(highlights, (start, end, highlight_type)) else: AddHighlight(highlights, ((0, 0), (0, 1), highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): highlights = self.Highlights.get(infos[0], []) if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: self.Highlights.pop(infos[0]) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -941,10 +945,10 @@ else: highlight_items = self.Highlights.items() for name, highlights in highlight_items: - highlights = ClearHighlights(highlight, highlight_type) + highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: self.Highlights.pop(name) - + # Draws coil def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -953,8 +957,8 @@ else: dc.SetPen(MiterPen(wx.BLACK, 2, wx.SOLID)) dc.SetBrush(wx.TRANSPARENT_BRUSH) - - # Compiling coil type modifier symbol + + # Compiling coil type modifier symbol typetext = "" if self.Type == COIL_REVERSE: typetext = "/" @@ -966,7 +970,7 @@ typetext = "P" elif self.Type == COIL_FALLING: typetext = "N" - + if getattr(dc, "printing", False) and not isinstance(dc, wx.PostScriptDC): # Draw an clipped ellipse for representing the coil clipping_box = dc.GetClippingBox() @@ -992,7 +996,7 @@ name_size = self.NameSize if typetext != "": type_size = self.TypeSize - + # Draw coil name name_pos = (self.Pos.x + (self.Size[0] - name_size[0]) / 2, self.Pos.y - (name_size[1] + 2)) @@ -1012,5 +1016,3 @@ DrawHighlightedText(dc, self.Name, highlights, name_pos[0], name_pos[1]) elif typetext != "": DrawHighlightedText(dc, typetext, highlights, type_pos[0], type_pos[1]) - - diff -r c1298e7ffe3a -r 8391c11477f4 graphics/RubberBand.py --- a/graphics/RubberBand.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/RubberBand.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,32 +22,35 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.GraphicCommons import GetScaledEventPosition -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Viewer RubberBand -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements a rubberband for graphic Viewers -""" -class RubberBand: - +class RubberBand(object): + """ + Class that implements a rubberband for graphic Viewers + """ + def __init__(self, viewer): """ Constructor @param viewer: Viewer on which rubberband must be drawn """ self.Viewer = viewer - + # wx.Panel on which rubberband will be drawn self.DrawingSurface = viewer.Editor - + self.Reset() - + def Reset(self): """ Initialize internal attributes of rubberband @@ -55,14 +58,14 @@ self.StartPoint = None self.CurrentBBox = None self.LastBBox = None - + def IsShown(self): """ Indicate if rubberband is drawn on viewer @return: True if rubberband is drawn """ - return self.CurrentBBox != None - + return self.CurrentBBox is not None + def GetCurrentExtent(self): """ Return the rubberband bounding box @@ -73,7 +76,7 @@ if self.IsShown(): return self.CurrentBBox return self.LastBBox - + def OnLeftDown(self, event, dc, scaling): """ Called when left mouse is pressed on Viewer. Starts to edit a new @@ -85,16 +88,16 @@ # Save the point where mouse was pressed in Viewer unit, position may # be modified by scroll and zoom applied on viewer self.StartPoint = GetScaledEventPosition(event, dc, scaling) - + # Initialize rubberband bounding box self.CurrentBBox = wx.Rect(self.StartPoint.x, self.StartPoint.y, 0, 0) - + # Change viewer mouse cursor to reflect a rubberband bounding box is # edited self.DrawingSurface.SetCursor(wx.StockCursor(wx.CURSOR_CROSS)) - + self.Redraw() - + def OnMotion(self, event, dc, scaling): """ Called when mouse is dragging over Viewer. Update the current edited @@ -106,19 +109,19 @@ # Get mouse position in Viewer unit, position may be modified by scroll # and zoom applied on viewer pos = GetScaledEventPosition(event, dc, scaling) - + # Save the last bounding box drawn for erasing it later self.LastBBox = wx.Rect(0, 0, 0, 0) self.LastBBox.Union(self.CurrentBBox) - - # Calculate new position and size of the box + + # Calculate new position and size of the box self.CurrentBBox.x = min(pos.x, self.StartPoint.x) self.CurrentBBox.y = min(pos.y, self.StartPoint.y) self.CurrentBBox.width = abs(pos.x - self.StartPoint.x) + 1 self.CurrentBBox.height = abs(pos.y - self.StartPoint.y) + 1 - + self.Redraw() - + def OnLeftUp(self, event, dc, scaling): """ Called when mouse is release from Viewer. Erase the current edited @@ -129,16 +132,16 @@ """ # Change viewer mouse cursor to default self.DrawingSurface.SetCursor(wx.NullCursor) - + # Save the last edited bounding box self.LastBBox = self.CurrentBBox self.CurrentBBox = None - + self.Redraw() - + def DrawBoundingBoxes(self, bboxes, dc=None): """ - Draw a list of bounding box on Viewer in the order given using XOR + Draw a list of bounding box on Viewer in the order given using XOR logical function @param bboxes: List of bounding boxes to draw on viewer @param dc: Device Context of Viewer (default None) @@ -146,38 +149,38 @@ # Get viewer Device Context if not given if dc is None: dc = self.Viewer.GetLogicalDC() - + # Save current viewer scale factors before resetting them in order to # avoid rubberband pen to be scaled scalex, scaley = dc.GetUserScale() dc.SetUserScale(1, 1) - + # Set DC drawing style dc.SetPen(wx.Pen(wx.WHITE, style=wx.DOT)) dc.SetBrush(wx.TRANSPARENT_BRUSH) dc.SetLogicalFunction(wx.XOR) - + # Draw the bounding boxes using viewer scale factor for bbox in bboxes: if bbox is not None: dc.DrawRectangle( - bbox.x * scalex, bbox.y * scaley, + bbox.x * scalex, bbox.y * scaley, bbox.width * scalex, bbox.height * scaley) - + dc.SetLogicalFunction(wx.COPY) - + # Restore Viewer scale factor dc.SetUserScale(scalex, scaley) - - def Redraw(self, dc = None): + + def Redraw(self, dc=None): """ Redraw rubberband on Viewer @param dc: Device Context of Viewer (default None) """ # Erase last bbox and draw current bbox self.DrawBoundingBoxes([self.LastBBox, self.CurrentBBox], dc) - - def Erase(self, dc = None): + + def Erase(self, dc=None): """ Erase rubberband from Viewer @param dc: Device Context of Viewer (default None) @@ -185,7 +188,7 @@ # Erase last bbox self.DrawBoundingBoxes([self.LastBBox], dc) - def Draw(self, dc = None): + def Draw(self, dc=None): """ Draw rubberband on Viewer @param dc: Device Context of Viewer (default None) diff -r c1298e7ffe3a -r 8391c11477f4 graphics/SFC_Objects.py --- a/graphics/SFC_Objects.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/SFC_Objects.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,30 +22,33 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from graphics.GraphicCommons import * from graphics.DebugDataConsumer import DebugDataConsumer from plcopen.structures import * + def GetWireSize(block): if isinstance(block, SFC_Step): return SFC_WIRE_MIN_SIZE + block.GetActionExtraLineNumber() * SFC_ACTION_MIN_SIZE[1] else: return SFC_WIRE_MIN_SIZE -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Sequencial Function Chart Step -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a step -""" +# ------------------------------------------------------------------------------- + class SFC_Step(Graphic_Element, DebugDataConsumer): - + """ + Class that implements the graphic representation of a step + """ + # Create a new step - def __init__(self, parent, name, initial = False, id = None): + def __init__(self, parent, name, initial=False, id=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.SetName(name) @@ -62,7 +65,7 @@ self.Action = None self.PreviousValue = None self.PreviousSpreading = False - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -70,16 +73,16 @@ if self.Output is not None: self.Output.Flush() self.Output = None - if self.Output is not None: + if self.Action is not None: self.Action.Flush() self.Action = None - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def SetValue(self, value): self.PreviousValue = self.Value self.Value = value @@ -87,7 +90,7 @@ if self.Visible: self.Parent.ElementNeedRefresh(self) self.SpreadCurrent() - + def SpreadCurrent(self): if self.Parent.Debug: spreading = self.Value @@ -102,9 +105,9 @@ if self.Action is not None: self.Action.SpreadCurrent(False) self.PreviousSpreading = spreading - + # Make a clone of this SFC_Step - def Clone(self, parent, id = None, name = "Step", pos = None): + def Clone(self, parent, id=None, name="Step", pos=None): step = SFC_Step(parent, name, self.Initial, id) step.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -118,7 +121,7 @@ if self.Action: step.Action = self.Action.Clone(step) return step - + def GetConnectorTranslation(self, element): connectors = {} if self.Input is not None: @@ -128,9 +131,9 @@ if self.Action is not None: connectors[self.Action] = element.Action return connectors - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if self.Input: rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) @@ -146,83 +149,85 @@ if self.Action and self.Action.IsConnected(): rect = rect.Union(self.Action.GetConnectedRedrawRect(movex, movey)) return rect - + # Delete this step by calling the appropriate method def Delete(self): self.Parent.DeleteStep(self) - + # Unconnect input and output def Clean(self): if self.Input: - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) if self.Output: - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) if self.Action: - self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Action.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for name def RefreshNameSize(self): self.NameSize = self.Parent.GetTextExtent(self.Name) - + # Add output connector to step def AddInput(self): if not self.Input: self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH) self.RefreshBoundingBox() - + # Remove output connector from step def RemoveInput(self): if self.Input: - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Input = None self.RefreshBoundingBox() - + # Add output connector to step def AddOutput(self): if not self.Output: - self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True) + self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True) self.RefreshBoundingBox() - + # Remove output connector from step def RemoveOutput(self): if self.Output: - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Output = None self.RefreshBoundingBox() - + # Add action connector to step def AddAction(self): if not self.Action: - self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone = True) + self.Action = Connector(self, "", None, wx.Point(self.Size[0], self.Size[1] / 2), EAST, onlyone=True) self.RefreshBoundingBox() - + # Remove action connector from step def RemoveAction(self): if self.Action: - self.Action.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Action.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Action = None self.RefreshBoundingBox() - + # Refresh the step bounding box def RefreshBoundingBox(self): + # TODO: check and remove dead coded + # # Calculate the bounding box size - if self.Action: - bbx_width = self.Size[0] + CONNECTOR_SIZE - else: - bbx_width = self.Size[0] - if self.Initial: - bbx_y = self.Pos.y - bbx_height = self.Size[1] - if self.Output: - bbx_height += CONNECTOR_SIZE - else: - bbx_y = self.Pos.y - CONNECTOR_SIZE - bbx_height = self.Size[1] + CONNECTOR_SIZE - if self.Output: - bbx_height += CONNECTOR_SIZE - #self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1) + # if self.Action: + # bbx_width = self.Size[0] + CONNECTOR_SIZE + # else: + # bbx_width = self.Size[0] + # if self.Initial: + # bbx_y = self.Pos.y + # bbx_height = self.Size[1] + # if self.Output: + # bbx_height += CONNECTOR_SIZE + # else: + # bbx_y = self.Pos.y - CONNECTOR_SIZE + # bbx_height = self.Size[1] + CONNECTOR_SIZE + # if self.Output: + # bbx_height += CONNECTOR_SIZE + # self.BoundingBox = wx.Rect(self.Pos.x, bbx_y, bbx_width + 1, bbx_height + 1) self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Refresh the positions of the step connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -241,22 +246,22 @@ if self.Action: self.Action.SetPosition(wx.Point(self.Size[0], vertical_pos)) self.RefreshConnected() - + # Refresh the position of wires connected to step - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): if self.Input: self.Input.MoveConnected(exclude) if self.Output: self.Output.MoveConnected(exclude) if self.Action: self.Action.MoveConnected(exclude) - - # Returns the step connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the step connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input, output and action connector if they exists - #if self.Input and name == self.Input.GetName(): + # if self.Input and name == self.Input.GetName(): # return self.Input if self.Output and name == self.Output.GetName(): return self.Output @@ -273,12 +278,12 @@ if self.Action: connectors.append(self.Action) return self.FindNearestConnector(position, connectors) - - # Returns action step connector + + # Returns action step connector def GetActionConnector(self): return self.Action - - # Returns input and output step connectors + + # Returns input and output step connectors def GetConnectors(self): connectors = {"inputs": [], "outputs": []} if self.Input: @@ -286,9 +291,9 @@ if self.Output: connectors["outputs"].append(self.Output) return connectors - + # Test if point given is on step input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if it exists if self.Input and self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -312,7 +317,7 @@ # Returns the step initial property def GetInitial(self): return self.Initial - + # Returns the connector connected to input def GetPreviousConnector(self): if self.Input: @@ -320,7 +325,7 @@ if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Input) return None - + # Returns the connector connected to output def GetNextConnector(self): if self.Output: @@ -328,7 +333,7 @@ if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Output) return None - + # Returns the connector connected to action def GetActionConnected(self): if self.Action: @@ -336,7 +341,7 @@ if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Action) return None - + # Returns the number of action line def GetActionExtraLineNumber(self): if self.Action: @@ -346,7 +351,7 @@ action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock() return max(0, action_block.GetLineNumber() - 1) return 0 - + # Returns the step minimum size def GetMinSize(self): text_width, text_height = self.Parent.GetTextExtent(self.Name) @@ -354,7 +359,7 @@ return text_width + 14, text_height + 14 else: return text_width + 10, text_height + 10 - + # Updates the step size def UpdateSize(self, width, height): diffx = self.Size.GetWidth() / 2 - width / 2 @@ -365,7 +370,7 @@ self.RefreshConnected() else: self.RefreshOutputPosition((0, diffy)) - + # Align input element with this step def RefreshInputPosition(self): if self.Input: @@ -382,9 +387,9 @@ input_block.MoveActionBlock((diffx, 0)) input_block.Move(diffx, 0) input_block.RefreshInputPosition() - + # Align output element with this step - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): if self.Output: wires = self.Output.GetWires() if len(wires) != 1: @@ -400,7 +405,7 @@ if isinstance(output_block, SFC_Step): output_block.MoveActionBlock((diffx, diffy)) wires[0][0].SetPoints([wx.Point(current_pos.x, current_pos.y + wire_size), - wx.Point(current_pos.x, current_pos.y)]) + wx.Point(current_pos.x, current_pos.y)]) if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: output_block.Move(diffx, diffy, self.Parent.Wires) output_block.RefreshOutputPosition((diffx, diffy)) @@ -422,7 +427,7 @@ output_block.MoveActionBlock((diffx, 0)) output_block.Move(diffx, 0) output_block.RefreshOutputPosition() - + # Refresh action element with this step def MoveActionBlock(self, move): if self.Action: @@ -432,34 +437,33 @@ action_block = wires[0][0].GetOtherConnected(self.Action).GetParentBlock() action_block.Move(move[0], move[1], self.Parent.Wires) wires[0][0].Move(move[0], move[1], True) - + # Resize the divergence from position and size given def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: self.UpdateSize(width, height) else: Graphic_Element.Resize(self, x, y, width, height) - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the step properties self.Parent.EditStepContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the menu with special items for a step self.Parent.PopupDefaultMenu() - + # Refreshes the step state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): - handle_type, handle = self.Handle + handle_type, _handle = self.Handle if handle_type == HANDLE_MOVE: movex = max(-self.BoundingBox.x, movex) movey = max(-self.BoundingBox.y, movey) if scaling is not None: movex = round(float(self.Pos.x + movex) / float(scaling[0])) * scaling[0] - self.Pos.x movey = round(float(self.Pos.y + movey) / float(scaling[1])) * scaling[1] - self.Pos.y - action_block = None if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: self.Move(movex, movey) self.RefreshConnected() @@ -477,17 +481,17 @@ return movex, 0 else: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - + # Refresh input element model def RefreshInputModel(self): if self.Input: input = self.GetPreviousConnector() - if input: + if input: input_block = input.GetParentBlock() input_block.RefreshModel(False) if not isinstance(input_block, SFC_Divergence): input_block.RefreshInputModel() - + # Refresh output element model def RefreshOutputModel(self, move=False): if self.Output: @@ -497,7 +501,7 @@ output_block.RefreshModel(False) if not isinstance(output_block, SFC_Divergence) or move: output_block.RefreshOutputModel(move) - + # Refreshes the step model def RefreshModel(self, move=True): self.Parent.RefreshStepModel(self) @@ -513,21 +517,21 @@ self.RefreshOutputModel(self.Initial) elif self.Output: self.Output.RefreshWires() - + # Adds an highlight to the connection def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "name" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the connection def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "name": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the connection def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws step def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -541,12 +545,12 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.WHITE_BRUSH) - + if getattr(dc, "printing", False): name_size = dc.GetTextExtent(self.Name) else: name_size = self.NameSize - + # Draw two rectangles for representing the step dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) if self.Initial: @@ -562,23 +566,23 @@ self.Output.Draw(dc) if self.Action: self.Action.Draw(dc) - + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Name, self.Highlights, name_pos[0], name_pos[1]) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Transition -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a transition -""" +# ------------------------------------------------------------------------------- + class SFC_Transition(Graphic_Element, DebugDataConsumer): - + """ + Class that implements the graphic representation of a transition + """ + # Create a new transition - def __init__(self, parent, type = "reference", condition = None, priority = 0, id = None): + def __init__(self, parent, type="reference", condition=None, priority=0, id=None): Graphic_Element.__init__(self, parent) DebugDataConsumer.__init__(self) self.Type = None @@ -586,14 +590,14 @@ self.Priority = 0 self.Size = wx.Size(SFC_TRANSITION_SIZE[0], SFC_TRANSITION_SIZE[1]) # Create an input and output connector - self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True) - self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True) + self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True) + self.Output = Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True) self.SetType(type, condition) self.SetPriority(priority) self.Highlights = {} self.PreviousValue = None self.PreviousSpreading = False - + def Flush(self): if self.Input is not None: self.Input.Flush() @@ -604,13 +608,13 @@ if self.Type == "connection" and self.Condition is not None: self.Condition.Flush() self.Condition = None - + def SetForced(self, forced): if self.Forced != forced: self.Forced = forced if self.Visible: self.Parent.ElementNeedRefresh(self) - + def SetValue(self, value): self.PreviousValue = self.Value self.Value = value @@ -618,7 +622,7 @@ if self.Visible: self.Parent.ElementNeedRefresh(self) self.SpreadCurrent() - + def SpreadCurrent(self): if self.Parent.Debug: if self.Value is None: @@ -629,9 +633,9 @@ elif not spreading and self.PreviousSpreading: self.Output.SpreadCurrent(False) self.PreviousSpreading = spreading - + # Make a clone of this SFC_Transition - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): transition = SFC_Transition(parent, self.Type, self.Condition, self.Priority, id) transition.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -643,18 +647,20 @@ if self.Type == "connection": transition.Condition = self.Condition.Clone(transition) return transition - + def GetConnectorTranslation(self, element): - connectors = {self.Input : element.Input, self.Output : element.Output} + connectors = {self.Input: element.Input, self.Output: element.Output} if self.Type == "connection" and self.Condition is not None: connectors[self.Condition] = element.Condition return connectors - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) - rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) - rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) + if self.Input: + rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) + if self.Output: + rect = rect.Union(self.Output.GetRedrawRect(movex, movey)) if movex != 0 or movey != 0: if self.Input.IsConnected(): rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) @@ -663,17 +669,17 @@ if self.Type == "connection" and self.Condition.IsConnected(): rect = rect.Union(self.Condition.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to change the transition size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) - + # Forbids to resize the transition def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.Resize(self, x, y, width, height) - + # Refresh the size of text for name def RefreshConditionSize(self): if self.Type != "connection": @@ -681,7 +687,7 @@ self.ConditionSize = self.Parent.GetTextExtent(self.Condition) else: self.ConditionSize = self.Parent.GetTextExtent("Transition") - + # Refresh the size of text for name def RefreshPrioritySize(self): if self.Priority != "": @@ -692,14 +698,14 @@ # Delete this transition by calling the appropriate method def Delete(self): self.Parent.DeleteTransition(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - self.Output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) if self.Type == "connection": - self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Condition.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): if self.Type != "connection": @@ -713,7 +719,7 @@ else: test_text = False return test_text or Graphic_Element.HitTest(self, pt, connectors) - + # Refresh the transition bounding box def RefreshBoundingBox(self): bbx_x, bbx_y, bbx_width, bbx_height = self.Pos.x, self.Pos.y, self.Size[0], self.Size[1] @@ -731,21 +737,21 @@ bbx_y = min(bbx_y, self.Pos.y - max(0, (text_height - self.Size[1]) / 2)) bbx_height = max(bbx_height, self.Pos.y - bbx_y + (self.Size[1] + text_height) / 2) self.BoundingBox = wx.Rect(bbx_x, bbx_y, bbx_width + 1, bbx_height + 1) - + # Returns the connector connected to input def GetPreviousConnector(self): wires = self.Input.GetWires() if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Input) return None - + # Returns the connector connected to output def GetNextConnector(self): wires = self.Output.GetWires() if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Output) return None - + # Refresh the positions of the transition connectors def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -761,20 +767,20 @@ if self.Type == "connection": self.Condition.SetPosition(wx.Point(0, vertical_pos)) self.RefreshConnected() - + # Refresh the position of the wires connected to transition - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): self.Input.MoveConnected(exclude) self.Output.MoveConnected(exclude) if self.Type == "connection": self.Condition.MoveConnected(exclude) - - # Returns the transition connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the transition connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test input and output connector - #if name == self.Input.GetName(): + # if name == self.Input.GetName(): # return self.Input if name == self.Output.GetName(): return self.Output @@ -784,19 +790,19 @@ if self.Type == "connection": connectors.append(self.Condition) return self.FindNearestConnector(position, connectors) - + # Returns the transition condition connector def GetConditionConnector(self): if self.Type == "connection": return self.Condition return None - + # Returns input and output transition connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": [self.Output]} - + # Test if point given is on transition input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input @@ -809,25 +815,25 @@ return None # Changes the transition type - def SetType(self, type, condition = None): + def SetType(self, type, condition=None): if self.Type != type: if self.Type == "connection": - self.Condition.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + self.Condition.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) self.Type = type if type == "connection": self.Condition = Connector(self, "", "BOOL", wx.Point(0, self.Size[1] / 2), WEST) else: - if condition == None: + if condition is None: condition = "" self.Condition = condition self.RefreshConditionSize() elif self.Type != "connection": - if condition == None: + if condition is None: condition = "" self.Condition = condition self.RefreshConditionSize() self.RefreshBoundingBox() - + # Returns the transition type def GetType(self): return self.Type @@ -837,7 +843,7 @@ self.Priority = priority self.RefreshPrioritySize() self.RefreshBoundingBox() - + # Returns the transition type def GetPriority(self): return self.Priority @@ -847,14 +853,13 @@ if self.Type != "connection": return self.Condition return None - + # Returns the transition minimum size def GetMinSize(self): return SFC_TRANSITION_SIZE - + # Align input element with this step def RefreshInputPosition(self): - wires = self.Input.GetWires() current_pos = self.Input.GetPosition(False) input = self.GetPreviousConnector() if input: @@ -868,9 +873,9 @@ input_block.MoveActionBlock((diffx, 0)) input_block.Move(diffx, 0) input_block.RefreshInputPosition() - + # Align output element with this step - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): wires = self.Output.GetWires() if len(wires) != 1: return @@ -900,12 +905,12 @@ def OnLeftDClick(self, event, dc, scaling): # Edit the transition properties self.Parent.EditTransitionContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the menu with special items for a step self.Parent.PopupDefaultMenu() - + # Refreshes the transition state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -917,8 +922,8 @@ self.RefreshOutputPosition() return movex, 0 else: - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2, height_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac=2, height_fac=2) + # Refresh input element model def RefreshInputModel(self): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -928,7 +933,7 @@ input_block.RefreshModel(False) if not isinstance(input_block, SFC_Divergence): input_block.RefreshInputModel() - + # Refresh output element model def RefreshOutputModel(self, move=False): output = self.GetNextConnector() @@ -937,7 +942,7 @@ output_block.RefreshModel(False) if not isinstance(output_block, SFC_Divergence) or move: output_block.RefreshOutputModel(move) - + # Refreshes the transition model def RefreshModel(self, move=True): self.Parent.RefreshTransitionModel(self) @@ -948,20 +953,20 @@ self.RefreshOutputModel() else: self.Output.RefreshWires() - + # Adds an highlight to the block - def AddHighlight(self, infos, start, end ,highlight_type): + def AddHighlight(self, infos, start, end, highlight_type): if infos[0] in ["reference", "inline", "priority"] and start[0] == 0 and end[0] == 0: highlights = self.Highlights.setdefault(infos[0], []) AddHighlight(highlights, (start, end, highlight_type)) - + # Removes an highlight from the block def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] in ["reference", "inline", "priority"]: highlights = self.Highlights.get(infos[0], []) if RemoveHighlight(highlights, (start, end, highlight_type)) and len(highlights) == 0: self.Highlights.pop(infos[0]) - + # Removes all the highlights of one particular type from the block def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -969,10 +974,10 @@ else: highlight_items = self.Highlights.items() for name, highlights in highlight_items: - highlights = ClearHighlights(highlight, highlight_type) + highlights = ClearHighlights(highlights, highlight_type) if len(highlights) == 0: self.Highlights.pop(name) - + # Draws transition def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -989,7 +994,7 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + if getattr(dc, "printing", False): if self.Type != "connection": condition_size = dc.GetTextExtent(self.Condition) @@ -1000,14 +1005,14 @@ condition_size = self.ConditionSize if self.Priority != 0: priority_size = self.PrioritySize - + # Draw plain rectangle for representing the transition - dc.DrawRectangle(self.Pos.x, - self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, + dc.DrawRectangle(self.Pos.x, + self.Pos.y + (self.Size[1] - SFC_TRANSITION_SIZE[1])/2, self.Size[0] + 1, SFC_TRANSITION_SIZE[1] + 1) vertical_line_x = self.Input.GetPosition()[0] - dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) + dc.DrawLine(vertical_line_x, self.Pos.y, vertical_line_x, self.Pos.y + self.Size[1] + 1) # Draw transition condition if self.Type != "connection": if self.Condition != "": @@ -1026,7 +1031,7 @@ self.Output.Draw(dc) if self.Type == "connection": self.Condition.Draw(dc) - + if not getattr(dc, "printing", False): for name, highlights in self.Highlights.iteritems(): if name == "priority": @@ -1034,19 +1039,20 @@ else: DrawHighlightedText(dc, condition, highlights, condition_pos[0], condition_pos[1]) -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Divergence and Convergence -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a divergence or convergence, -selection or simultaneous -""" +# ------------------------------------------------------------------------------- + class SFC_Divergence(Graphic_Element): - + """ + Class that implements the graphic representation of a divergence or convergence, + selection or simultaneous + """ + # Create a new divergence - def __init__(self, parent, type, number = 2, id = None): + def __init__(self, parent, type, number=2, id=None): Graphic_Element.__init__(self, parent) self.Type = type self.Id = id @@ -1055,18 +1061,18 @@ self.Size = wx.Size((number - 1) * SFC_DEFAULT_SEQUENCE_INTERVAL, self.GetMinSize()[1]) # Create an input and output connector if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: - self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True)] + self.Inputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True)] self.Outputs = [] for i in xrange(number): - self.Outputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone = True)) + self.Outputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone=True)) elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: self.Inputs = [] for i in xrange(number): - self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True)) - self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone = True)] + self.Inputs.append(Connector(self, "", None, wx.Point(i * SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone=True)) + self.Outputs = [Connector(self, "", None, wx.Point(self.Size[0] / 2, self.Size[1]), SOUTH, onlyone=True)] self.Value = None self.PreviousValue = None - + def Flush(self): for input in self.Inputs: input.Flush() @@ -1074,7 +1080,7 @@ for output in self.Outputs: output.Flush() self.Outputs = [] - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value @@ -1100,9 +1106,9 @@ self.Parent.ElementNeedRefresh(self) for output in self.Outputs: output.SpreadCurrent(False) - + # Make a clone of this SFC_Divergence - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): divergence = SFC_Divergence(parent, self.Type, max(len(self.Inputs), len(self.Outputs)), id) divergence.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -1112,12 +1118,12 @@ divergence.Inputs = [input.Clone(divergence) for input in self.Inputs] divergence.Outputs = [output.Clone(divergence) for output in self.Outputs] return divergence - + def GetConnectorTranslation(self, element): return dict(zip(self.Inputs + self.Outputs, element.Inputs + element.Outputs)) - + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) if movex != 0 or movey != 0: for input in self.Inputs: @@ -1127,27 +1133,27 @@ if output.IsConnected(): rect = rect.Union(output.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to resize the divergence def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.Resize(self, x, 0, width, self.GetMinSize()[1]) - + # Delete this divergence by calling the appropriate method def Delete(self): self.Parent.DeleteDivergence(self) - + # Returns the divergence type def GetType(self): return self.Type - + # Unconnect input and output def Clean(self): for input in self.Inputs: - input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) for output in self.Outputs: - output.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + output.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Add a branch to the divergence def AddBranch(self): if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: @@ -1155,7 +1161,7 @@ for output in self.Outputs: pos = output.GetRelPosition() maxx = max(maxx, pos.x) - connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone = True) + connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, self.Size[1]), SOUTH, onlyone=True) self.Outputs.append(connector) self.MoveConnector(connector, 0) elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: @@ -1163,10 +1169,10 @@ for input in self.Inputs: pos = input.GetRelPosition() maxx = max(maxx, pos.x) - connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone = True) + connector = Connector(self, "", None, wx.Point(maxx + SFC_DEFAULT_SEQUENCE_INTERVAL, 0), NORTH, onlyone=True) self.Inputs.append(connector) self.MoveConnector(connector, SFC_DEFAULT_SEQUENCE_INTERVAL) - + # Remove a branch from the divergence def RemoveBranch(self, connector): if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: @@ -1177,41 +1183,42 @@ if connector in self.Inputs and len(self.Inputs) > 2: self.Inputs.remove(connector) self.MoveConnector(self.Inputs[0], 0) - + # Remove the handled branch from the divergence def RemoveHandledBranch(self): handle_type, handle = self.Handle if handle_type == HANDLE_CONNECTOR: handle.UnConnect(delete=True) self.RemoveBranch(handle) - + # Return the number of branches for the divergence def GetBranchNumber(self): if self.Type in [SELECTION_DIVERGENCE, SIMULTANEOUS_DIVERGENCE]: return len(self.Outputs) elif self.Type in [SELECTION_CONVERGENCE, SIMULTANEOUS_CONVERGENCE]: return len(self.Inputs) - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): - return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) != None - + return self.BoundingBox.InsideXY(pt.x, pt.y) or self.TestConnector(pt, exclude=False) is not None + # Refresh the divergence bounding box def RefreshBoundingBox(self): if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, - self.Size[0] + 1, self.Size[1] + 1) + self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, + self.Size[0] + 1, self.Size[1] + 1) elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: - self.BoundingBox = wx.Rect(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, + self.BoundingBox = wx.Rect( + self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, self.Size[0] + 2 * SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Size[1] + 1) - + # Refresh the position of wires connected to divergence - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): for input in self.Inputs: input.MoveConnected(exclude) for output in self.Outputs: output.MoveConnected(exclude) - + # Moves the divergence connector given def MoveConnector(self, connector, movex): position = connector.GetRelPosition() @@ -1239,26 +1246,26 @@ self.Size[0] = maxx - minx connector.MoveConnected() self.RefreshBoundingBox() - - # Returns the divergence connector that starts with the point given if it exists - def GetConnector(self, position, name = None): + + # Returns the divergence connector that starts with the point given if it exists + def GetConnector(self, position, name=None): # if a name is given if name is not None: # Test each input and output connector - #for input in self.Inputs: + # for input in self.Inputs: # if name == input.GetName(): # return input for output in self.Outputs: if name == output.GetName(): return output return self.FindNearestConnector(position, self.Inputs + self.Outputs) - - # Returns input and output divergence connectors + + # Returns input and output divergence connectors def GetConnectors(self): return {"inputs": self.Inputs, "outputs": self.Outputs} - + # Test if point given is on divergence input or output connector - def TestConnector(self, pt, direction = None, exclude=True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector for input in self.Inputs: if input.TestPoint(pt, direction, exclude): @@ -1268,7 +1275,7 @@ if output.TestPoint(pt, direction, exclude): return output return None - + # Changes the divergence size def SetSize(self, width, height): height = self.GetMinSize()[1] @@ -1288,7 +1295,7 @@ output.MoveConnected() self.Size = wx.Size(width, height) self.RefreshBoundingBox() - + # Returns the divergence minimum size def GetMinSize(self, default=False): width = 0 @@ -1302,7 +1309,7 @@ elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: return width, 3 return 0, 0 - + # Refresh the position of the block connected to connector def RefreshConnectedPosition(self, connector): wires = connector.GetWires() @@ -1338,18 +1345,15 @@ self.RefreshOutputPosition((0, diffy)) for input in self.Inputs: input.MoveConnected() - + # Align output element with this divergence - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): if move: for output_connector in self.Outputs: wires = output_connector.GetWires() if len(wires) != 1: return - current_pos = output_connector.GetPosition(False) output = wires[0][0].GetOtherConnected(self.Output) - output_pos = output.GetPosition(False) - diffx = current_pos.x - output_pos.x output_block = output.GetParentBlock() if isinstance(output_block, SFC_Step): output_block.MoveActionBlock(move) @@ -1357,10 +1361,10 @@ if not isinstance(output_block, SFC_Divergence) or output_block.GetConnectors()["inputs"].index(output) == 0: output_block.Move(move[0], move[1], self.Parent.Wires) output_block.RefreshOutputPosition(move) - + # Method called when a LeftDown event have been generated def OnLeftDown(self, event, dc, scaling): - self.RealConnectors = {"Inputs":[],"Outputs":[]} + self.RealConnectors = {"Inputs": [], "Outputs": []} for input in self.Inputs: position = input.GetRelPosition() self.RealConnectors["Inputs"].append(float(position.x)/float(self.Size[0])) @@ -1368,12 +1372,12 @@ position = output.GetRelPosition() self.RealConnectors["Outputs"].append(float(position.x)/float(self.Size[0])) Graphic_Element.OnLeftDown(self, event, dc, scaling) - + # Method called when a LeftUp event have been generated def OnLeftUp(self, event, dc, scaling): Graphic_Element.OnLeftUp(self, event, dc, scaling) self.RealConnectors = None - + # Method called when a RightDown event have been generated def OnRightDown(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -1387,7 +1391,7 @@ self.oldPos = GetScaledEventPosition(event, dc, scaling) else: Graphic_Element.OnRightDown(self, event, dc, scaling) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): pos = GetScaledEventPosition(event, dc, scaling) @@ -1412,7 +1416,7 @@ else: # Popup the divergence menu without delete branch self.Parent.PopupDivergenceMenu(False) - + # Refreshes the divergence state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): handle_type, handle = self.Handle @@ -1428,7 +1432,7 @@ elif self.Parent.GetDrawingMode() == FREEDRAWING_MODE: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) return 0, 0 - + # Refresh output element model def RefreshOutputModel(self, move=False): if move and self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1440,7 +1444,7 @@ output_block.RefreshModel(False) if not isinstance(output_block, SFC_Divergence) or move: output_block.RefreshOutputModel(move) - + # Refreshes the divergence model def RefreshModel(self, move=True): self.Parent.RefreshDivergenceModel(self) @@ -1451,7 +1455,7 @@ else: for output in self.Outputs: output.RefreshWires() - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -1465,13 +1469,13 @@ if self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: posx -= SFC_SIMULTANEOUS_SEQUENCE_EXTRA width += SFC_SIMULTANEOUS_SEQUENCE_EXTRA * 2 - dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, - int(round((self.Pos.y - 1) * scaley)) - 2, - int(round((width + 3) * scalex)) + 5, + dc.DrawRectangle(int(round((posx - 1) * scalex)) - 2, + int(round((self.Pos.y - 1) * scaley)) - 2, + int(round((width + 3) * scalex)) + 5, int(round((self.Size.height + 3) * scaley)) + 5) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws divergence def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -1485,53 +1489,53 @@ if self.Type in [SELECTION_DIVERGENCE, SELECTION_CONVERGENCE]: dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) elif self.Type in [SIMULTANEOUS_DIVERGENCE, SIMULTANEOUS_CONVERGENCE]: - dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, + dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y, self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y) - dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], + dc.DrawLine(self.Pos.x - SFC_SIMULTANEOUS_SEQUENCE_EXTRA, self.Pos.y + self.Size[1], self.Pos.x + self.Size[0] + SFC_SIMULTANEOUS_SEQUENCE_EXTRA + 1, self.Pos.y + self.Size[1]) # Draw inputs and outputs connectors for input in self.Inputs: input.Draw(dc) for output in self.Outputs: output.Draw(dc) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Jump to Step -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of a jump to step -""" +# ------------------------------------------------------------------------------- + class SFC_Jump(Graphic_Element): - + """ + Class that implements the graphic representation of a jump to step + """ + # Create a new jump - def __init__(self, parent, target, id = None): + def __init__(self, parent, target, id=None): Graphic_Element.__init__(self, parent) self.SetTarget(target) self.Id = id self.Size = wx.Size(SFC_JUMP_SIZE[0], SFC_JUMP_SIZE[1]) self.Highlights = [] # Create an input and output connector - self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone = True) + self.Input = Connector(self, "", None, wx.Point(self.Size[0] / 2, 0), NORTH, onlyone=True) self.Value = None self.PreviousValue = None - + def Flush(self): if self.Input is not None: self.Input.Flush() self.Input = None - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value self.Value = self.Input.ReceivingCurrent() if self.Value != self.PreviousValue and self.Visible: self.Parent.ElementNeedRefresh(self) - + # Make a clone of this SFC_Jump - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): jump = SFC_Jump(parent, self.Target, id) jump.SetSize(self.Size[0], self.Size[1]) if pos is not None: @@ -1540,41 +1544,42 @@ jump.SetPosition(self.Pos.x, self.Pos.y) jump.Input = self.Input.Clone(jump) return jump - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input} - + return {self.Input: element.Input} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) - rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) + if self.Input: + rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) if movex != 0 or movey != 0: if self.Input.IsConnected(): rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) return rect - + # Forbids to change the jump size def SetSize(self, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.SetSize(self, width, height) - + # Forbids to resize jump def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: Graphic_Element.Resize(self, x, y, width, height) - + # Delete this jump by calling the appropriate method def Delete(self): self.Parent.DeleteJump(self) - + # Unconnect input def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the size of text for target def RefreshTargetSize(self): self.TargetSize = self.Parent.GetTextExtent(self.Target) - + # Returns if the point given is in the bounding box def HitTest(self, pt, connectors=True): # Calculate the bounding box of the condition outside the transition @@ -1584,22 +1589,22 @@ text_width, text_height) return text_bbx.InsideXY(pt.x, pt.y) or Graphic_Element.HitTest(self, pt, connectors) - + # Refresh the jump bounding box def RefreshBoundingBox(self): - text_width, text_height = self.Parent.GetTextExtent(self.Target) + text_width, _text_height = self.Parent.GetTextExtent(self.Target) # Calculate the bounding box size bbx_width = self.Size[0] + 2 + text_width - self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, - bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1) - + self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y - CONNECTOR_SIZE, + bbx_width + 1, self.Size[1] + CONNECTOR_SIZE + 1) + # Returns the connector connected to input def GetPreviousConnector(self): wires = self.Input.GetWires() if len(wires) == 1: return wires[0][0].GetOtherConnected(self.Input) return None - + # Refresh the element connectors position def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -1608,41 +1613,41 @@ horizontal_pos = round(float(self.Pos.x + horizontal_pos) / float(scaling[0])) * scaling[0] - self.Pos.x self.Input.SetPosition(wx.Point(horizontal_pos, 0)) self.RefreshConnected() - + # Refresh the position of wires connected to jump - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): if self.Input: self.Input.MoveConnected(exclude) - - # Returns input jump connector - def GetConnector(self, position = None, name = None): + + # Returns input jump connector + def GetConnector(self, position=None, name=None): return self.Input - - # Returns all the jump connectors + + # Returns all the jump connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": []} - + # Test if point given is on jump input connector - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input and self.Input.TestPoint(pt, direction, exclude): return self.Input return None - + # Changes the jump target def SetTarget(self, target): self.Target = target self.RefreshTargetSize() self.RefreshBoundingBox() - + # Returns the jump target def GetTarget(self): return self.Target - + # Returns the jump minimum size def GetMinSize(self): return SFC_JUMP_SIZE - + # Align input element with this jump def RefreshInputPosition(self): if self.Input: @@ -1659,21 +1664,21 @@ input_block.MoveActionBlock((diffx, 0)) input_block.Move(diffx, 0) input_block.RefreshInputPosition() - + # Can't align output element, because there is no output - def RefreshOutputPosition(self, move = None): + def RefreshOutputPosition(self, move=None): pass - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the jump properties self.Parent.EditJumpContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the jump state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1684,8 +1689,8 @@ self.RefreshInputPosition() return movex, 0 else: - return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac = 2) - + return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling, width_fac=2) + # Refresh input element model def RefreshInputModel(self): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1695,32 +1700,32 @@ input_block.RefreshModel(False) if not isinstance(input_block, SFC_Divergence): input_block.RefreshInputModel() - + # Refresh output element model def RefreshOutputModel(self, move=False): pass - + # Refreshes the jump model def RefreshModel(self, move=True): self.Parent.RefreshJumpModel(self) if move: if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: self.RefreshInputModel() - + # Adds an highlight to the variable def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "target" and start[0] == 0 and end[0] == 0: AddHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes an highlight from the variable def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "target": RemoveHighlight(self.Highlights, (start, end, highlight_type)) - + # Removes all the highlights of one particular type from the variable def ClearHighlight(self, highlight_type=None): ClearHighlights(self.Highlights, highlight_type) - + # Draws the highlightment of this element if it is highlighted def DrawHighlightment(self, dc): scalex, scaley = dc.GetUserScale() @@ -1728,16 +1733,16 @@ dc.SetPen(MiterPen(HIGHLIGHTCOLOR)) dc.SetBrush(wx.Brush(HIGHLIGHTCOLOR)) dc.SetLogicalFunction(wx.AND) - points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, + points = [wx.Point(int(round((self.Pos.x - 2) * scalex)) - 3, int(round((self.Pos.y - 2) * scaley)) - 2), - wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, + wx.Point(int(round((self.Pos.x + self.Size[0] + 2) * scalex)) + 4, int(round((self.Pos.y - 2) * scaley)) - 2), - wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), + wx.Point(int(round((self.Pos.x + self.Size[0] / 2) * scalex)), int(round((self.Pos.y + self.Size[1] + 3) * scaley)) + 4)] dc.DrawPolygon(points) dc.SetLogicalFunction(wx.COPY) dc.SetUserScale(scalex, scaley) - + # Draws divergence def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -1747,12 +1752,12 @@ else: dc.SetPen(MiterPen(wx.BLACK)) dc.SetBrush(wx.BLACK_BRUSH) - + if getattr(dc, "printing", False): target_size = dc.GetTextExtent(self.Target) else: target_size = self.TargetSize - + # Draw plain rectangle for representing the divergence dc.DrawLine(self.Pos.x + self.Size[0] / 2, self.Pos.y, self.Pos.x + self.Size[0] / 2, self.Pos.y + self.Size[1]) points = [wx.Point(self.Pos.x, self.Pos.y), @@ -1766,48 +1771,48 @@ # Draw input connector if self.Input: self.Input.Draw(dc) - + if not getattr(dc, "printing", False): DrawHighlightedText(dc, self.Target, self.Highlights, target_pos[0], target_pos[1]) - - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Sequencial Function Chart Action Block -#------------------------------------------------------------------------------- - -""" -Class that implements the graphic representation of an action block -""" +# ------------------------------------------------------------------------------- + class SFC_ActionBlock(Graphic_Element): - + """ + Class that implements the graphic representation of an action block + """ + # Create a new action block - def __init__(self, parent, actions = [], id = None): + def __init__(self, parent, actions=None, id=None): Graphic_Element.__init__(self, parent) self.Id = id self.Size = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) self.MinSize = wx.Size(SFC_ACTION_MIN_SIZE[0], SFC_ACTION_MIN_SIZE[1]) self.Highlights = {} # Create an input and output connector - self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone = True) + self.Input = Connector(self, "", None, wx.Point(0, SFC_ACTION_MIN_SIZE[1] / 2), WEST, onlyone=True) self.SetActions(actions) self.Value = None self.PreviousValue = None - + def Flush(self): if self.Input is not None: self.Input.Flush() self.Input = None - + def SpreadCurrent(self): if self.Parent.Debug: self.PreviousValue = self.Value self.Value = self.Input.ReceivingCurrent() if self.Value != self.PreviousValue and self.Visible: self.Parent.ElementNeedRefresh(self) - + # Make a clone of this SFC_ActionBlock - def Clone(self, parent, id = None, pos = None): + def Clone(self, parent, id=None, pos=None): actions = [action.copy() for action in self.Actions] action_block = SFC_ActionBlock(parent, actions, id) action_block.SetSize(self.Size[0], self.Size[1]) @@ -1817,29 +1822,30 @@ action_block.SetPosition(self.Pos.x, self.Pos.y) action_block.Input = self.Input.Clone(action_block) return action_block - + def GetConnectorTranslation(self, element): - return {self.Input : element.Input} - + return {self.Input: element.Input} + # Returns the RedrawRect - def GetRedrawRect(self, movex = 0, movey = 0): + def GetRedrawRect(self, movex=0, movey=0): rect = Graphic_Element.GetRedrawRect(self, movex, movey) - rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) + if self.Input: + rect = rect.Union(self.Input.GetRedrawRect(movex, movey)) if movex != 0 or movey != 0: if self.Input.IsConnected(): rect = rect.Union(self.Input.GetConnectedRedrawRect(movex, movey)) return rect - + # Returns the number of action lines def GetLineNumber(self): return len(self.Actions) - + def GetLineSize(self): if len(self.Actions) > 0: return self.Size[1] / len(self.Actions) else: return SFC_ACTION_MIN_SIZE[1] - + # Forbids to resize the action block def Resize(self, x, y, width, height): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: @@ -1847,38 +1853,38 @@ self.SetSize(width, self.Size[1]) else: Graphic_Element.Resize(self, x, y, width, height) - + # Delete this action block by calling the appropriate method def Delete(self): self.Parent.DeleteActionBlock(self) - + # Unconnect input and output def Clean(self): - self.Input.UnConnect(delete = self.Parent.GetDrawingMode() == FREEDRAWING_MODE) - + self.Input.UnConnect(delete=self.Parent.GetDrawingMode() == FREEDRAWING_MODE) + # Refresh the action block bounding box def RefreshBoundingBox(self): self.BoundingBox = wx.Rect(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - + # Refresh the position of wires connected to action block - def RefreshConnected(self, exclude = []): + def RefreshConnected(self, exclude=None): self.Input.MoveConnected(exclude) - - # Returns input action block connector - def GetConnector(self, position = None, name = None): + + # Returns input action block connector + def GetConnector(self, position=None, name=None): return self.Input - - # Returns all the action block connectors + + # Returns all the action block connectors def GetConnectors(self): return {"inputs": [self.Input], "outputs": []} - + # Test if point given is on action block input connector - def TestConnector(self, pt, direction = None, exclude = True): + def TestConnector(self, pt, direction=None, exclude=True): # Test input connector if self.Input.TestPoint(pt, direction, exclude): return self.Input return None - + # Refresh the element connectors position def RefreshConnectors(self): scaling = self.Parent.GetScaling() @@ -1887,9 +1893,10 @@ vertical_pos = round(float(self.Pos.y + vertical_pos) / float(scaling[1])) * scaling[1] - self.Pos.y self.Input.SetPosition(wx.Point(0, vertical_pos)) self.RefreshConnected() - + # Changes the action block actions - def SetActions(self, actions): + def SetActions(self, actions=None): + actions = [] if actions is None else actions self.Actions = actions self.ColSize = [0, 0, 0] min_height = 0 @@ -1913,13 +1920,14 @@ if self.Parent.GetDrawingMode() == FREEDRAWING_MODE: self.Size = wx.Size(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], max(min_height, SFC_ACTION_MIN_SIZE[1], self.Size[1])) self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], - SFC_ACTION_MIN_SIZE[0]), max(SFC_ACTION_MIN_SIZE[1], min_height) + SFC_ACTION_MIN_SIZE[0]), max(SFC_ACTION_MIN_SIZE[1], min_height) self.RefreshBoundingBox() else: self.Size = wx.Size(max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], - SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1]) + SFC_ACTION_MIN_SIZE[0]), + len(self.Actions) * SFC_ACTION_MIN_SIZE[1]) self.MinSize = max(self.ColSize[0] + self.ColSize[1] + self.ColSize[2], - SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1] + SFC_ACTION_MIN_SIZE[0]), len(self.Actions) * SFC_ACTION_MIN_SIZE[1] self.RefreshBoundingBox() if self.Input is not None: wires = self.Input.GetWires() @@ -1927,29 +1935,29 @@ input_block = wires[0][0].GetOtherConnected(self.Input).GetParentBlock() input_block.RefreshOutputPosition() input_block.RefreshOutputModel(True) - + # Returns the action block actions def GetActions(self): return self.Actions - + # Returns the action block minimum size def GetMinSize(self): return self.MinSize - + # Method called when a LeftDClick event have been generated def OnLeftDClick(self, event, dc, scaling): # Edit the action block properties self.Parent.EditActionBlockContent(self) - + # Method called when a RightUp event have been generated def OnRightUp(self, event, dc, scaling): # Popup the default menu self.Parent.PopupDefaultMenu() - + # Refreshes the action block state according to move defined and handle selected def ProcessDragging(self, movex, movey, event, scaling): if self.Parent.GetDrawingMode() != FREEDRAWING_MODE: - handle_type, handle = self.Handle + handle_type, _handle = self.Handle if handle_type == HANDLE_MOVE: movex = max(-self.BoundingBox.x, movex) if scaling is not None: @@ -1966,18 +1974,17 @@ else: return Graphic_Element.ProcessDragging(self, movex, movey, event, scaling) - # Refreshes the action block model def RefreshModel(self, move=True): self.Parent.RefreshActionBlockModel(self) - + # Adds an highlight to the variable def AddHighlight(self, infos, start, end, highlight_type): if infos[0] == "action" and infos[1] < len(self.Actions): action_highlights = self.Highlights.setdefault(infos[1], {}) attribute_highlights = action_highlights.setdefault(infos[2], []) AddHighlight(attribute_highlights, (start, end, highlight_type)) - + # Removes an highlight from the block def RemoveHighlight(self, infos, start, end, highlight_type): if infos[0] == "action" and infos[1] < len(self.Actions): @@ -1987,7 +1994,7 @@ action_highlights.pop(infos[2]) if len(action_highlights) == 0: self.Highlights.pop(infos[1]) - + # Removes all the highlights of one particular type from the block def ClearHighlight(self, highlight_type=None): if highlight_type is None: @@ -1996,13 +2003,13 @@ highlight_items = self.Highlights.items() for number, action_highlights in highlight_items: action_highlight_items = action_highlights.items() - for name, attribute_highlights in action_highlights: + for name, attribute_highlights in action_highlight_items: attribute_highlights = ClearHighlights(attribute_highlights, highlight_type) if len(attribute_highlights) == 0: action_highlights.pop(name) if len(action_highlights) == 0: self.Highlights.pop(number) - + # Draws divergence def Draw(self, dc): Graphic_Element.Draw(self, dc) @@ -2014,15 +2021,15 @@ colsize = [self.ColSize[0], self.Size[0] - self.ColSize[0] - self.ColSize[2], self.ColSize[2]] # Draw plain rectangle for representing the action block dc.DrawRectangle(self.Pos.x, self.Pos.y, self.Size[0] + 1, self.Size[1] + 1) - dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, - self.Pos.x + colsize[0], self.Pos.y + self.Size[1]) - dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, - self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x + colsize[0], self.Pos.y, + self.Pos.x + colsize[0], self.Pos.y + self.Size[1]) + dc.DrawLine(self.Pos.x + colsize[0] + colsize[1], self.Pos.y, + self.Pos.x + colsize[0] + colsize[1], self.Pos.y + self.Size[1]) line_size = self.GetLineSize() for i, action in enumerate(self.Actions): if i != 0: - dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, - self.Pos.x + self.Size[0], self.Pos.y + i * line_size) + dc.DrawLine(self.Pos.x, self.Pos.y + i * line_size, + self.Pos.x + self.Size[0], self.Pos.y + i * line_size) qualifier_size = dc.GetTextExtent(action.qualifier) if action.duration != "": qualifier_pos = (self.Pos.x + (colsize[0] - qualifier_size[0]) / 2, @@ -2044,7 +2051,7 @@ indicator_pos = (self.Pos.x + colsize[0] + colsize[1] + (colsize[2] - indicator_size[0]) / 2, self.Pos.y + i * line_size + (line_size - indicator_size[1]) / 2) dc.DrawText(action.indicator, indicator_pos[0], indicator_pos[1]) - + if not getattr(dc, "printing", False): action_highlights = self.Highlights.get(i, {}) for name, attribute_highlights in action_highlights.iteritems(): @@ -2056,7 +2063,6 @@ DrawHighlightedText(dc, action.value, attribute_highlights, content_pos[0], content_pos[1]) elif name == "indicator": DrawHighlightedText(dc, action.indicator, attribute_highlights, indicator_pos[0], indicator_pos[1]) - + # Draw input connector self.Input.Draw(dc) - diff -r c1298e7ffe3a -r 8391c11477f4 graphics/ToolTipProducer.py --- a/graphics/ToolTipProducer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/ToolTipProducer.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,42 +22,45 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import wx from controls.CustomToolTip import CustomToolTip, TOOLTIP_WAIT_PERIOD - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Tool Tip Producer class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Class that implements an element that generate Tool Tip -""" -class ToolTipProducer: - +class ToolTipProducer(object): + """ + Class that implements an element that generate Tool Tip + """ + def __init__(self, parent): """ Constructor @param parent: Parent Viewer """ self.Parent = parent - + self.ToolTip = None self.ToolTipPos = None - + # Timer for firing Tool tip display self.ToolTipTimer = wx.Timer(self.Parent, -1) - self.Parent.Bind(wx.EVT_TIMER, - self.OnToolTipTimer, - self.ToolTipTimer) - + self.Parent.Bind(wx.EVT_TIMER, + self.OnToolTipTimer, + self.ToolTipTimer) + def __del__(self): """ Destructor """ self.DestroyToolTip() - + def OnToolTipTimer(self, event): """ Callback for Tool Tip firing timer Event @@ -65,21 +68,21 @@ """ # Get Tool Tip text value = self.GetToolTipValue() - + if value is not None and self.ToolTipPos is not None: # Create Tool Tip self.ToolTip = CustomToolTip(self.Parent, value) self.ToolTip.SetToolTipPosition(self.ToolTipPos) self.ToolTip.Show() - + def GetToolTipValue(self): """ Return tool tip text - Have to be overridden by inherited classes - @return: Tool tip text (None if not overridden) + Have to be overridden by inherited classes + @return: Tool tip text (None if not overridden) """ return None - + def DisplayToolTip(self, pos): """ Display Tool tip @@ -87,14 +90,14 @@ """ # Destroy current displayed Tool tip self.DestroyToolTip() - + # Save Tool Tip position self.ToolTipPos = pos # Start Tool tip firing timer self.ToolTipTimer.Start( - int(TOOLTIP_WAIT_PERIOD * 1000), + int(TOOLTIP_WAIT_PERIOD * 1000), oneShot=True) - + def SetToolTipText(self, text): """ Set current Tool tip text @@ -102,7 +105,7 @@ """ if self.ToolTip is not None: self.ToolTip.SetTip(text) - + def DestroyToolTip(self): """ Destroy current displayed Tool Tip @@ -110,7 +113,7 @@ # Stop Tool tip firing timer self.ToolTipTimer.Stop() self.ToolTipPos = None - + # Destroy Tool Tip if self.ToolTip is not None: self.ToolTip.Destroy() diff -r c1298e7ffe3a -r 8391c11477f4 graphics/__init__.py --- a/graphics/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/graphics/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -24,9 +24,11 @@ # Package initialisation -from GraphicCommons import * -from FBD_Objects import * -from LD_Objects import * -from SFC_Objects import * -from RubberBand import RubberBand -from DebugDataConsumer import DebugDataConsumer +from __future__ import absolute_import + +from graphics.GraphicCommons import * +from graphics.FBD_Objects import * +from graphics.LD_Objects import * +from graphics.SFC_Objects import * +from graphics.RubberBand import RubberBand +from graphics.DebugDataConsumer import DebugDataConsumer diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_bn_BD.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_bn_BD.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3910 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Adhir Dutta , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Adhir Dutta , 2017\n" +"Language-Team: Bengali (Bangladesh) (https://www.transifex.com/beremiz/teams/75746/bn_BD/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: bn_BD\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" +"\n" +"একটি ব্যতিক্রমী অসঙ্গতির (বাগ) প্রাদুর্ভাব ঘটেছে। সংরক্ষিত অসঙ্গতির নথিটিঃ\n" +"(%s)\n" +"\n" +"এই ঠিকানায় অনুগ্রহপূর্বক নথিটি প্রেরণ করুনঃ\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"আপনাকে প্রোগ্রামটি পুনঃআরম্ভ করতে হবে.\n" +"\n" +"উপাত্তসমূহ:\n" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr "বাহ্যিক" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr "ইনআউট" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr "ইনপুট" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr "স্থানীয়" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr "আউটপুট" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr "সাময়িক" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr "এবং %s" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr "উত্পাদন ব্যর্থ !\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "\"%s\" Data Type অস্তিত্বহীন !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "\"%s\" POU ইতিমধ্যেই বিদ্যমান !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "\"%s\" POU এর অস্তিত্ব নেই !!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" স্বীয়কার্যে ব্যবহার অযোগ্য! " + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "\"%s\" config ইতিমধ্যেই বিদ্যমান!" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "\"%s\" configuration ইতিমধ্যেই বিদ্যমান !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "\"%s\" data type ইতিমধ্যেই বিদ্যমান! " + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "\"%s\" এই pou এর উপাদান ইতিমধ্যেই বিদ্যমান!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "\"%s\" ফোল্ডারটিতে বেরিমিজ প্রকল্প নেই\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "\"%s\" একটি কি-ওয়ার্ড। তাই ব্যবহার অযোগ্য!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "\"%s\" একটি অযৌক্তিক সংখ্যা!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "\"%s\" যৌক্তিক ফ্লোল্ডার নয়!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "\"%s\" একটি অযৌক্তিক শনাক্তকারী!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "\"%s\" এক বা একাধিক POU তে ব্যবহৃত। আপনি কি কাজ করবেন?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "\"%s\" POU ইতিমধ্যেই বিদ্যমান!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "\"%s\" step ইতিমধ্যেই বিদ্যমান!" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "\"%s\" মান ইতিমধ্যেই সংজ্ঞায়িত!" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "\"%s\" সাংখ্যমানটি অ্যারের জন্য অযৌক্তিক মাত্রা!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"\"%s\" সাংখ্যমানটি অ্যারের জন্য অযৌক্তিক মাত্রা!\n" +"ডানের সাংখ্যমান অবশ্যই বামের চেয়ে বড় হতে হবে." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "\"{a1}\" ফাংশন বাতিল \"{a2}\" তে POU: কোনও ইনপুট প্রযুক্ত হয়নি" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" ইতিমধ্যেই \"{a2}\" দ্বারা ব্যবহৃত!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" এর সম্পদ ইতিমধ্যেই \"{a2}\" এর configuration এ বিদ্যমান !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" এর সম্পদ \"{a2}\" এর configuration এ ব্যবহৃত হয়নি !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gমিসে" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dদি" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dঘ" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dমি" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dমিসে" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%dসে" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "%s Data Types" + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%sPOUs" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "%s Profile" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "%sপ্রধান অংশে প্রতিলিপি নেই!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "%sপ্রধান অংশে text নেই!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr "" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr "" + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "" + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "" + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "" + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "" + +#: ../features.py:32 +msgid "C extension" +msgstr "" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "" + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "" + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "" + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "" + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "" + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "" + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "" + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "" + +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "" + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "" + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "" + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "" + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "" + +#: ../features.py:33 +msgid "Python file" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "" + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "" + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "" + +msgid "CAN_Device" +msgstr "" + +msgid "CAN_Baudrate" +msgstr "" + +msgid "NodeId" +msgstr "" + +msgid "Sync_Align" +msgstr "" + +msgid "Sync_Align_Ratio" +msgstr "" + +msgid "CanFestivalNode" +msgstr "" + +msgid "Sync_TPDOs" +msgstr "" + +msgid "CanFestivalInstance" +msgstr "" + +msgid "CAN_Driver" +msgstr "" + +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "" + +msgid "XenoConfig" +msgstr "" + +msgid "Compiler" +msgstr "" + +msgid "CFLAGS" +msgstr "" + +msgid "Linker" +msgstr "" + +msgid "LDFLAGS" +msgstr "" + +msgid "Linux" +msgstr "" + +msgid "Win32" +msgstr "" + +msgid "BaseParams" +msgstr "" + +msgid "IEC_Channel" +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "BeremizRoot" +msgstr "" + +msgid "TargetType" +msgstr "" + +msgid "Libraries" +msgstr "" + +msgid "URI_location" +msgstr "" + +msgid "Disable_Extensions" +msgstr "" + +msgid "%(codefile_name)s" +msgstr "" + +msgid "variables" +msgstr "" + +msgid "variable" +msgstr "" + +msgid "name" +msgstr "" + +msgid "type" +msgstr "" + +msgid "class" +msgstr "" + +msgid "initial" +msgstr "" + +msgid "desc" +msgstr "" + +msgid "onchange" +msgstr "" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "" + +msgid "second input parameter" +msgstr "" + +msgid "first output parameter" +msgstr "" + +msgid "second output parameter" +msgstr "" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_de_DE.po --- a/i18n/Beremiz_de_DE.po Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/Beremiz_de_DE.po Tue Jan 30 16:06:58 2018 +0100 @@ -1,49 +1,27 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Andrey Skvortsov , 2017 +# Hendrik Knackstedt , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-09-07 01:17+0200\n" -"PO-Revision-Date: 2012-09-09 18:40+0100\n" -"Last-Translator: Laurent BESSARD \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Hendrik Knackstedt , 2017\n" +"Language-Team: German (Germany) (https://www.transifex.com/beremiz/teams/75746/de_DE/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Language: German\n" -"X-Poedit-Country: GERMANY\n" - -#: ../PLCOpenEditor.py:520 -#, fuzzy -msgid "" -"\n" -"An error has occurred.\n" -"\n" -"Click OK to save an error report.\n" -"\n" -"Please be kind enough to send this file to:\n" -"edouard.tisserant@gmail.com\n" -"\n" -"Error:\n" -msgstr "" -"\n" -"Ein nicht behandelter Fehler ist aufgetreten. Fehlerreport gespeichert unter: \n" -"(%s)\n" -"\n" -"Bitte unterstützen Sie uns und senden ihn hierher:\n" -"edouard.tisserant@gmail.com\n" -"\n" -"Sie sollten Beremiz nun neu starten.\n" -"\n" -"Traceback:\n" - -#: ../Beremiz.py:1071 -#, fuzzy, python-format +"Language: de_DE\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format msgid "" "\n" "An unhandled exception (bug) occured. Bug report saved at :\n" @@ -52,3505 +30,3538 @@ "Please be kind enough to send this file to:\n" "beremiz-devel@lists.sourceforge.net\n" "\n" -"You should now restart Beremiz.\n" +"You should now restart program.\n" "\n" "Traceback:\n" msgstr "" "\n" -"Ein nicht behandelter Fehler ist aufgetreten. Fehlerreport gespeichert unter: \n" +"Eine unbehandelte Ausnahme (Fehler) ist aufgetreten. Fehlerbericht gespeichert unter:\n" "(%s)\n" "\n" -"Bitte unterstützen Sie uns und senden ihn hierher:\n" -"edouard.tisserant@gmail.com\n" +"Bitte schicken Sie die Datei an:\n" +"beremiz-devel@lists.sourceforge.net\n" "\n" -"Sie sollten Beremiz nun neu starten.\n" +"Sie sollten das Programm jetzt neu starten.\n" "\n" "Traceback:\n" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid " External" -msgstr "" - -#: ../controls/VariablePanel.py:76 +msgstr " Extern" + +#: ../controls/VariablePanel.py:71 msgid " InOut" -msgstr "" - -#: ../controls/VariablePanel.py:76 +msgstr "EinAusgang" + +#: ../controls/VariablePanel.py:71 msgid " Input" -msgstr "" - -#: ../controls/VariablePanel.py:77 -#, fuzzy +msgstr "Eingang" + +#: ../controls/VariablePanel.py:72 msgid " Local" -msgstr "Lokal" - -#: ../controls/VariablePanel.py:76 +msgstr " Lokal" + +#: ../controls/VariablePanel.py:71 msgid " Output" -msgstr "" - -#: ../controls/VariablePanel.py:78 +msgstr "Ausgang" + +#: ../controls/VariablePanel.py:73 msgid " Temp" -msgstr "" - -#: ../PLCOpenEditor.py:530 -#, fuzzy -msgid " : " -msgstr "Datei : " - -#: ../dialogs/PouTransitionDialog.py:94 -#: ../dialogs/PouActionDialog.py:91 -#: ../dialogs/PouDialog.py:111 -#: ../dialogs/SFCTransitionDialog.py:144 +msgstr " Temp" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 #, python-format msgid " and %s" -msgstr "" - -#: ../ProjectController.py:890 +msgstr "und %s" + +#: ../ProjectController.py:1151 msgid " generation failed !\n" msgstr " Erstellung fehlgeschlagen !\n" -#: ../plcopen/plcopen.py:1051 +#: ../plcopen/plcopen.py:886 #, python-format msgid "\"%s\" Data Type doesn't exist !!!" -msgstr "" - -#: ../plcopen/plcopen.py:1069 +msgstr "Datentyp \"%s\" existiert bereits !!!" + +#: ../plcopen/plcopen.py:904 #, python-format msgid "\"%s\" POU already exists !!!" -msgstr "" - -#: ../plcopen/plcopen.py:1090 +msgstr "Baustein \"%s\" existiert bereits !!!" + +#: ../plcopen/plcopen.py:925 #, python-format msgid "\"%s\" POU doesn't exist !!!" -msgstr "" - -#: ../editors/Viewer.py:234 +msgstr "Baustein \"%s\" existiert nicht !!!" + +#: ../editors/Viewer.py:247 #, python-format msgid "\"%s\" can't use itself!" -msgstr "" - -#: ../IDEFrame.py:1706 -#: ../IDEFrame.py:1725 +msgstr "\"%s\" kann sich nicht selbst aufrufen!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 #, python-format msgid "\"%s\" config already exists!" -msgstr "" - -#: ../plcopen/plcopen.py:315 +msgstr "Konfiguration \"%s\" existiert bereits!" + +#: ../plcopen/plcopen.py:472 #, python-format msgid "\"%s\" configuration already exists !!!" -msgstr "" - -#: ../IDEFrame.py:1660 +msgstr "Konfiguration \"%s\" existiert bereits !!!" + +#: ../IDEFrame.py:1605 #, python-format msgid "\"%s\" data type already exists!" -msgstr "" - -#: ../PLCControler.py:2040 -#: ../PLCControler.py:2044 -#, python-format -msgid "\"%s\" element can't be pasted here!!!" -msgstr "" - -#: ../editors/TextViewer.py:305 -#: ../editors/TextViewer.py:325 -#: ../editors/Viewer.py:252 -#: ../dialogs/PouTransitionDialog.py:105 -#: ../dialogs/ConnectionDialog.py:150 -#: ../dialogs/PouActionDialog.py:102 -#: ../dialogs/FBDBlockDialog.py:162 +msgstr "Datentyp \"%s\" existiert bereits!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 #, python-format msgid "\"%s\" element for this pou already exists!" -msgstr "" - -#: ../Beremiz.py:894 +msgstr "\"%s\" Element ist für diesen Baustein bereits vorhanden!" + +#: ../BeremizIDE.py:897 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "Verzeichnis \"%s\" ist kein korrektes Beremiz-Projekt\n" -#: ../plcopen/structures.py:106 -#, python-format -msgid "\"%s\" function cancelled in \"%s\" POU: No input connected" -msgstr "" - -#: ../controls/VariablePanel.py:656 -#: ../IDEFrame.py:1651 -#: ../editors/DataTypeEditor.py:548 -#: ../editors/DataTypeEditor.py:577 -#: ../dialogs/PouNameDialog.py:49 -#: ../dialogs/PouTransitionDialog.py:101 -#: ../dialogs/SFCStepNameDialog.py:51 -#: ../dialogs/ConnectionDialog.py:146 -#: ../dialogs/FBDVariableDialog.py:199 -#: ../dialogs/PouActionDialog.py:98 -#: ../dialogs/PouDialog.py:118 -#: ../dialogs/SFCStepDialog.py:122 -#: ../dialogs/FBDBlockDialog.py:158 +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 #, python-format msgid "\"%s\" is a keyword. It can't be used!" -msgstr "" - -#: ../editors/Viewer.py:240 -#, python-format -msgid "\"%s\" is already used by \"%s\"!" -msgstr "" - -#: ../plcopen/plcopen.py:2786 +msgstr "\"%s\" ist ein Schlüsselwort. Es kann nicht verwendet werden!" + +#: ../plcopen/plcopen.py:2417 #, python-format msgid "\"%s\" is an invalid value!" -msgstr "" - -#: ../PLCOpenEditor.py:362 -#: ../PLCOpenEditor.py:399 -#, fuzzy, python-format +msgstr "\"%s\" ist ein ungültiger Wert!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format msgid "\"%s\" is not a valid folder!" -msgstr "IP ist nicht gültig!" - -#: ../controls/VariablePanel.py:654 -#: ../IDEFrame.py:1649 -#: ../editors/DataTypeEditor.py:572 -#: ../dialogs/PouNameDialog.py:47 -#: ../dialogs/PouTransitionDialog.py:99 -#: ../dialogs/SFCStepNameDialog.py:49 -#: ../dialogs/ConnectionDialog.py:144 -#: ../dialogs/PouActionDialog.py:96 -#: ../dialogs/PouDialog.py:116 -#: ../dialogs/SFCStepDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:156 -#, fuzzy, python-format +msgstr "\"%s\" ist kein Ordner!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format msgid "\"%s\" is not a valid identifier!" -msgstr "Verzeichnis \"%s\" ist kein korrektes Beremiz-Projekt\n" - -#: ../IDEFrame.py:214 -#: ../IDEFrame.py:2445 -#: ../IDEFrame.py:2464 -#, python-format -msgid "\"%s\" is used by one or more POUs. It can't be removed!" -msgstr "" - -#: ../controls/VariablePanel.py:311 -#: ../IDEFrame.py:1669 -#: ../editors/TextViewer.py:303 -#: ../editors/TextViewer.py:323 -#: ../editors/TextViewer.py:360 -#: ../editors/Viewer.py:250 -#: ../editors/Viewer.py:295 -#: ../editors/Viewer.py:312 -#: ../dialogs/ConnectionDialog.py:148 -#: ../dialogs/PouDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:160 +msgstr "\"%s\" ist kein gültiger Bezeichner!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "" +"\"%s\" wird von einer oder mehreren Bausteinen benutzt. Wollen Sie " +"fortfahren?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 #, python-format msgid "\"%s\" pou already exists!" -msgstr "" - -#: ../plcopen/plcopen.py:346 -#, python-format -msgid "\"%s\" resource already exists in \"%s\" configuration !!!" -msgstr "" - -#: ../plcopen/plcopen.py:362 -#, python-format -msgid "\"%s\" resource doesn't exist in \"%s\" configuration !!!" -msgstr "" - -#: ../dialogs/SFCStepNameDialog.py:57 -#: ../dialogs/SFCStepDialog.py:128 +msgstr "Baustein \"%s\" existiert bereits!" + +#: ../dialogs/SFCStepNameDialog.py:58 #, python-format msgid "\"%s\" step already exists!" -msgstr "" - -#: ../editors/DataTypeEditor.py:543 +msgstr "Schritt \"%s\" existiert bereits!" + +#: ../editors/DataTypeEditor.py:550 #, python-format msgid "\"%s\" value already defined!" -msgstr "" - -#: ../editors/DataTypeEditor.py:719 -#: ../dialogs/ArrayTypeDialog.py:97 +msgstr "Wert \"%s\" bereits definiert!" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 #, python-format msgid "\"%s\" value isn't a valid array dimension!" -msgstr "" - -#: ../editors/DataTypeEditor.py:726 -#: ../dialogs/ArrayTypeDialog.py:103 +msgstr "Wert \"%s\" ist keine gültige Array-Größe!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 #, python-format msgid "" "\"%s\" value isn't a valid array dimension!\n" "Right value must be greater than left value." msgstr "" - -#: ../PLCControler.py:793 -#, python-format -msgid "%s \"%s\" can't be pasted as a %s." -msgstr "" - -#: ../PLCControler.py:1422 +"Wert \"%s\" ist keine gültige Array-Größe!\n" +"Der rechte Wert muss größer sein als der linke." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "Funktion \"{a1}\" in \"{a2}\" abgebrochen POU: Nicht mit Eingang verbunden" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" wird von \"{a2}\" bereits benutzt?" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "Ressource \"{a1}\" existiert in Konfiguration \"{a2}\" bereits !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "Ressource \"{a1}\" existiert in Konfiguration \"{a2}\" nicht !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03g ms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%d T" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%d S" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%d M" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%d ms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%d s" + +#: ../PLCControler.py:1533 #, python-format msgid "%s Data Types" -msgstr "" - -#: ../editors/GraphicViewer.py:278 -#, python-format -msgid "%s Graphics" -msgstr "" - -#: ../PLCControler.py:1417 +msgstr "%s Datentypen" + +#: ../PLCControler.py:1516 #, python-format msgid "%s POUs" -msgstr "" - -#: ../canfestival/SlaveEditor.py:42 -#: ../canfestival/NetworkEditor.py:72 +msgstr "%s Bausteine" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 #, python-format msgid "%s Profile" -msgstr "" - -#: ../plcopen/plcopen.py:1780 -#: ../plcopen/plcopen.py:1790 -#: ../plcopen/plcopen.py:1800 -#: ../plcopen/plcopen.py:1810 -#: ../plcopen/plcopen.py:1819 +msgstr "%s Profil" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 #, python-format msgid "%s body don't have instances!" -msgstr "" - -#: ../plcopen/plcopen.py:1842 -#: ../plcopen/plcopen.py:1849 +msgstr "Baustein \"%s\" besitzt keine Instanzen!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 #, python-format msgid "%s body don't have text!" -msgstr "" - -#: ../IDEFrame.py:364 +msgstr "Baustein \"%s\" ist leer!" + +#: ../IDEFrame.py:386 msgid "&Add Element" -msgstr "" - -#: ../IDEFrame.py:334 +msgstr "&Element hinzufügen" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Schließen" + +#: ../IDEFrame.py:356 msgid "&Configuration" -msgstr "" - -#: ../IDEFrame.py:325 -#, fuzzy +msgstr "&Konfiguration" + +#: ../IDEFrame.py:345 msgid "&Data Type" -msgstr "ZielTyp" - -#: ../IDEFrame.py:368 +msgstr "&Datentyp" + +#: ../IDEFrame.py:390 msgid "&Delete" -msgstr "" - -#: ../IDEFrame.py:317 +msgstr "&Löschen" + +#: ../IDEFrame.py:337 msgid "&Display" -msgstr "" - -#: ../IDEFrame.py:316 +msgstr "&Ansicht" + +#: ../IDEFrame.py:336 msgid "&Edit" msgstr "&Bearbeiten" -#: ../IDEFrame.py:315 +#: ../IDEFrame.py:335 msgid "&File" -msgstr "" - -#: ../IDEFrame.py:327 -#, fuzzy +msgstr "&Datei" + +#: ../IDEFrame.py:347 msgid "&Function" -msgstr "Funktion : " - -#: ../IDEFrame.py:318 +msgstr "&Funktion" + +#: ../IDEFrame.py:338 msgid "&Help" -msgstr "" - -#: ../IDEFrame.py:331 +msgstr "&Hilfe" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&Lizenz" + +#: ../IDEFrame.py:351 msgid "&Program" -msgstr "" - -#: ../PLCOpenEditor.py:148 -#, fuzzy +msgstr "&Programm" + +#: ../PLCOpenEditor.py:127 msgid "&Properties" -msgstr "Eigenschaften" - -#: ../Beremiz.py:310 -#, fuzzy +msgstr "&Einstellungen" + +#: ../BeremizIDE.py:219 msgid "&Recent Projects" -msgstr "Projekt schließen" - -#: ../Beremiz.py:352 +msgstr "&Zuletzt verwendete Projekte" + +#: ../IDEFrame.py:353 msgid "&Resource" -msgstr "" - -#: ../controls/SearchResultPanel.py:237 -#, python-format -msgid "'%s' - %d match in project" -msgstr "" +msgstr "&Ressource" #: ../controls/SearchResultPanel.py:239 -#, python-format -msgid "'%s' - %d matches in project" -msgstr "" - -#: ../connectors/PYRO/__init__.py:51 -#, python-format -msgid "'%s' is located at %s\n" -msgstr "" - -#: ../controls/SearchResultPanel.py:289 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} Übereinstimmung im Projekt" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} Übereinstimmungen im Projekt" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' befindet sich unter {a2}\n" + +#: ../controls/SearchResultPanel.py:291 #, python-format msgid "(%d matches)" -msgstr "" - -#: ../PLCOpenEditor.py:508 -#: ../PLCOpenEditor.py:510 -#: ../PLCOpenEditor.py:511 +msgstr "(%d Ergebnisse)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 msgid ", " msgstr ", " -#: ../dialogs/PouTransitionDialog.py:96 -#: ../dialogs/PouActionDialog.py:93 -#: ../dialogs/PouDialog.py:113 -#: ../dialogs/SFCTransitionDialog.py:146 +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 #, python-format msgid ", %s" -msgstr "" - -#: ../PLCOpenEditor.py:506 +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 msgid ". " msgstr ". " -#: ../ProjectController.py:1268 -#, fuzzy -msgid "... debugger recovered\n" -msgstr "Warte auf Selbstheilung des Debuggers...\n" - -#: ../IDEFrame.py:1672 -#: ../IDEFrame.py:1714 -#: ../IDEFrame.py:1733 -#: ../dialogs/PouDialog.py:122 -#, python-format -msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" -msgstr "" - -#: ../controls/VariablePanel.py:658 -#: ../IDEFrame.py:1684 -#: ../IDEFrame.py:1695 -#: ../dialogs/PouNameDialog.py:51 -#: ../dialogs/PouTransitionDialog.py:103 -#: ../dialogs/SFCStepNameDialog.py:53 -#: ../dialogs/PouActionDialog.py:100 -#: ../dialogs/SFCStepDialog.py:124 -#, fuzzy, python-format +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1T" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1S" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"Ein POU enthält ein Element mit dem Namen \"%s\". Das könnte eine Kollision " +"sein. Möchten Sie fortfahren?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format msgid "A POU named \"%s\" already exists!" -msgstr "Ein Zweigname \"%s\" existiert bereits -> \"%s\"\n" - -#: ../ConfigTreeNode.py:371 -#, fuzzy, python-format -msgid "A child named \"%s\" already exist -> \"%s\"\n" -msgstr "Ein Zweigname \"%s\" existiert bereits -> \"%s\"\n" - -#: ../dialogs/BrowseLocationsDialog.py:175 +msgstr "Baustein mit dem Namen \"%s\" existiert bereits!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "Ein Kind mit dem Namen \"{a1}\" existiert bereits -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 msgid "A location must be selected!" -msgstr "" - -#: ../controls/VariablePanel.py:660 -#: ../IDEFrame.py:1686 -#: ../IDEFrame.py:1697 -#: ../dialogs/SFCStepNameDialog.py:55 -#: ../dialogs/SFCStepDialog.py:126 +msgstr "Bitte wählen Sie einen Ort aus!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Task mit dem gleichen Namen existiert bereits!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 #, python-format msgid "A variable with \"%s\" as name already exists in this pou!" -msgstr "" - -#: ../Beremiz.py:362 -#: ../PLCOpenEditor.py:181 +msgstr "Variable mit den Namen \"%s\" existiert bereits im Baustein!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "Variable mit dem Namen \"%s\" existiert bereits!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 msgid "About" msgstr "Über" -#: ../Beremiz.py:931 -msgid "About Beremiz" -msgstr "Über Beremiz" - -#: ../PLCOpenEditor.py:376 -msgid "About PLCOpenEditor" -msgstr "" - #: ../plcopen/iec_std.csv:22 msgid "Absolute number" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCStepDialog.py:69 +msgstr "Absolute Zahl" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 msgid "Action" -msgstr "" - -#: ../editors/Viewer.py:495 +msgstr "Aktion" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 msgid "Action Block" -msgstr "" - -#: ../dialogs/PouActionDialog.py:81 +msgstr "Aktionsblock" + +#: ../dialogs/PouActionDialog.py:82 msgid "Action Name" -msgstr "" +msgstr "Aktionname" #: ../dialogs/PouActionDialog.py:49 -#, fuzzy msgid "Action Name:" -msgstr "Funktion : " - -#: ../plcopen/plcopen.py:1480 +msgstr "Aktionname:" + +#: ../plcopen/plcopen.py:1364 #, python-format msgid "Action with name %s doesn't exist!" -msgstr "" - -#: ../PLCControler.py:95 +msgstr "Aktion mit dem Namen %s existiert nicht!" + +#: ../PLCControler.py:98 msgid "Actions" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:134 -#, fuzzy +msgstr "Aktionen" + +#: ../dialogs/ActionBlockDialog.py:133 msgid "Actions:" -msgstr "Funktion : " - -#: ../canfestival/SlaveEditor.py:54 -#: ../canfestival/NetworkEditor.py:84 -#: ../editors/Viewer.py:527 +msgstr "Aktionen:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Aktiv" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 msgid "Add" -msgstr "" - -#: ../IDEFrame.py:1925 -#: ../IDEFrame.py:1956 -#, fuzzy +msgstr "Hinzufügen" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 msgid "Add Action" -msgstr "Plugin hinzufügen" - -#: ../features.py:7 +msgstr "Aktion hinzufügen" + +#: ../features.py:32 msgid "Add C code accessing located variables synchronously" -msgstr "" - -#: ../IDEFrame.py:1908 +msgstr "Synchron ausführenden C-Code hinzufügen" + +#: ../IDEFrame.py:1876 msgid "Add Configuration" -msgstr "" - -#: ../IDEFrame.py:1888 +msgstr "Konfiguration hinzufügen" + +#: ../IDEFrame.py:1856 msgid "Add DataType" -msgstr "" - -#: ../editors/Viewer.py:453 +msgstr "Datentyp hinzufügen" + +#: ../editors/Viewer.py:572 msgid "Add Divergence Branch" -msgstr "" - -#: ../dialogs/DiscoveryDialog.py:115 -#, fuzzy +msgstr "Zweig hinzufügen" + +#: ../dialogs/DiscoveryDialog.py:117 msgid "Add IP" -msgstr "Plugin hinzufügen" - -#: ../IDEFrame.py:1896 +msgstr "IP hinzufügen" + +#: ../IDEFrame.py:1864 msgid "Add POU" -msgstr "" - -#: ../features.py:8 +msgstr "Baustein hinzufügen" + +#: ../features.py:33 msgid "Add Python code executed asynchronously" -msgstr "" - -#: ../IDEFrame.py:1936 -#: ../IDEFrame.py:1982 +msgstr "Asynchron auszuführenden Python-Code hinzufügen" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 msgid "Add Resource" -msgstr "" - -#: ../IDEFrame.py:1914 -#: ../IDEFrame.py:1953 +msgstr "Ressource hinzufügen" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 msgid "Add Transition" -msgstr "" - -#: ../editors/Viewer.py:442 +msgstr "Transitionsbedingung hinzufügen" + +#: ../editors/Viewer.py:559 msgid "Add Wire Segment" -msgstr "" - -#: ../editors/SFCViewer.py:359 +msgstr "Drahtsegment hinzufügen " + +#: ../editors/SFCViewer.py:433 msgid "Add a new initial step" -msgstr "" - -#: ../editors/Viewer.py:2289 -#: ../editors/SFCViewer.py:696 +msgstr "Den ersten Schritt hinzufügen" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 msgid "Add a new jump" -msgstr "" - -#: ../editors/SFCViewer.py:381 +msgstr "Neuen Sprung hinzufügen" + +#: ../editors/SFCViewer.py:455 msgid "Add a new step" -msgstr "" - -#: ../features.py:9 +msgstr "Neuen Schritt hinzufügen" + +#: ../features.py:34 msgid "Add a simple WxGlade based GUI." -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:138 -#, fuzzy +msgstr "Einfache WxGlade basierte GUI hinzufügen." + +#: ../dialogs/ActionBlockDialog.py:137 msgid "Add action" -msgstr "Plugin hinzufügen" - -#: ../editors/DataTypeEditor.py:345 +msgstr "Aktion hinzufügen" + +#: ../editors/DataTypeEditor.py:352 msgid "Add element" -msgstr "" - -#: ../editors/ResourceEditor.py:251 +msgstr "Komponent hinzufügen" + +#: ../editors/ResourceEditor.py:268 msgid "Add instance" -msgstr "" - -#: ../canfestival/NetworkEditor.py:86 +msgstr "Instanz hinzufügen" + +#: ../canfestival/NetworkEditor.py:103 msgid "Add slave" -msgstr "" - -#: ../editors/ResourceEditor.py:222 +msgstr "Slave hinzufügen" + +#: ../editors/ResourceEditor.py:239 msgid "Add task" -msgstr "" - -#: ../controls/VariablePanel.py:378 +msgstr "Task hinzufügen" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 msgid "Add variable" -msgstr "" +msgstr "Variable hinzufügen" #: ../plcopen/iec_std.csv:33 msgid "Addition" -msgstr "" - -#: ../plcopen/structures.py:250 +msgstr "Addition" + +#: ../plcopen/definitions.py:49 msgid "Additional function blocks" -msgstr "" - -#: ../editors/Viewer.py:1395 +msgstr "Zusätzliche Bausteine" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "Bausteingröße anpassen" + +#: ../editors/Viewer.py:1686 msgid "Alignment" -msgstr "" - -#: ../controls/VariablePanel.py:75 -#: ../dialogs/BrowseLocationsDialog.py:35 -#: ../dialogs/BrowseLocationsDialog.py:116 +msgstr "Ausrichtung" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 msgid "All" -msgstr "" +msgstr "Alle" #: ../editors/FileManagementPanel.py:35 -#, fuzzy msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" -msgstr "SVG files (*.svg)|*.svg|Alle Dateien|*.*" - -#: ../ProjectController.py:1335 +msgstr "Alle Dateien (*.*)|*.*|CSV Dateien (*.csv)|*.csv" + +#: ../ProjectController.py:1685 msgid "Already connected. Please disconnect\n" msgstr "Bereits verbunden. Bitte Verbindung trennen\n" -#: ../editors/DataTypeEditor.py:587 -#, fuzzy, python-format +#: ../editors/DataTypeEditor.py:591 +#, python-format msgid "An element named \"%s\" already exists in this structure!" -msgstr "Ein Zweigname \"%s\" existiert bereits -> \"%s\"\n" +msgstr "Komponent mit den Namen \"%s\" existiert bereits in der Struktur!" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Instanz mit dem gleichen Namen existiert bereits!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "Umbenennen alle Fortsetzungen mit dem gleichen Namen" #: ../plcopen/iec_std.csv:31 msgid "Arc cosine" -msgstr "" +msgstr " Arkuskosinus" #: ../plcopen/iec_std.csv:30 msgid "Arc sine" -msgstr "" +msgstr "Arkussinus" #: ../plcopen/iec_std.csv:32 msgid "Arc tangent" -msgstr "" +msgstr "Arkustangens" #: ../plcopen/iec_std.csv:33 msgid "Arithmetic" -msgstr "" - -#: ../controls/VariablePanel.py:729 -#: ../editors/DataTypeEditor.py:52 +msgstr "Arithmetik" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 msgid "Array" -msgstr "" +msgstr "Array" #: ../plcopen/iec_std.csv:39 msgid "Assignment" -msgstr "" - -#: ../dialogs/FBDVariableDialog.py:197 +msgstr "Zuordnung" + +#: ../dialogs/FBDVariableDialog.py:222 msgid "At least a variable or an expression must be selected!" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:99 +msgstr "Mindestens eine Variable oder ein Ausdruck muss ausgewählt werden!" + +#: ../controls/ProjectPropertiesPanel.py:100 msgid "Author" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:96 +msgstr "Autor" + +#: ../controls/ProjectPropertiesPanel.py:97 msgid "Author Name (optional):" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:72 +msgstr "Autor (optional):" + +#: ../dialogs/FindInPouDialog.py:77 msgid "Backward" -msgstr "" +msgstr "Zurück" #: ../util/Zeroconf.py:599 msgid "Bad domain name (circular) at " -msgstr "" +msgstr "Schlechter Domainname (Endlosschleife) für" #: ../util/Zeroconf.py:602 msgid "Bad domain name at " -msgstr "" - -#: ../canfestival/config_utils.py:341 -#: ../canfestival/config_utils.py:623 +msgstr "Schlechter Domainname für" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 #, python-format msgid "Bad location size : %s" msgstr "Fehlerhafte location size : %s" -#: ../editors/DataTypeEditor.py:168 -#: ../editors/DataTypeEditor.py:198 -#: ../editors/DataTypeEditor.py:290 -#: ../dialogs/ArrayTypeDialog.py:55 -#, fuzzy +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 msgid "Base Type:" -msgstr "ZielTyp" - -#: ../controls/VariablePanel.py:699 -#: ../editors/DataTypeEditor.py:617 -#, fuzzy +msgstr "Basistyp:" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 msgid "Base Types" -msgstr "ZielTyp" - -#: ../Beremiz.py:486 +msgstr "Basistypen" + +#: ../BeremizIDE.py:455 msgid "Beremiz" msgstr "Beremiz" #: ../plcopen/iec_std.csv:70 msgid "Binary selection (1 of 2)" -msgstr "" +msgstr "Binärauswahl (1 von 2)" #: ../plcopen/iec_std.csv:62 msgid "Bit-shift" -msgstr "" +msgstr "Bit-Shift" #: ../plcopen/iec_std.csv:66 msgid "Bitwise" -msgstr "" +msgstr "Bitweise Operationen" #: ../plcopen/iec_std.csv:66 msgid "Bitwise AND" -msgstr "" +msgstr "bitweises UND" #: ../plcopen/iec_std.csv:67 msgid "Bitwise OR" -msgstr "" +msgstr "bitweises ODER" #: ../plcopen/iec_std.csv:68 msgid "Bitwise XOR" -msgstr "" +msgstr "bitweises XOR" #: ../plcopen/iec_std.csv:69 msgid "Bitwise inverting" -msgstr "" - -#: ../editors/Viewer.py:465 +msgstr "bitweises NICHT" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 msgid "Block" -msgstr "" - -#: ../dialogs/FBDBlockDialog.py:38 -#, fuzzy +msgstr "Funktionsblock" + +#: ../dialogs/FBDBlockDialog.py:60 msgid "Block Properties" -msgstr "Eigenschaften" - -#: ../editors/Viewer.py:434 +msgstr "Funktionsblockeigenschaften" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Blockname" + +#: ../editors/Viewer.py:550 msgid "Bottom" -msgstr "" - -#: ../dialogs/BrowseValuesLibraryDialog.py:37 +msgstr "Unten" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "Gebrochen" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 #, python-format msgid "Browse %s values library" -msgstr "" - -#: ../dialogs/BrowseLocationsDialog.py:55 -#, fuzzy +msgstr "Wert für %s wählen" + +#: ../dialogs/BrowseLocationsDialog.py:65 msgid "Browse Locations" -msgstr "schließe Applikation" - -#: ../ProjectController.py:1484 +msgstr "Verfügbare IEC-Adressen:" + +#: ../ProjectController.py:1832 msgid "Build" msgstr "Build" -#: ../ProjectController.py:1051 +#: ../ProjectController.py:1297 msgid "Build directory already clean\n" msgstr "Build-Verzeichnis bereits sauber\n" -#: ../ProjectController.py:1485 +#: ../ProjectController.py:1833 msgid "Build project into build folder" msgstr "Build-Projekt nach Build-Verzeichnis" -#: ../ProjectController.py:910 +#: ../ProjectController.py:1080 msgid "C Build crashed !\n" msgstr "C Build abgestürzt !\n" -#: ../ProjectController.py:907 +#: ../ProjectController.py:1077 msgid "C Build failed.\n" msgstr "C Build fehlgeschlagen.\n" -#: ../ProjectController.py:895 +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "C code" + +#: ../ProjectController.py:1155 msgid "C code generated successfully.\n" msgstr "C Code erfolgreich generiert.\n" -#: ../targets/toolchain_gcc.py:132 +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "C Kompilierung fehlgeschlagen.\n" + +#: ../targets/toolchain_gcc.py:192 #, python-format msgid "C compilation of %s failed.\n" msgstr "C Kompilierung von %s fehlgeschlagen.\n" -#: ../features.py:7 -#, fuzzy +#: ../features.py:32 msgid "C extension" -msgstr "CExtension" - -#: ../features.py:6 +msgstr "C-Erweiterung" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "&Beiträge" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "CANOpen Netzwerk" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "CANOpen slave" + +#: ../features.py:31 msgid "CANopen support" -msgstr "" - -#: ../plcopen/plcopen.py:1722 -#: ../plcopen/plcopen.py:1736 -#: ../plcopen/plcopen.py:1757 -#: ../plcopen/plcopen.py:1773 +msgstr "CANopen support" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 msgid "Can only generate execution order on FBD networks!" -msgstr "" - -#: ../controls/VariablePanel.py:256 +msgstr "Abarbeitungsreihenfolge kann nur für FUP Bausteine bestimmt werden!" + +#: ../controls/VariablePanel.py:267 msgid "Can only give a location to local or global variables" -msgstr "" - -#: ../PLCOpenEditor.py:357 +msgstr "EIC-Nur lokale oder globale Variable können eine zugeordnet sein." + +#: ../PLCOpenEditor.py:344 #, python-format msgid "Can't generate program to file %s!" -msgstr "" - -#: ../controls/VariablePanel.py:254 +msgstr "Code-Erzeugung zur Datei \"%s\" ist fehlgeschlagen!" + +#: ../controls/VariablePanel.py:265 msgid "Can't give a location to a function block instance" -msgstr "" - -#: ../PLCOpenEditor.py:397 +msgstr "IEC-Adresse für eine Funktionsbausteininstanz ist nicht erlaubt " + +#: ../PLCOpenEditor.py:389 #, python-format msgid "Can't save project to file %s!" -msgstr "" - -#: ../controls/VariablePanel.py:298 +msgstr "Projekt kann nicht in die Datei %s gespeichert werden!" + +#: ../controls/VariablePanel.py:313 msgid "Can't set an initial value to a function block instance" -msgstr "" - -#: ../ConfigTreeNode.py:470 -#, python-format -msgid "Cannot create child %s of type %s " -msgstr "Kann Zweig %s von Typ %s nicht erstellen" - -#: ../ConfigTreeNode.py:400 +msgstr "Anfangswert für eine Funktionsbausteininstanz ist nicht erlaubt" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "Kann nicht Kind {a1} vom Typ {a2} erstellen" + +#: ../ConfigTreeNode.py:454 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "Kann keinen niedrigeren IEC-Kanal als %d finden\n" -#: ../connectors/PYRO/__init__.py:92 +#: ../connectors/PYRO/__init__.py:131 msgid "Cannot get PLC status - connection failed.\n" msgstr "Kann den SPS-Status nicht einlesen - Verbindung gescheitert.\n" -#: ../ProjectController.py:715 +#: ../ProjectController.py:943 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "Kann die Datei VARIABLES.csv nicht öffnen/lesen!\n" -#: ../canfestival/config_utils.py:371 -#, python-format -msgid "Cannot set bit offset for non bool '%s' variable (ID:%d,Idx:%x,sIdx:%x))" -msgstr "Unmöglich, den Bit-Offset in der nicht boolschen Variable '%s' zu setzen! (ID:%d,Idx:%x,sIdx:%x))" - -#: ../dialogs/FindInPouDialog.py:81 -#: ../dialogs/SearchInProjectDialog.py:67 +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Kann Bit-Offset für nicht-bool '{a1}' Variable (ID: {a2}, Idx: {a3}, sIdx: " +"{a4})) nicht setzen" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 msgid "Case sensitive" -msgstr "" - -#: ../editors/Viewer.py:429 +msgstr "Case-sensitive" + +#: ../editors/Viewer.py:545 msgid "Center" -msgstr "" - -#: ../Beremiz_service.py:322 +msgstr "Zentriert" + +#: ../Beremiz_service.py:268 msgid "Change IP of interface to bind" msgstr "Ändere IP-Adresse des zu verbindenden Interfaces." -#: ../Beremiz_service.py:321 +#: ../Beremiz_service.py:267 msgid "Change Name" msgstr "Ändere Name" -#: ../IDEFrame.py:1974 +#: ../IDEFrame.py:1946 msgid "Change POU Type To" -msgstr "" - -#: ../Beremiz_service.py:325 +msgstr "Bausteintyp konvertieren nach" + +#: ../Beremiz_service.py:269 msgid "Change Port Number" msgstr "Ändere Port-Nummer" -#: ../Beremiz_service.py:327 +#: ../Beremiz_service.py:270 msgid "Change working directory" msgstr "Ändere Arbeitsverzeichnis" #: ../plcopen/iec_std.csv:81 msgid "Character string" -msgstr "" - -#: ../svgui/svgui.py:92 +msgstr "Zeichenkette" + +#: ../svgui/svgui.py:128 msgid "Choose a SVG file" msgstr "Wählen Sie eine SVG-Datei" -#: ../ProjectController.py:353 +#: ../ProjectController.py:542 msgid "Choose a directory to save project" msgstr "Wählen Sie ein Verzeichnis um das Projekt zu speichern" -#: ../canfestival/canfestival.py:118 -#: ../PLCOpenEditor.py:313 -#: ../PLCOpenEditor.py:347 -#: ../PLCOpenEditor.py:391 -#, fuzzy +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 msgid "Choose a file" -msgstr "Wählen Sie eine SVG-Datei" - -#: ../Beremiz.py:831 -#: ../Beremiz.py:866 +msgstr "Datei auswählen" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 msgid "Choose a project" msgstr "Wähle Projekt" -#: ../dialogs/BrowseValuesLibraryDialog.py:42 +#: ../dialogs/BrowseValuesLibraryDialog.py:41 #, python-format msgid "Choose a value for %s:" -msgstr "" - -#: ../Beremiz_service.py:373 +msgstr "Wählen Sie den Wert für %s:" + +#: ../Beremiz_service.py:325 msgid "Choose a working directory " msgstr "Wähle Arbeitsverzeichnis" -#: ../ProjectController.py:281 +#: ../ProjectController.py:449 msgid "Chosen folder doesn't contain a program. It's not a valid project!" -msgstr "Ausgewählter Ordner beinhaltet kein Programm. Es handelt sich dabei nicht um ein gültiges Projekt!" - -#: ../ProjectController.py:247 +msgstr "" +"Ausgewählter Ordner beinhaltet kein Programm. Es handelt sich dabei nicht um" +" ein gültiges Projekt!" + +#: ../ProjectController.py:416 msgid "Chosen folder isn't empty. You can't use it for a new project!" -msgstr "Gewählter Ordner ist nicht leer. Sie können diesen nicht für ein neues Projekt verwenden!" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +msgstr "" +"Gewählter Ordner ist nicht leer. Sie können diesen nicht für ein neues " +"Projekt verwenden!" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Class" -msgstr "" - -#: ../controls/VariablePanel.py:369 +msgstr "Klasse" + +#: ../controls/VariablePanel.py:441 msgid "Class Filter:" -msgstr "" - -#: ../dialogs/FBDVariableDialog.py:62 +msgstr "Klassenfilter:" + +#: ../dialogs/FBDVariableDialog.py:70 msgid "Class:" -msgstr "" - -#: ../ProjectController.py:1488 +msgstr "Klasse:" + +#: ../ProjectController.py:1836 msgid "Clean" -msgstr "Säubern" - -#: ../ProjectController.py:1490 +msgstr "Aufräumen" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Protokoll zurücksetzen" + +#: ../ProjectController.py:1838 msgid "Clean project build folder" -msgstr "Säubere Projekt-Build Verzeichnis" - -#: ../ProjectController.py:1048 +msgstr "Projekt-Build-Verzeichnis leeren" + +#: ../ProjectController.py:1294 msgid "Cleaning the build directory\n" -msgstr "Säubere das Build-Verzeichnis\n" - -#: ../IDEFrame.py:411 +msgstr "Build-Verzeichnis leeren\n" + +#: ../IDEFrame.py:435 msgid "Clear Errors" -msgstr "" - -#: ../editors/Viewer.py:520 +msgstr "Fehlermeldungen zurücksetzen" + +#: ../editors/Viewer.py:641 msgid "Clear Execution Order" -msgstr "" - -#: ../editors/GraphicViewer.py:125 -msgid "Clear the graph values" -msgstr "" - -#: ../Beremiz.py:598 -#: ../PLCOpenEditor.py:221 +msgstr "Ausführungsreihenfolge zurücksetzen" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Schließen" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 msgid "Close Application" -msgstr "schließe Applikation" - -#: ../IDEFrame.py:1089 -#: ../Beremiz.py:319 -#: ../Beremiz.py:552 -#: ../PLCOpenEditor.py:131 +msgstr "Anwendung beenden" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 msgid "Close Project" msgstr "Projekt schließen" -#: ../Beremiz.py:317 -#: ../PLCOpenEditor.py:129 -#, fuzzy +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 msgid "Close Tab" -msgstr "Schließe Tab\tCTRL+W" - -#: ../editors/Viewer.py:481 +msgstr "Reiter schließen" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 msgid "Coil" -msgstr "" - -#: ../editors/Viewer.py:501 -#: ../editors/LDViewer.py:503 +msgstr "Spule" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 msgid "Comment" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:94 +msgstr "Kommentar" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Community Unterstützung" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Firmenname" + +#: ../controls/ProjectPropertiesPanel.py:95 msgid "Company Name (required):" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:95 +msgstr "Firmenname (erforderlich):" + +#: ../controls/ProjectPropertiesPanel.py:96 msgid "Company URL (optional):" -msgstr "" +msgstr "Firmenwebseite (optional):" #: ../plcopen/iec_std.csv:75 msgid "Comparison" -msgstr "" - -#: ../ProjectController.py:538 +msgstr "Vergleich" + +#: ../ProjectController.py:734 msgid "Compiling IEC Program into C code...\n" msgstr "Kompilliere IEC Programm zu c-Code...\n" #: ../plcopen/iec_std.csv:85 msgid "Concatenation" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:47 +msgstr "Konkatenation" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "Konfiguration" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "Konfigurationsvariablen" + +#: ../dialogs/SearchInProjectDialog.py:40 msgid "Configuration" -msgstr "" - -#: ../PLCControler.py:96 +msgstr "Konfiguration" + +#: ../PLCControler.py:99 msgid "Configurations" -msgstr "" - -#: ../ProjectController.py:1503 +msgstr "Konfigurationen" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Variablennamen bestätigen oder anpassen" + +#: ../ProjectController.py:1851 msgid "Connect" msgstr "Verbinden" -#: ../ProjectController.py:1504 +#: ../ProjectController.py:1852 msgid "Connect to the target PLC" -msgstr "Verbinde zur Ziel-SPS" - -#: ../connectors/PYRO/__init__.py:40 -#, python-format -msgid "Connecting to URI : %s\n" -msgstr "verbinde zu URI: %s\n" - -#: ../editors/Viewer.py:467 -#: ../dialogs/SFCTransitionDialog.py:76 -#, fuzzy +msgstr "Mit Ziel-SPS verbinden" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "Verbunden mit URI: %s" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 msgid "Connection" -msgstr "Verbinden" - -#: ../dialogs/ConnectionDialog.py:37 -#, fuzzy +msgstr "Verbindung" + +#: ../dialogs/ConnectionDialog.py:53 msgid "Connection Properties" -msgstr "Eigenschaften" - -#: ../ProjectController.py:1359 +msgstr "Verbindungseigenschaften" + +#: ../ProjectController.py:1709 msgid "Connection canceled!\n" msgstr "Verbindung abgebrochen!\n" -#: ../ProjectController.py:1384 +#: ../ProjectController.py:1734 #, python-format msgid "Connection failed to %s!\n" -msgstr "Verbindung zu %s! gescheitert!\n" - -#: ../connectors/PYRO/__init__.py:63 -#, fuzzy, python-format +msgstr "Verbindung zu %s gescheitert!\n" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Verbindung verloren!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format msgid "Connection to '%s' failed.\n" -msgstr "C Kompilierung von %s fehlgeschlagen.\n" - -#: ../dialogs/ConnectionDialog.py:56 -#, fuzzy +msgstr "Verbindung zu '%s' gescheitert.\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 msgid "Connector" -msgstr "Verbinden" - -#: ../dialogs/SFCStepDialog.py:58 -#, fuzzy +msgstr "Anschluss" + +#: ../dialogs/SFCStepDialog.py:66 msgid "Connectors:" -msgstr "Verbinden" - -#: ../controls/VariablePanel.py:65 +msgstr "Anschlüsse:" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "Konsole" + +#: ../controls/VariablePanel.py:60 msgid "Constant" -msgstr "" - -#: ../editors/Viewer.py:477 +msgstr "Konstante" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 msgid "Contact" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:197 +msgstr "Kontakt" + +#: ../controls/ProjectPropertiesPanel.py:198 msgid "Content Description (optional):" -msgstr "" - -#: ../dialogs/ConnectionDialog.py:61 +msgstr "Beschreibung (optional):" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 msgid "Continuation" -msgstr "" +msgstr "Fortsetzung" #: ../plcopen/iec_std.csv:18 msgid "Conversion from BCD" -msgstr "" +msgstr "Konvertierung von BCD" #: ../plcopen/iec_std.csv:19 msgid "Conversion to BCD" -msgstr "" +msgstr "Konvertierung in BCD" #: ../plcopen/iec_std.csv:21 msgid "Conversion to date" -msgstr "" +msgstr "Umwandlung in Datum" #: ../plcopen/iec_std.csv:20 msgid "Conversion to time-of-day" -msgstr "" - -#: ../IDEFrame.py:348 -#: ../IDEFrame.py:401 -#: ../editors/Viewer.py:536 +msgstr "Umwandlung in Tageszeit" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 msgid "Copy" -msgstr "" - -#: ../IDEFrame.py:1961 +msgstr "Kopieren" + +#: ../IDEFrame.py:1933 msgid "Copy POU" -msgstr "" - -#: ../editors/FileManagementPanel.py:283 +msgstr "Baustein kopieren" + +#: ../editors/FileManagementPanel.py:65 msgid "Copy file from left folder to right" -msgstr "" - -#: ../editors/FileManagementPanel.py:282 +msgstr "Datei vom linken Ordner nach den rechten kopieren" + +#: ../editors/FileManagementPanel.py:64 msgid "Copy file from right folder to left" -msgstr "" +msgstr "Datei vom rechten Ordner nach den linken kopieren" #: ../plcopen/iec_std.csv:28 msgid "Cosine" -msgstr "" - -#: ../ConfigTreeNode.py:582 -#, python-format -msgid "" -"Could not add child \"%s\", type %s :\n" -"%s\n" -msgstr "" -"Konnte Zweig nicht hinzufügen \"%s\", type %s :\n" -"%s\n" - -#: ../ConfigTreeNode.py:559 -#, fuzzy, python-format -msgid "" -"Couldn't load confnode base parameters %s :\n" -" %s" -msgstr "" -"Konnte PlugIn Basis Parameter %s nicht laden :\n" -" %s" - -#: ../ConfigTreeNode.py:570 -#, fuzzy, python-format -msgid "" -"Couldn't load confnode parameters %s :\n" -" %s" -msgstr "" -"Konnte PlugIn Parameter %s nicht laden :\n" -" %s" - -#: ../PLCControler.py:765 -#: ../PLCControler.py:802 +msgstr "Kosinus" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" +"Kind \"{a1}\", Typ {a2} konnte nicht hinzugefügt werden:\n" +"{a3}\n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Die alte Datei %s konnte nicht importiert weden." + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" +"Confnode Basis-Parameter {a1} konnten nicht geladen werden :\n" +"{a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" +"Confnode Parameter {a1} konnten nicht geladen werden :\n" +"{a2}" + +#: ../PLCControler.py:948 msgid "Couldn't paste non-POU object." -msgstr "" - -#: ../ProjectController.py:1317 +msgstr "Nur Bausteine können eingefügt werden." + +#: ../ProjectController.py:1651 msgid "Couldn't start PLC !\n" msgstr "Konnte SPS nicht starten !\n" -#: ../ProjectController.py:1325 +#: ../ProjectController.py:1659 msgid "Couldn't stop PLC !\n" msgstr "Konnte SPS nicht anhalten !\n" -#: ../ProjectController.py:1295 -#, fuzzy +#: ../ProjectController.py:1623 msgid "Couldn't stop debugger.\n" -msgstr "Konnte SPS nicht anhalten !\n" - -#: ../svgui/svgui.py:22 +msgstr "Debugger kann nicht gestoppt werden.\n" + +#: ../svgui/svgui.py:49 msgid "Create HMI" -msgstr "Erstelle HMI" - -#: ../dialogs/PouDialog.py:43 +msgstr "HMI erstellen" + +#: ../dialogs/PouDialog.py:46 msgid "Create a new POU" -msgstr "" +msgstr "Neuer Baustein" #: ../dialogs/PouActionDialog.py:38 msgid "Create a new action" -msgstr "" - -#: ../IDEFrame.py:135 +msgstr "Neue Aktion" + +#: ../IDEFrame.py:159 msgid "Create a new action block" -msgstr "" - -#: ../IDEFrame.py:84 -#: ../IDEFrame.py:114 -#: ../IDEFrame.py:147 +msgstr "Neuer Aktionsblock" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 msgid "Create a new block" -msgstr "" - -#: ../IDEFrame.py:108 +msgstr "Neuer Funktionsblock" + +#: ../IDEFrame.py:132 msgid "Create a new branch" -msgstr "" - -#: ../IDEFrame.py:102 +msgstr "Neuer Zweig" + +#: ../IDEFrame.py:126 msgid "Create a new coil" -msgstr "" - -#: ../IDEFrame.py:78 -#: ../IDEFrame.py:93 +msgstr "Neue Spule" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "Neuer Kommentar" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "Neue Verbindung" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "Neuer Kontakt" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "Neue Verzweigung" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "Neue Verzweigung" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "Erster Schritt" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "Neuer Sprung" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "Neue Stomleitung" + #: ../IDEFrame.py:123 -msgid "Create a new comment" -msgstr "" - -#: ../IDEFrame.py:87 -#: ../IDEFrame.py:117 -#: ../IDEFrame.py:150 -msgid "Create a new connection" -msgstr "" - -#: ../IDEFrame.py:105 -#: ../IDEFrame.py:156 -msgid "Create a new contact" -msgstr "" - -#: ../IDEFrame.py:138 -msgid "Create a new divergence" -msgstr "" - -#: ../dialogs/SFCDivergenceDialog.py:36 -msgid "Create a new divergence or convergence" -msgstr "" - -#: ../IDEFrame.py:126 -msgid "Create a new initial step" -msgstr "" - -#: ../IDEFrame.py:141 -msgid "Create a new jump" -msgstr "" - -#: ../IDEFrame.py:96 +msgid "Create a new rung" +msgstr "Neues KOP-Netzwerk" + #: ../IDEFrame.py:153 -msgid "Create a new power rail" -msgstr "" - -#: ../IDEFrame.py:99 -msgid "Create a new rung" -msgstr "" - -#: ../IDEFrame.py:129 msgid "Create a new step" -msgstr "" - -#: ../IDEFrame.py:132 -#: ../dialogs/PouTransitionDialog.py:42 +msgstr "Neuer Schritt" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 msgid "Create a new transition" -msgstr "" - -#: ../IDEFrame.py:81 -#: ../IDEFrame.py:111 -#: ../IDEFrame.py:144 +msgstr "Neue Transitionsbedingung" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 msgid "Create a new variable" -msgstr "" - -#: ../IDEFrame.py:346 -#: ../IDEFrame.py:400 -#: ../editors/Viewer.py:535 +msgstr "Erstellen eine neue Variable" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Mitwirkende" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Arbeitsverzeichnis :" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 msgid "Cut" -msgstr "" - -#: ../editors/ResourceEditor.py:71 +msgstr "Ausschneiden" + +#: ../editors/ResourceEditor.py:72 msgid "Cyclic" -msgstr "" - -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:58 +msgstr "Zyklisch" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 #: ../plcopen/iec_std.csv:60 msgid "DEPRECATED" -msgstr "" - -#: ../canfestival/SlaveEditor.py:50 -#: ../canfestival/NetworkEditor.py:80 +msgstr "VERALTET" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 msgid "DS-301 Profile" -msgstr "" - -#: ../canfestival/SlaveEditor.py:51 -#: ../canfestival/NetworkEditor.py:81 +msgstr "DS-301 Profil" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 msgid "DS-302 Profile" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:43 -#, fuzzy +msgstr "DS-302 Profil" + +#: ../dialogs/SearchInProjectDialog.py:36 msgid "Data Type" -msgstr "ZielTyp" - -#: ../PLCControler.py:95 -#, fuzzy +msgstr "Datentyp" + +#: ../PLCControler.py:98 msgid "Data Types" -msgstr "ZielTyp" +msgstr "Datentypen" #: ../plcopen/iec_std.csv:16 msgid "Data type conversion" -msgstr "" - -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:45 +msgstr "Datentypumwandlung" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 msgid "Date addition" -msgstr "" - -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:57 -#: ../plcopen/iec_std.csv:58 -#: ../plcopen/iec_std.csv:59 +msgstr "Datumsaddition" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 msgid "Date and time subtraction" -msgstr "" - -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:51 +msgstr "Datums- und Zeitsubstraktion" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 msgid "Date subtraction" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:43 +msgstr "Datumssubstraktion" + +#: ../dialogs/DurationEditorDialog.py:44 msgid "Days:" -msgstr "" - -#: ../ProjectController.py:1405 -msgid "Debug connect matching running PLC\n" -msgstr "Debug Verbindung entspricht laufender SPS\n" - -#: ../ProjectController.py:1408 -msgid "Debug do not match PLC - stop/transfert/start to re-enable\n" -msgstr "Debug entspricht nicht der SPS - stop/transfert/start um neu zu aktivieren\n" - -#: ../controls/PouInstanceVariablesPanel.py:52 -#, fuzzy +msgstr "Tage:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" +"Debug entspricht der SPS nicht - stoppen/übertragen/starten um Debug wieder " +"zu aktivieren\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 msgid "Debug instance" -msgstr "Debugger deaktiviert\n" - -#: ../editors/Viewer.py:3222 +msgstr "Debuginstanz" + +#: ../editors/Viewer.py:448 #, python-format msgid "Debug: %s" -msgstr "" - -#: ../ProjectController.py:1122 -#, fuzzy, python-format +msgstr "Debug: %s" + +#: ../ProjectController.py:1412 +#, python-format msgid "Debug: Unknown variable '%s'\n" -msgstr "Debug : Unbekannte Variable %s\n" - -#: ../ProjectController.py:1120 +msgstr "Debug: Unbekannte Variable '%s'\n" + +#: ../ProjectController.py:1410 #, python-format msgid "Debug: Unsupported type to debug '%s'\n" -msgstr "" - -#: ../IDEFrame.py:608 -#, fuzzy +msgstr "Debug: Datentyp '%s' ist nicht unterstützt\n" + +#: ../IDEFrame.py:639 msgid "Debugger" -msgstr "Debug_Modus" - -#: ../ProjectController.py:1285 +msgstr "Debugger" + +#: ../ProjectController.py:1592 msgid "Debugger disabled\n" msgstr "Debugger deaktiviert\n" -#: ../ProjectController.py:1297 -#, fuzzy +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "Debugger ist bereit\n" + +#: ../ProjectController.py:1625 msgid "Debugger stopped.\n" -msgstr "Debugger deaktiviert\n" - -#: ../IDEFrame.py:1990 -#: ../Beremiz.py:958 -#: ../editors/Viewer.py:511 +msgstr "Debugger ist gestoppt.\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 msgid "Delete" -msgstr "" - -#: ../editors/Viewer.py:454 +msgstr "Löschen" + +#: ../editors/Viewer.py:573 msgid "Delete Divergence Branch" -msgstr "" - -#: ../editors/FileManagementPanel.py:371 -#, fuzzy +msgstr "Zweig löschen" + +#: ../editors/FileManagementPanel.py:153 msgid "Delete File" -msgstr "lösche dieses PlugIn" - -#: ../editors/Viewer.py:443 +msgstr "Datei löschen" + +#: ../editors/Viewer.py:560 msgid "Delete Wire Segment" -msgstr "" +msgstr "Kabelabschnitt löschen" #: ../controls/CustomEditableListBox.py:41 msgid "Delete item" -msgstr "" +msgstr "Object löschen" #: ../plcopen/iec_std.csv:88 msgid "Deletion (within)" -msgstr "" - -#: ../editors/DataTypeEditor.py:146 +msgstr "Löschen (innerhalb)" + +#: ../editors/DataTypeEditor.py:153 msgid "Derivation Type:" -msgstr "" - -#: ../plcopen/structures.py:264 -msgid "" -"Derivative\n" -"The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." -msgstr "" - -#: ../controls/VariablePanel.py:360 +msgstr "Referenz:" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Beschreibung" + +#: ../controls/VariablePanel.py:432 msgid "Description:" -msgstr "" - -#: ../editors/DataTypeEditor.py:314 -#: ../dialogs/ArrayTypeDialog.py:61 +msgstr "Beschreibung:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 msgid "Dimensions:" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:61 +msgstr "Dimensionen:" + +#: ../dialogs/FindInPouDialog.py:66 msgid "Direction" -msgstr "" - -#: ../dialogs/BrowseLocationsDialog.py:78 +msgstr "Richtung" + +#: ../dialogs/BrowseLocationsDialog.py:91 msgid "Direction:" -msgstr "" - -#: ../editors/DataTypeEditor.py:52 +msgstr "Richtung:" + +#: ../editors/DataTypeEditor.py:54 msgid "Directly" -msgstr "" - -#: ../ProjectController.py:1512 +msgstr "Direkt" + +#: ../ProjectController.py:1860 msgid "Disconnect" msgstr "Verbindung trennen" -#: ../ProjectController.py:1514 +#: ../ProjectController.py:1862 msgid "Disconnect from PLC" msgstr "Verbindung zu SPS trennen" -#: ../editors/Viewer.py:496 +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "Getrennt" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 msgid "Divergence" -msgstr "" +msgstr "Verzweigung" #: ../plcopen/iec_std.csv:36 msgid "Division" -msgstr "" - -#: ../editors/FileManagementPanel.py:370 +msgstr "Division" + +#: ../editors/FileManagementPanel.py:152 #, python-format msgid "Do you really want to delete the file '%s'?" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +msgstr "Möchten Sie die Datei '%s' wirklich löschen?" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Documentation" -msgstr "" - -#: ../PLCOpenEditor.py:351 +msgstr "Dokumentation" + +#: ../PLCOpenEditor.py:338 msgid "Done" -msgstr "" - -#: ../plcopen/structures.py:227 -msgid "" -"Down-counter\n" -"The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "Fertig" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Duration" -msgstr "" - -#: ../canfestival/canfestival.py:118 -#, fuzzy +msgstr "Zeitangabe" + +#: ../canfestival/canfestival.py:165 msgid "EDS files (*.eds)|*.eds|All files|*.*" -msgstr "SVG files (*.svg)|*.svg|Alle Dateien|*.*" - -#: ../editors/Viewer.py:510 -#, fuzzy +msgstr "EDS Dateien (*.eds)|*.eds|Alle Dateien|*.*" + +#: ../editors/Viewer.py:629 msgid "Edit Block" -msgstr "bearbeite Netzwerk" - -#: ../dialogs/LDElementDialog.py:41 -#, fuzzy +msgstr "Funktionsblock bearbeiten" + +#: ../dialogs/LDElementDialog.py:56 msgid "Edit Coil Values" -msgstr "bearbeite C-Datei" - -#: ../dialogs/LDElementDialog.py:38 -#, fuzzy +msgstr "Spule bearbeiten" + +#: ../dialogs/LDElementDialog.py:54 msgid "Edit Contact Values" -msgstr "bearbeite C-Datei" +msgstr "Kontaktwerte bearbeiten" #: ../dialogs/DurationEditorDialog.py:59 msgid "Edit Duration" -msgstr "" - -#: ../dialogs/SFCStepDialog.py:35 -#, fuzzy +msgstr "Zeitangabe ändern" + +#: ../dialogs/SFCStepDialog.py:51 msgid "Edit Step" -msgstr "bearbeite C-Datei" - -#: ../wxglade_hmi/wxglade_hmi.py:12 +msgstr "Schritt bearbeiten" + +#: ../wxglade_hmi/wxglade_hmi.py:38 msgid "Edit a WxWidgets GUI with WXGlade" -msgstr "bearbeite eine wxWidgets GUI mit wxGlade" - -#: ../dialogs/ActionBlockDialog.py:122 +msgstr "Eine wxWidgets GUI mit wxGlade bearbeiten" + +#: ../dialogs/ActionBlockDialog.py:121 msgid "Edit action block properties" -msgstr "" - -#: ../dialogs/ArrayTypeDialog.py:45 +msgstr "Aktionsblockeigenschaften bearbeiten" + +#: ../dialogs/ArrayTypeDialog.py:44 msgid "Edit array type properties" -msgstr "" - -#: ../editors/Viewer.py:2112 -#: ../editors/Viewer.py:2114 -#: ../editors/Viewer.py:2630 -#: ../editors/Viewer.py:2632 +msgstr "Array-Typeigenschaften bearbeiten" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 msgid "Edit comment" -msgstr "" - -#: ../editors/FileManagementPanel.py:284 -#, fuzzy +msgstr "Kommentar bearbeiten" + +#: ../editors/FileManagementPanel.py:66 msgid "Edit file" -msgstr "bearbeite C-Datei" +msgstr "Datei bearbeiten" #: ../controls/CustomEditableListBox.py:39 -#, fuzzy msgid "Edit item" -msgstr "bearbeite C-Datei" - -#: ../editors/Viewer.py:2594 +msgstr "Objekt bearbeiten" + +#: ../editors/Viewer.py:3014 msgid "Edit jump target" -msgstr "" - -#: ../ProjectController.py:1526 +msgstr "Sprungziel bearbeiten" + +#: ../ProjectController.py:1874 msgid "Edit raw IEC code added to code generated by PLCGenerator" -msgstr "Bearbeite hinzugefügten Roh-IEC Code, der vom PLCGenerator generiert wurde" - -#: ../editors/SFCViewer.py:725 +msgstr "" +"Bearbeite hinzugefügten Roh-IEC Code, der vom PLCGenerator generiert wurde" + +#: ../editors/SFCViewer.py:799 msgid "Edit step name" -msgstr "" - -#: ../dialogs/SFCTransitionDialog.py:38 +msgstr "Schrittname bearbeiten" + +#: ../dialogs/SFCTransitionDialog.py:52 msgid "Edit transition" -msgstr "" - -#: ../IDEFrame.py:580 +msgstr "Transitionsbedingung bearbeiten" + +#: ../IDEFrame.py:611 msgid "Editor ToolBar" -msgstr "" - -#: ../ProjectController.py:1013 +msgstr "Werkzeugleiste bearbeiten" + +#: ../ProjectController.py:1257 msgid "Editor selection" -msgstr "" - -#: ../editors/DataTypeEditor.py:341 +msgstr "Editor-Auswahl" + +#: ../editors/DataTypeEditor.py:348 msgid "Elements :" -msgstr "" - -#: ../IDEFrame.py:343 -#, fuzzy +msgstr "Komponente:" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "Leer" + +#: ../IDEFrame.py:365 msgid "Enable Undo/Redo" -msgstr "Aktiviert" - -#: ../Beremiz_service.py:380 +msgstr "Wiederherstellen/Rückgängig aktivieren" + +#: ../Beremiz_service.py:333 msgid "Enter a name " msgstr "Geben Sie einen Namen ein" -#: ../Beremiz_service.py:365 +#: ../Beremiz_service.py:318 msgid "Enter a port number " msgstr "Geben Sie eine Port-Nummer ein" -#: ../Beremiz_service.py:355 +#: ../Beremiz_service.py:309 msgid "Enter the IP of the interface to bind" msgstr "Geben Sie die IP-Adresse des anzubindenden Interfaces ein" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Enumerated" -msgstr "" +msgstr "Aufzählung" #: ../plcopen/iec_std.csv:77 msgid "Equal to" -msgstr "" - -#: ../Beremiz_service.py:270 -#: ../Beremiz_service.py:394 -#: ../controls/VariablePanel.py:330 -#: ../controls/VariablePanel.py:678 -#: ../controls/DebugVariablePanel.py:164 -#: ../IDEFrame.py:1083 -#: ../IDEFrame.py:1672 -#: ../IDEFrame.py:1709 -#: ../IDEFrame.py:1714 -#: ../IDEFrame.py:1728 -#: ../IDEFrame.py:1733 -#: ../IDEFrame.py:2422 -#: ../Beremiz.py:1083 -#: ../PLCOpenEditor.py:358 -#: ../PLCOpenEditor.py:363 -#: ../PLCOpenEditor.py:531 -#: ../PLCOpenEditor.py:541 -#: ../editors/TextViewer.py:376 -#: ../editors/DataTypeEditor.py:543 -#: ../editors/DataTypeEditor.py:548 -#: ../editors/DataTypeEditor.py:572 -#: ../editors/DataTypeEditor.py:577 -#: ../editors/DataTypeEditor.py:587 -#: ../editors/DataTypeEditor.py:719 -#: ../editors/DataTypeEditor.py:726 -#: ../editors/Viewer.py:366 -#: ../editors/LDViewer.py:663 -#: ../editors/LDViewer.py:879 -#: ../editors/LDViewer.py:883 -#: ../editors/FileManagementPanel.py:210 -#: ../ProjectController.py:221 -#: ../dialogs/PouNameDialog.py:53 -#: ../dialogs/PouTransitionDialog.py:107 -#: ../dialogs/BrowseLocationsDialog.py:175 -#: ../dialogs/ProjectDialog.py:71 -#: ../dialogs/SFCStepNameDialog.py:59 -#: ../dialogs/ConnectionDialog.py:152 -#: ../dialogs/FBDVariableDialog.py:201 -#: ../dialogs/PouActionDialog.py:104 +msgstr "Gleich" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 #: ../dialogs/BrowseValuesLibraryDialog.py:83 -#: ../dialogs/PouDialog.py:132 -#: ../dialogs/SFCTransitionDialog.py:147 -#: ../dialogs/DurationEditorDialog.py:121 -#: ../dialogs/DurationEditorDialog.py:163 -#: ../dialogs/SearchInProjectDialog.py:157 -#: ../dialogs/SFCStepDialog.py:130 -#: ../dialogs/ArrayTypeDialog.py:97 -#: ../dialogs/ArrayTypeDialog.py:103 -#: ../dialogs/FBDBlockDialog.py:164 -#: ../dialogs/ForceVariableDialog.py:169 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 msgid "Error" msgstr "Fehler" -#: ../ProjectController.py:587 -msgid "Error : At least one configuration and one resource must be declared in PLC !\n" -msgstr "Fehler : Mindestens eine Konfiguration und eine Ressource müssen in der SPS deklariert sein!\n" - -#: ../ProjectController.py:579 +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" +"Fehler : Mindestens eine Konfiguration und eine Ressource müssen in der SPS " +"deklariert sein!\n" + +#: ../ProjectController.py:781 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "Fehler : IEC nach C Compiler gab folgendes zurück: %d\n" -#: ../ProjectController.py:520 +#: ../ProjectController.py:712 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" "%s\n" msgstr "" -"Fehler in ST/IL/SFC Code-Generator :\n" +"Fehler in ST/AWL/AS Code-Generator :\n" "%s\n" -#: ../ConfigTreeNode.py:182 +#: ../ConfigTreeNode.py:216 #, python-format msgid "Error while saving \"%s\"\n" msgstr "Fehler während der Speicherung von \"%s\"\n" -#: ../canfestival/canfestival.py:122 +#: ../canfestival/canfestival.py:170 msgid "Error: Export slave failed\n" -msgstr "" - -#: ../canfestival/canfestival.py:270 +msgstr "Fehler: Slave Export fehlgeschlagen\n" + +#: ../canfestival/canfestival.py:371 msgid "Error: No Master generated\n" msgstr "Fehler: Kein Master angelegt\n" -#: ../canfestival/canfestival.py:265 +#: ../canfestival/canfestival.py:366 msgid "Error: No PLC built\n" msgstr "Fehler: Kein SPS built\n" -#: ../ProjectController.py:1378 +#: ../ProjectController.py:1728 #, python-format msgid "Exception while connecting %s!\n" msgstr "Fehler beim verbinden von %s!\n" -#: ../dialogs/FBDBlockDialog.py:95 +#: ../dialogs/FBDBlockDialog.py:120 msgid "Execution Control:" -msgstr "" - -#: ../dialogs/FBDVariableDialog.py:76 -#: ../dialogs/FBDBlockDialog.py:87 +msgstr "Ausführungssteuerung:" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 msgid "Execution Order:" -msgstr "" - -#: ../features.py:10 +msgstr "Ausführungsreihenfolge:" + +#: ../features.py:35 msgid "Experimental web based HMI" -msgstr "" +msgstr "Experimentelle web-basierte HMI" #: ../plcopen/iec_std.csv:38 msgid "Exponent" -msgstr "" +msgstr "Exponent" #: ../plcopen/iec_std.csv:26 msgid "Exponentiation" -msgstr "" - -#: ../canfestival/canfestival.py:128 +msgstr "Exponentialfunktion des Operanden" + +#: ../canfestival/canfestival.py:176 msgid "Export CanOpen slave to EDS file" -msgstr "" - -#: ../editors/GraphicViewer.py:144 +msgstr "CanOpen-Slave in EDS-Datei exportieren" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 msgid "Export graph values to clipboard" -msgstr "" - -#: ../canfestival/canfestival.py:127 +msgstr "Graphenwerte in Zwischenablage exportieren" + +#: ../canfestival/canfestival.py:175 msgid "Export slave" -msgstr "" - -#: ../dialogs/FBDVariableDialog.py:69 -#, fuzzy +msgstr "Slave exportieren" + +#: ../dialogs/FBDVariableDialog.py:90 msgid "Expression:" -msgstr "CExtension" - -#: ../controls/VariablePanel.py:77 +msgstr "Ausdruck:" + +#: ../controls/VariablePanel.py:72 msgid "External" -msgstr "" - -#: ../ProjectController.py:591 +msgstr "External" + +#: ../ProjectController.py:802 msgid "Extracting Located Variables...\n" msgstr "Extrahiere gefundene Variablen...\n" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "FBD" -msgstr "" - -#: ../ProjectController.py:1445 +msgstr "FUP" + +#: ../ProjectController.py:1791 msgid "Failed : Must build before transfer.\n" msgstr "Fehler : Sie müssen kompillieren vor dem Transfer.\n" -#: ../editors/Viewer.py:405 -#: ../dialogs/LDElementDialog.py:84 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 msgid "Falling Edge" -msgstr "" - -#: ../plcopen/structures.py:217 -msgid "" -"Falling edge detector\n" -"The output produces a single pulse when a falling edge is detected." -msgstr "" - -#: ../ProjectController.py:900 +msgstr "Fallende Flanke" + +#: ../ProjectController.py:1070 msgid "Fatal : cannot get builder.\n" msgstr "schwerer Fehler : Kann den Builder nicht finden.\n" -#: ../dialogs/DurationEditorDialog.py:160 +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "Lade %s" + +#: ../dialogs/DurationEditorDialog.py:164 #, python-format msgid "Field %s hasn't a valid value!" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:162 +msgstr "Das Feld %s enthält keinen gültigen Wert!" + +#: ../dialogs/DurationEditorDialog.py:166 #, python-format msgid "Fields %s haven't a valid value!" -msgstr "" - -#: ../editors/FileManagementPanel.py:209 -#, fuzzy, python-format +msgstr "Die Felder %s enthalten keine gültigen Werte!" + +#: ../controls/FolderTree.py:216 +#, python-format msgid "File '%s' already exists!" -msgstr "Ein Zweigname \"%s\" existiert bereits -> \"%s\"\n" - -#: ../IDEFrame.py:353 -#: ../dialogs/FindInPouDialog.py:30 -#: ../dialogs/FindInPouDialog.py:99 +msgstr "Datei '%s' existiert bereits!" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 msgid "Find" -msgstr "" - -#: ../IDEFrame.py:355 +msgstr "Suchen" + +#: ../IDEFrame.py:377 msgid "Find Next" -msgstr "" - -#: ../IDEFrame.py:357 +msgstr "Weiter suchen" + +#: ../IDEFrame.py:379 msgid "Find Previous" -msgstr "" +msgstr "Rückwärts suchen" #: ../plcopen/iec_std.csv:90 msgid "Find position" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:51 +msgstr "Position suchen" + +#: ../dialogs/FindInPouDialog.py:55 msgid "Find:" -msgstr "" - -#: ../connectors/PYRO/__init__.py:125 +msgstr "Suchen:" + +#: ../connectors/PYRO/__init__.py:163 msgid "Force runtime reload\n" msgstr "Erzwinge neues laden der Runtime\n" -#: ../controls/DebugVariablePanel.py:295 -#: ../editors/Viewer.py:1353 +#: ../editors/Viewer.py:1600 msgid "Force value" -msgstr "" - -#: ../dialogs/ForceVariableDialog.py:152 +msgstr "Wert forcen" + +#: ../dialogs/ForceVariableDialog.py:162 msgid "Forcing Variable Value" -msgstr "" - -#: ../dialogs/PouTransitionDialog.py:97 -#: ../dialogs/ProjectDialog.py:70 -#: ../dialogs/PouActionDialog.py:94 -#: ../dialogs/PouDialog.py:114 -#: ../dialogs/SFCTransitionDialog.py:147 +msgstr "Variable forcen" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 #, python-format msgid "Form isn't complete. %s must be filled!" -msgstr "" - -#: ../dialogs/ConnectionDialog.py:142 -#: ../dialogs/FBDBlockDialog.py:154 +msgstr "Formular unvollständig. %s muss ausgefüllt werden!" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 msgid "Form isn't complete. Name must be filled!" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:145 -msgid "Form isn't complete. Pattern to search must be filled!" -msgstr "" - -#: ../dialogs/FBDBlockDialog.py:152 +msgstr "Formular unvollständig. Der Name muss ausgefüllt werden!" + +#: ../dialogs/FBDBlockDialog.py:232 msgid "Form isn't complete. Valid block type must be selected!" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:67 +msgstr "Formular unvollständig. Bausteintyp muss ausgewählt werden!" + +#: ../dialogs/FindInPouDialog.py:72 msgid "Forward" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:44 -#, fuzzy +msgstr "Vorwärts" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 msgid "Function" -msgstr "Funktion : " - -#: ../IDEFrame.py:329 -#, fuzzy +msgstr "Funktion" + +#: ../IDEFrame.py:349 msgid "Function &Block" -msgstr "Funktion : " - -#: ../IDEFrame.py:1969 -#: ../dialogs/SearchInProjectDialog.py:45 -#, fuzzy +msgstr "Funktions&block" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 msgid "Function Block" -msgstr "Funktion : " - -#: ../controls/VariablePanel.py:741 +msgstr "Funktionsblock" + +#: ../controls/VariablePanel.py:854 msgid "Function Block Types" -msgstr "" - -#: ../PLCControler.py:94 -#, fuzzy +msgstr "Funktionsblocktypen" + +#: ../PLCControler.py:97 msgid "Function Blocks" -msgstr "Funktion : " - -#: ../editors/Viewer.py:236 +msgstr "Funktionsblöcke" + +#: ../editors/Viewer.py:249 msgid "Function Blocks can't be used in Functions!" -msgstr "" - -#: ../editors/Viewer.py:238 -msgid "Function Blocks can't be used in Transitions!" -msgstr "" - -#: ../PLCControler.py:2055 +msgstr "Funktionsblock kann nicht in Funktion verwendet werden!" + +#: ../PLCControler.py:2343 #, python-format msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" -msgstr "" - -#: ../PLCControler.py:94 -#, fuzzy +msgstr "Funktionsblock \"%s\" kann nicht in eine Funktion eingefügt werden!!!" + +#: ../PLCControler.py:97 msgid "Functions" -msgstr "Funktion : " - -#: ../PLCOpenEditor.py:138 +msgstr "Funktionen" + +#: ../PLCOpenEditor.py:117 msgid "Generate Program" -msgstr "" - -#: ../ProjectController.py:510 +msgstr "Programm generieren" + +#: ../ProjectController.py:703 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" -msgstr "Generiere SoftPLC IEC-61131 ST/IL/SFC Code...\n" - -#: ../controls/VariablePanel.py:78 +msgstr "Generiere SoftPLC IEC-61131 ST/AWL/AS Code...\n" + +#: ../controls/VariablePanel.py:73 msgid "Global" -msgstr "" - -#: ../editors/GraphicViewer.py:131 +msgstr "Global" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 msgid "Go to current value" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:173 +msgstr "Zum aktuellen Wert wechseln" + +#: ../controls/ProjectPropertiesPanel.py:174 msgid "Graphics" -msgstr "" +msgstr "Grafiken" #: ../plcopen/iec_std.csv:75 msgid "Greater than" -msgstr "" +msgstr "Größer als" #: ../plcopen/iec_std.csv:76 msgid "Greater than or equal to" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:134 +msgstr "Größer oder gleich" + +#: ../controls/ProjectPropertiesPanel.py:135 msgid "Grid Resolution:" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:120 +msgstr "Gitterauflösung:" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "HTTP interface port :" + +#: ../controls/ProjectPropertiesPanel.py:121 msgid "Height:" -msgstr "" - -#: ../editors/FileManagementPanel.py:303 +msgstr "Höhe:" + +#: ../editors/FileManagementPanel.py:85 msgid "Home Directory:" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:150 +msgstr "Homeverzeichnis:" + +#: ../controls/ProjectPropertiesPanel.py:151 msgid "Horizontal:" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:44 +msgstr "Horizontal:" + +#: ../dialogs/DurationEditorDialog.py:45 msgid "Hours:" -msgstr "" - -#: ../plcopen/structures.py:279 -msgid "" -"Hysteresis\n" -"The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." -msgstr "" - -#: ../ProjectController.py:827 -msgid "IEC-61131-3 code generation failed !\n" -msgstr "IEC-61131-3 Code Generierung gescheitert !\n" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +msgstr "Stunden:" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "IL" -msgstr "" - -#: ../Beremiz_service.py:356 -#: ../Beremiz_service.py:357 +msgstr "AWL" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "IP" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 msgid "IP is not valid!" msgstr "IP ist nicht gültig!" -#: ../svgui/svgui.py:17 -#: ../svgui/svgui.py:18 +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 msgid "Import SVG" msgstr "Importiere SVG" -#: ../controls/VariablePanel.py:76 -#: ../dialogs/FBDVariableDialog.py:34 +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 msgid "InOut" -msgstr "" - -#: ../controls/VariablePanel.py:263 -#, python-format -msgid "Incompatible data types between \"%s\" and \"%s\"" -msgstr "" - -#: ../controls/VariablePanel.py:274 -#, python-format -msgid "Incompatible size of data between \"%s\" and \"%s\"" -msgstr "" - -#: ../controls/VariablePanel.py:270 +msgstr "Ein- und Ausgang" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "Inaktiv" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "Inkompatible Datentypen zwischen \"{a1}\" und \"{a2}\"" + +#: ../controls/VariablePanel.py:282 #, python-format msgid "Incompatible size of data between \"%s\" and \"BOOL\"" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "Inkompatible Datengröße zwischen \"%s\" und \"BOOL\"" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "Inkompatible Datengröße zwischen \"{a1}\" und \"{a2}\"" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Indicator" -msgstr "" - -#: ../editors/Viewer.py:492 +msgstr "Indikator" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "Anfangswert" + +#: ../editors/Viewer.py:611 msgid "Initial Step" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 +msgstr "Anfangsschritt" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 msgid "Initial Value" -msgstr "" - -#: ../editors/DataTypeEditor.py:178 -#: ../editors/DataTypeEditor.py:209 -#: ../editors/DataTypeEditor.py:265 -#: ../editors/DataTypeEditor.py:303 +msgstr "Anfangswert" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 msgid "Initial Value:" -msgstr "" - -#: ../svgui/svgui.py:21 +msgstr "Anfangswert:" + +#: ../svgui/svgui.py:48 msgid "Inkscape" msgstr "Inkscape" -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCTransitionDialog.py:66 -#: ../dialogs/SFCTransitionDialog.py:137 -#, fuzzy +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 msgid "Inline" -msgstr "Zeile :" - -#: ../controls/VariablePanel.py:76 -#: ../dialogs/BrowseLocationsDialog.py:36 -#: ../dialogs/FBDVariableDialog.py:33 -#: ../dialogs/SFCStepDialog.py:61 +msgstr "Inline" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Input" -msgstr "" - -#: ../dialogs/FBDBlockDialog.py:78 +msgstr "Eingang" + +#: ../dialogs/FBDBlockDialog.py:96 msgid "Inputs:" -msgstr "" +msgstr "Eingänge:" #: ../plcopen/iec_std.csv:87 msgid "Insertion (into)" -msgstr "" - -#: ../plcopen/plcopen.py:1833 +msgstr "Einfügen String in ein anderes String an die entsprechende Position" + +#: ../plcopen/plcopen.py:1696 #, python-format msgid "Instance with id %d doesn't exist!" -msgstr "" - -#: ../editors/ResourceEditor.py:247 +msgstr "Eine Instanz mit der ID%d existiert nicht!" + +#: ../editors/ResourceEditor.py:264 msgid "Instances:" -msgstr "" - -#: ../plcopen/structures.py:259 -msgid "" -"Integral\n" -"The integral function block integrates the value of input XIN over time." -msgstr "" - -#: ../controls/VariablePanel.py:75 +msgstr "Instanzen:" + +#: ../controls/VariablePanel.py:70 msgid "Interface" -msgstr "" - -#: ../editors/ResourceEditor.py:71 +msgstr "Schnittstelle" + +#: ../editors/ResourceEditor.py:72 msgid "Interrupt" -msgstr "" - -#: ../editors/ResourceEditor.py:67 +msgstr "Ereignisgesteuert" + +#: ../editors/ResourceEditor.py:68 msgid "Interval" -msgstr "" - -#: ../PLCControler.py:2032 -#: ../PLCControler.py:2070 +msgstr "Intervall" + +#: ../PLCControler.py:2331 msgid "Invalid plcopen element(s)!!!" -msgstr "" - -#: ../canfestival/config_utils.py:376 -#: ../canfestival/config_utils.py:637 -#, python-format -msgid "Invalid type \"%s\"-> %d != %d for location\"%s\"" -msgstr "Ungültiger Typ \"%s\"-> %d != %d für Ort\"%s\"" - -#: ../dialogs/ForceVariableDialog.py:167 -#, python-format -msgid "Invalid value \"%s\" for \"%s\" variable!" -msgstr "" - -#: ../controls/DebugVariablePanel.py:153 -#: ../controls/DebugVariablePanel.py:156 +msgstr "Ungültige plcopen-Element(en)!!!" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "Ungültiger Datentyp \"{a1}\" -> {a2}! = {a3} für \"{a4}\"" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "Ungültiger Datentyp \"{a1}\" -> {a2}! = {a3} für die Adresse \"{a4}\"" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 #, python-format msgid "Invalid value \"%s\" for debug variable" -msgstr "" - -#: ../controls/VariablePanel.py:244 -#: ../controls/VariablePanel.py:247 +msgstr "Ungültiger Wert \"%s\" für Debug-Variable" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 #, python-format msgid "Invalid value \"%s\" for variable grid element" -msgstr "" - -#: ../editors/Viewer.py:221 -#: ../editors/Viewer.py:224 +msgstr "Ungültiger Wert \"%s\" " + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 #, python-format msgid "Invalid value \"%s\" for viewer block" -msgstr "" +msgstr "Ungültiger Wert \"%s\"" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "Ungültiger Wert \"{a1}\" für Variable \"{a2}\"!" #: ../dialogs/DurationEditorDialog.py:121 msgid "" "Invalid value!\n" "You must fill a numeric value." msgstr "" - -#: ../editors/Viewer.py:497 +"Ungültiger Wert!\n" +"Bitte geben eine Zahl ein." + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 msgid "Jump" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +msgstr "Sprung" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "LD" -msgstr "" - -#: ../editors/LDViewer.py:215 -#: ../editors/LDViewer.py:231 +msgstr "KOP" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 #, python-format msgid "Ladder element with id %d is on more than one rung." -msgstr "" - -#: ../dialogs/PouTransitionDialog.py:86 -#: ../dialogs/PouActionDialog.py:83 -#: ../dialogs/PouDialog.py:102 +msgstr "KOP-Element mit id %d ist auf mehr als einem Netzwerk." + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 msgid "Language" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:186 +msgstr "Sprache" + +#: ../controls/ProjectPropertiesPanel.py:187 msgid "Language (optional):" -msgstr "" - -#: ../dialogs/PouTransitionDialog.py:60 -#: ../dialogs/PouActionDialog.py:56 -#: ../dialogs/PouDialog.py:71 +msgstr "Sprache (optional):" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 msgid "Language:" -msgstr "" - -#: ../ProjectController.py:1451 +msgstr "Sprache:" + +#: ../ProjectController.py:1797 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "Letzter Build entspricht bereits dem Ziel. Übertrage trotzdem...\n" -#: ../Beremiz_service.py:324 +#: ../Beremiz_service.py:273 msgid "Launch WX GUI inspector" msgstr "Starte WX GUI Inspector" -#: ../Beremiz_service.py:323 +#: ../Beremiz_service.py:272 msgid "Launch a live Python shell" msgstr "Starte eine live-Python shell" -#: ../editors/Viewer.py:428 +#: ../editors/Viewer.py:544 msgid "Left" -msgstr "" - -#: ../dialogs/LDPowerRailDialog.py:55 +msgstr "Links" + +#: ../dialogs/LDPowerRailDialog.py:63 msgid "Left PowerRail" -msgstr "" +msgstr "Linke Stromleitung" #: ../plcopen/iec_std.csv:81 msgid "Length of string" -msgstr "" +msgstr "Länge der Zeichenkette" #: ../plcopen/iec_std.csv:78 msgid "Less than" -msgstr "" +msgstr "Kleiner als" #: ../plcopen/iec_std.csv:79 msgid "Less than or equal to" -msgstr "" - -#: ../IDEFrame.py:600 +msgstr "Kleiner oder gleich" + +#: ../IDEFrame.py:631 msgid "Library" -msgstr "" +msgstr "Bibliotheke" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "Lizenz" #: ../plcopen/iec_std.csv:73 msgid "Limitation" -msgstr "" - -#: ../targets/toolchain_gcc.py:142 +msgstr "Limitierung" + +#: ../targets/toolchain_gcc.py:202 msgid "Linking :\n" msgstr "Linking :\n" -#: ../controls/VariablePanel.py:77 -#: ../dialogs/DiscoveryDialog.py:110 +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 msgid "Local" msgstr "Lokal" -#: ../ProjectController.py:1353 +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "Lokale Einträge" + +#: ../ProjectController.py:1703 msgid "Local service discovery failed!\n" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#, fuzzy +msgstr "Lokale Service discovery fehlgeschlagen!\n" + +#: ../controls/VariablePanel.py:53 msgid "Location" -msgstr "URI_location" - -#: ../dialogs/BrowseLocationsDialog.py:61 -#, fuzzy +msgstr "IEC-Adresse" + +#: ../dialogs/BrowseLocationsDialog.py:72 msgid "Locations available:" -msgstr "Service verfügbar:" - -#: ../Beremiz.py:393 -msgid "Log Console" -msgstr "Log Konsole" +msgstr "Verfügbare IEC-Adressen:" #: ../plcopen/iec_std.csv:25 msgid "Logarithm to base 10" -msgstr "" - -#: ../connectors/PYRO/__init__.py:55 +msgstr "Logarithmus zur Basis 10" + +#: ../connectors/PYRO/__init__.py:94 #, python-format msgid "MDNS resolution failure for '%s'\n" -msgstr "" - -#: ../canfestival/SlaveEditor.py:37 -#: ../canfestival/NetworkEditor.py:67 +msgstr "MDNS-Auflösungsfehler für '%s'\n" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 msgid "Map Variable" -msgstr "" - -#: ../features.py:6 +msgstr "Map Variable" + +#: ../features.py:31 msgid "Map located variables over CANopen" -msgstr "" - -#: ../canfestival/NetworkEditor.py:89 -#, fuzzy +msgstr "Variablen über CANopen mappen" + +#: ../canfestival/NetworkEditor.py:106 msgid "Master" -msgstr "Zeige Master" - -#: ../ConfigTreeNode.py:480 -#, fuzzy, python-format -msgid "Max count (%d) reached for this confnode of type %s " -msgstr "Maximale Zahl (%d) erreicht für dieses PlugIn des Typs %s " +msgstr "Master" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "Maximale Anzahl ({a1}) ist erreicht für diesen confnode des Typs {a2}" #: ../plcopen/iec_std.csv:71 msgid "Maximum" -msgstr "" - -#: ../editors/DataTypeEditor.py:232 +msgstr "Maximum" + +#: ../editors/DataTypeEditor.py:239 msgid "Maximum:" -msgstr "" - -#: ../dialogs/BrowseLocationsDialog.py:38 +msgstr "Maximum:" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Memory" -msgstr "" - -#: ../IDEFrame.py:568 +msgstr "Speicher" + +#: ../IDEFrame.py:599 msgid "Menu ToolBar" -msgstr "" +msgstr "Menüleiste" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "Mikrosekunden:" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "Mitte" #: ../dialogs/DurationEditorDialog.py:48 -msgid "Microseconds:" -msgstr "" - -#: ../editors/Viewer.py:433 -msgid "Middle" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:47 msgid "Milliseconds:" -msgstr "" +msgstr "Millisekunden:" #: ../plcopen/iec_std.csv:72 msgid "Minimum" -msgstr "" - -#: ../editors/DataTypeEditor.py:219 +msgstr "Minimum" + +#: ../editors/DataTypeEditor.py:226 msgid "Minimum:" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:45 +msgstr "Minimum:" + +#: ../dialogs/DurationEditorDialog.py:46 msgid "Minutes:" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:210 +msgstr "Minuten:" + +#: ../controls/ProjectPropertiesPanel.py:211 msgid "Miscellaneous" -msgstr "" - -#: ../dialogs/LDElementDialog.py:59 +msgstr "Sonstiges" + +#: ../dialogs/LDElementDialog.py:63 msgid "Modifier:" -msgstr "" - -#: ../PLCGenerator.py:703 -#: ../PLCGenerator.py:936 -#, python-format -msgid "More than one connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:141 +msgstr "Modifikator:" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "Mehr als ein Anschluss gefunden zu Fortsetzung \"{a1}\" im Baustein \"{a2}\"" + +#: ../dialogs/ActionBlockDialog.py:140 msgid "Move action down" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:140 +msgstr "Aktion nach unten schieben" + +#: ../dialogs/ActionBlockDialog.py:139 msgid "Move action up" -msgstr "" - -#: ../controls/DebugVariablePanel.py:185 -msgid "Move debug variable down" -msgstr "" - -#: ../controls/DebugVariablePanel.py:184 -msgid "Move debug variable up" -msgstr "" +msgstr "Aktion nach oben schieben" #: ../controls/CustomEditableListBox.py:43 msgid "Move down" -msgstr "" - -#: ../editors/DataTypeEditor.py:348 +msgstr "Verschieben nach unten" + +#: ../editors/DataTypeEditor.py:355 msgid "Move element down" -msgstr "" - -#: ../editors/DataTypeEditor.py:347 +msgstr "Komponent nach unten schieben" + +#: ../editors/DataTypeEditor.py:354 msgid "Move element up" -msgstr "" - -#: ../editors/ResourceEditor.py:254 +msgstr "Komponent nach oben schieben" + +#: ../editors/ResourceEditor.py:271 msgid "Move instance down" -msgstr "" - -#: ../editors/ResourceEditor.py:253 +msgstr "Insanz nach unten schieben" + +#: ../editors/ResourceEditor.py:270 msgid "Move instance up" -msgstr "" - -#: ../editors/ResourceEditor.py:225 +msgstr "Instanz nach oben schieben" + +#: ../editors/ResourceEditor.py:242 msgid "Move task down" -msgstr "" - -#: ../editors/ResourceEditor.py:224 +msgstr "Verschieben Task nach unten" + +#: ../editors/ResourceEditor.py:241 msgid "Move task up" -msgstr "" - -#: ../IDEFrame.py:75 -#: ../IDEFrame.py:90 -#: ../IDEFrame.py:120 -#: ../IDEFrame.py:161 +msgstr "Verschieben Task nach oben" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 msgid "Move the view" -msgstr "" +msgstr "Ansicht verschieben" #: ../controls/CustomEditableListBox.py:42 msgid "Move up" -msgstr "" - -#: ../controls/VariablePanel.py:381 +msgstr "Verschieben nach oben" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 msgid "Move variable down" -msgstr "" - -#: ../controls/VariablePanel.py:380 +msgstr "Verschieben Variable nach unten" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 msgid "Move variable up" -msgstr "" +msgstr "Verschieben Variable nach oben" #: ../plcopen/iec_std.csv:74 msgid "Multiplexer (select 1 of N)" -msgstr "" +msgstr "Multiplexer (wähle 1 von N)" #: ../plcopen/iec_std.csv:34 -#, fuzzy msgid "Multiplication" -msgstr "schließe Applikation" - -#: ../editors/FileManagementPanel.py:301 -#, fuzzy +msgstr "Multiplikation" + +#: ../editors/FileManagementPanel.py:83 msgid "My Computer:" -msgstr "Compiler" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 -#: ../editors/ResourceEditor.py:67 -#: ../editors/ResourceEditor.py:76 +msgstr "Mein Rechner:" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NAME" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Name" msgstr "Name" -#: ../Beremiz_service.py:381 +#: ../Beremiz_service.py:334 msgid "Name must not be null!" msgstr "Name darf nicht NULL sein!" -#: ../dialogs/ConnectionDialog.py:65 -#: ../dialogs/FBDVariableDialog.py:89 -#: ../dialogs/LDElementDialog.py:88 -#: ../dialogs/SFCStepDialog.py:51 -#: ../dialogs/FBDBlockDialog.py:70 -#, fuzzy +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 msgid "Name:" -msgstr "Name" +msgstr "Name:" #: ../plcopen/iec_std.csv:24 msgid "Natural logarithm" -msgstr "" - -#: ../editors/Viewer.py:403 -#: ../dialogs/LDElementDialog.py:67 +msgstr "Natürlicher Logarithmus" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 msgid "Negated" -msgstr "" - -#: ../Beremiz.py:307 -#: ../Beremiz.py:342 -#: ../PLCOpenEditor.py:125 -#: ../PLCOpenEditor.py:167 +msgstr "Negiert" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "Nevow Web-Dienst fehlgeschlagen." + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "Nevow/Athena-Import gescheitert :" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 msgid "New" -msgstr "" +msgstr "Neu" #: ../controls/CustomEditableListBox.py:40 msgid "New item" -msgstr "" - -#: ../editors/Viewer.py:402 +msgstr "Neuer Eintrag" + +#: ../editors/Viewer.py:518 msgid "No Modifier" -msgstr "" - -#: ../PLCControler.py:2929 -msgid "No PLC project found" -msgstr "" - -#: ../ProjectController.py:1478 +msgstr "Kein Modifikator" + +#: ../ProjectController.py:1826 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "Keine SPS zu übertragen (war das Kompillieren erfolgreich ?)\n" -#: ../PLCGenerator.py:1321 +#: ../PLCGenerator.py:1631 #, python-format msgid "No body defined in \"%s\" POU" -msgstr "" - -#: ../PLCGenerator.py:722 -#: ../PLCGenerator.py:945 -#, python-format -msgid "No connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "" - -#: ../PLCOpenEditor.py:370 +msgstr "Baustein \"%s\" ist leer" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "Kein Anschluss gefunden zu Fortsetzung \"{a1}\" im Baustein \"{a2}\"" + +#: ../PLCOpenEditor.py:357 msgid "" "No documentation available.\n" "Coming soon." msgstr "" - -#: ../PLCGenerator.py:744 +"Keine Dokumentation.\n" +"Coming soon." + +#: ../PLCGenerator.py:829 #, python-format msgid "No informations found for \"%s\" block" -msgstr "" - -#: ../plcopen/structures.py:167 -msgid "No output variable found" -msgstr "" - -#: ../Beremiz_service.py:394 -msgid "No running PLC" -msgstr "Keine laufende SPS" +msgstr "Keine Informationen für Block \"%s\" sind gefunden" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" +"Keine Ausgabe {a1} Variable gefunden im Block {a2} im Baustein {a3}. " +"Verbindung muss gebrochen werden" #: ../controls/SearchResultPanel.py:169 -#, fuzzy msgid "No search results available." -msgstr "Service verfügbar:" - -#: ../svgui/svgui.py:98 +msgstr "Keine Suchergebnisse." + +#: ../svgui/svgui.py:134 #, python-format msgid "No such SVG file: %s\n" msgstr "Kein SVG-File namens: %s\n" -#: ../canfestival/config_utils.py:632 -#, python-format -msgid "No such index/subindex (%x,%x) (variable %s)" -msgstr "Kein solcher index/subindex (%x,%x) (variable %s)" - -#: ../canfestival/config_utils.py:361 -#, python-format -msgid "No such index/subindex (%x,%x) in ID : %d (variable %s)" -msgstr "Kein solcher index/subindex (%x,%x) in ID : %d (variable %s)" +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "Kein Index/Subindex ({a1},{a2}) - (Variable {a3})" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "Kein Index/Subindex ({a1},{a2}) in ID : {a3} (Variable {a4})" #: ../dialogs/BrowseValuesLibraryDialog.py:83 msgid "No valid value selected!" -msgstr "" - -#: ../PLCGenerator.py:1319 +msgstr "Kein gültiger Wert ausgewählt!" + +#: ../PLCGenerator.py:1629 #, python-format msgid "No variable defined in \"%s\" POU" -msgstr "" - -#: ../canfestival/SlaveEditor.py:49 -#: ../canfestival/NetworkEditor.py:79 -msgid "Node infos" -msgstr "" - -#: ../canfestival/config_utils.py:354 -#, python-format -msgid "Non existing node ID : %d (variable %s)" -msgstr "Nicht existierende Zweig-ID : %d (variable %s)" - -#: ../controls/VariablePanel.py:69 +msgstr "Keine Variable ist im Baustein \"%s\" definiert." + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "Nicht existierende Node ID : {a1} (Variable {a2})" + +#: ../controls/VariablePanel.py:64 msgid "Non-Retain" -msgstr "" - -#: ../dialogs/LDElementDialog.py:62 +msgstr "Non-Retain" + +#: ../dialogs/LDElementDialog.py:75 msgid "Normal" -msgstr "" - -#: ../canfestival/config_utils.py:383 -#, python-format -msgid "Not PDO mappable variable : '%s' (ID:%d,Idx:%x,sIdx:%x))" -msgstr "Keine PDO Mappbare Variable : '%s' (ID:%d,Idx:%x,sIdx:%x))" +msgstr "Normal" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Variable '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4})) unterstützt PDO-Mapping nicht" #: ../plcopen/iec_std.csv:80 msgid "Not equal to" -msgstr "" - -#: ../dialogs/SFCDivergenceDialog.py:80 +msgstr "Ungleich" + +#: ../dialogs/SFCDivergenceDialog.py:89 msgid "Number of sequences:" -msgstr "" +msgstr "Anzahl der Verzweigungen:" #: ../plcopen/iec_std.csv:22 msgid "Numerical" -msgstr "" - -#: ../plcopen/structures.py:247 -msgid "" -"Off-delay timer\n" -"The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." -msgstr "" - -#: ../plcopen/structures.py:242 -msgid "" -"On-delay timer\n" -"The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:93 +msgstr "Numerisch" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "Bei Änderung" + +#: ../dialogs/SearchInProjectDialog.py:84 msgid "Only Elements" -msgstr "" - -#: ../Beremiz.py:309 -#: ../Beremiz.py:343 -#: ../PLCOpenEditor.py:127 -#: ../PLCOpenEditor.py:168 +msgstr "Nur Elemente" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 msgid "Open" -msgstr "" - -#: ../svgui/svgui.py:107 +msgstr "Öffnen" + +#: ../svgui/svgui.py:143 msgid "Open Inkscape" msgstr "öffne Inkscape" -#: ../ProjectController.py:1530 +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" +"Open-Source-Framework für die Automatisierung, implementiert IEC-61131 " +"Entwicklungsumgebung mit ständig wachsender Reihe von Erweiterungen und " +"flexibler SPS-Runtime." + +#: ../ProjectController.py:1878 msgid "Open a file explorer to manage project files" -msgstr "" - -#: ../wxglade_hmi/wxglade_hmi.py:109 +msgstr "Datei-Browser zum Verwalten der Projektdateien öffnen" + +#: ../wxglade_hmi/wxglade_hmi.py:155 msgid "Open wxGlade" -msgstr "öffne wxGlade" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +msgstr "WxGlade öffnen" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Option" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:76 +msgstr "Option" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 msgid "Options" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:97 +msgstr "Optionen" + +#: ../controls/ProjectPropertiesPanel.py:98 msgid "Organization (optional):" -msgstr "" - -#: ../canfestival/SlaveEditor.py:47 -#: ../canfestival/NetworkEditor.py:77 +msgstr "Organisation (optional):" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 msgid "Other Profile" -msgstr "" - -#: ../controls/VariablePanel.py:76 -#: ../dialogs/BrowseLocationsDialog.py:37 -#: ../dialogs/FBDVariableDialog.py:35 -#: ../dialogs/SFCStepDialog.py:65 +msgstr "Anderes Profil" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Output" -msgstr "" - -#: ../canfestival/SlaveEditor.py:36 -#: ../canfestival/NetworkEditor.py:66 +msgstr "Ausgang" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 msgid "PDO Receive" -msgstr "" - -#: ../canfestival/SlaveEditor.py:35 -#: ../canfestival/NetworkEditor.py:65 +msgstr "PDO Empfangen" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 msgid "PDO Transmit" -msgstr "" - -#: ../plcopen/structures.py:269 -msgid "" -"PID\n" -"The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." -msgstr "" - -#: ../targets/toolchain_gcc.py:107 +msgstr "PDO Senden" + +#: ../targets/toolchain_gcc.py:167 msgid "PLC :\n" msgstr "SPS :\n" -#: ../ProjectController.py:1096 -#: ../ProjectController.py:1398 -#, python-format -msgid "PLC is %s\n" -msgstr "SPS ist %s\n" - -#: ../PLCOpenEditor.py:313 -#: ../PLCOpenEditor.py:391 -#, fuzzy +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "SPS-Protokoll" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "SPS-Code-Erzeugung fehlgeschlagen !\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "SPS ist leer oder bereits gestartet." + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "SPS ist nicht gestartet." + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" +"SPS-Syntaxfehler in Zeile {a1}:\n" +"{a2}" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" -msgstr "SVG files (*.svg)|*.svg|Alle Dateien|*.*" - -#: ../PLCOpenEditor.py:175 -#: ../PLCOpenEditor.py:231 -#, fuzzy +msgstr "PLCOpen Dateien (*.xml)|*.xml|Alle Dateien|*.*" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 msgid "PLCOpenEditor" -msgstr "Öffne CFileEditor" - -#: ../dialogs/PouDialog.py:98 -#, fuzzy +msgstr "PLCOpenEditor" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" +"PLCOpenEditor ist entwickelt als Teil des Projekts Beremiz.\n" +"\n" +"Beremiz ist " + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "PORT" + +#: ../dialogs/PouDialog.py:101 msgid "POU Name" -msgstr "Name" - -#: ../dialogs/PouDialog.py:56 -#, fuzzy +msgstr "Bausteinname" + +#: ../dialogs/PouDialog.py:58 msgid "POU Name:" -msgstr "Name" - -#: ../dialogs/PouDialog.py:100 +msgstr "Bausteinname:" + +#: ../dialogs/PouDialog.py:103 msgid "POU Type" -msgstr "" - -#: ../dialogs/PouDialog.py:63 +msgstr "Bausteintyp" + +#: ../dialogs/PouDialog.py:65 msgid "POU Type:" -msgstr "" - -#: ../Beremiz.py:322 -#: ../PLCOpenEditor.py:141 +msgstr "Bausteintyp:" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "PYRO Verbindung zu URI : %s\n" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "PYRO mit Zertifikaten aus '%s'\n" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 msgid "Page Setup" msgstr "Seitenformat" -#: ../controls/ProjectPropertiesPanel.py:110 +#: ../controls/ProjectPropertiesPanel.py:111 msgid "Page Size (optional):" -msgstr "" - -#: ../PLCOpenEditor.py:476 +msgstr "Seitengröße (optional):" + +#: ../IDEFrame.py:2613 #, python-format msgid "Page: %d" -msgstr "" - -#: ../controls/PouInstanceVariablesPanel.py:41 -#, fuzzy +msgstr "Seite: %d" + +#: ../controls/PouInstanceVariablesPanel.py:124 msgid "Parent instance" -msgstr "CanFestivalInstanz" - -#: ../IDEFrame.py:350 -#: ../IDEFrame.py:402 -#: ../editors/Viewer.py:537 +msgstr "Elterninstanz" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 msgid "Paste" -msgstr "" - -#: ../IDEFrame.py:1900 +msgstr "Einfügen" + +#: ../IDEFrame.py:1868 msgid "Paste POU" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:64 +msgstr "Baustein einfügen" + +#: ../dialogs/SearchInProjectDialog.py:56 msgid "Pattern to search:" -msgstr "" - -#: ../dialogs/LDPowerRailDialog.py:64 +msgstr "Zu suchendes Muster:" + +#: ../dialogs/LDPowerRailDialog.py:74 msgid "Pin number:" -msgstr "" - -#: ../editors/Viewer.py:2289 -#: ../editors/Viewer.py:2594 -#: ../editors/SFCViewer.py:696 +msgstr "Anschlussnummer:" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 msgid "Please choose a target" -msgstr "" - -#: ../editors/Viewer.py:2112 -#: ../editors/Viewer.py:2114 -#: ../editors/Viewer.py:2630 -#: ../editors/Viewer.py:2632 +msgstr "Bitte wählen Sie ein Ziel" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "Bitte geben Sie einen Blocknamen ein" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 msgid "Please enter comment text" -msgstr "" - -#: ../editors/SFCViewer.py:359 -#: ../editors/SFCViewer.py:381 -#: ../editors/SFCViewer.py:725 -#, fuzzy +msgstr "Bitte geben Sie einen Kommentartext ein" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 msgid "Please enter step name" -msgstr "Bitte geben sie einen Namen für das PlugIn ein:" - -#: ../dialogs/ForceVariableDialog.py:153 -#, fuzzy, python-format +msgstr "Bitte geben Sie einen Schrittnamen ein" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "Bitte geben Sie Text ein" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format msgid "Please enter value for a \"%s\" variable:" -msgstr "Bitte geben sie einen Namen für das PlugIn ein:" - -#: ../Beremiz_service.py:366 +msgstr "Bitte geben Sie einen Wert für die Variable \"%s\" ein:" + +#: ../Beremiz_service.py:319 msgid "Port number must be 0 <= port <= 65535!" msgstr "Port Nummer darf folgenden Wertbereich haben: 0 <= port <= 65535!" -#: ../Beremiz_service.py:366 +#: ../Beremiz_service.py:319 msgid "Port number must be an integer!" msgstr "Port Nummer muß eine natürliche Zahl sein (Integer)!" -#: ../editors/GraphicViewer.py:105 -msgid "Position:" -msgstr "" - -#: ../editors/Viewer.py:476 +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 msgid "Power Rail" -msgstr "" - -#: ../dialogs/LDPowerRailDialog.py:36 -#, fuzzy +msgstr "Stromleitung" + +#: ../dialogs/LDPowerRailDialog.py:51 msgid "Power Rail Properties" -msgstr "Eigenschaften" - -#: ../Beremiz.py:324 -#: ../PLCOpenEditor.py:143 +msgstr "Stromleitungeigenschaften" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 msgid "Preview" msgstr "Vorschau" -#: ../dialogs/SFCDivergenceDialog.py:93 -#: ../dialogs/LDPowerRailDialog.py:78 -#: ../dialogs/ConnectionDialog.py:78 -#: ../dialogs/FBDVariableDialog.py:97 -#: ../dialogs/SFCTransitionDialog.py:96 -#: ../dialogs/LDElementDialog.py:101 -#: ../dialogs/SFCStepDialog.py:79 -#: ../dialogs/FBDBlockDialog.py:103 -#, fuzzy +#: ../dialogs/BlockPreviewDialog.py:57 msgid "Preview:" -msgstr "Vorschau" - -#: ../Beremiz.py:326 -#: ../Beremiz.py:346 -#: ../PLCOpenEditor.py:145 -#: ../PLCOpenEditor.py:171 +msgstr "Vorschau:" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 msgid "Print" msgstr "Drucken" -#: ../IDEFrame.py:1155 -#, fuzzy +#: ../IDEFrame.py:1079 msgid "Print preview" -msgstr "Vorschau" - -#: ../editors/ResourceEditor.py:67 +msgstr "Druckvorschau" + +#: ../editors/ResourceEditor.py:68 msgid "Priority" -msgstr "" - -#: ../dialogs/SFCTransitionDialog.py:83 +msgstr "Priorität" + +#: ../dialogs/SFCTransitionDialog.py:90 msgid "Priority:" -msgstr "" +msgstr "Priorität:" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "Problem mit SPS-Start : Fehler %d" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "Produktname" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "Produktname (erforderlich):" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "Produkt-Release (optional):" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "Produktversion" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "Produktversion (erforderlich):" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "Programm" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "Programm wurde erfolgreich generiert!" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "Programme" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "Programme können nicht von anderen Bausteinen verwendet werden!" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "Projekt" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "Projekt '%s':" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "Projektdateien" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "Projektname" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "Projektname (erforderlich):" #: ../controls/ProjectPropertiesPanel.py:80 -msgid "Product Name (required):" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:82 -msgid "Product Release (optional):" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:81 -msgid "Product Version (required):" -msgstr "" - -#: ../IDEFrame.py:1972 -#: ../dialogs/SearchInProjectDialog.py:46 -msgid "Program" -msgstr "" - -#: ../PLCOpenEditor.py:360 -msgid "Program was successfully generated!" -msgstr "" - -#: ../PLCControler.py:95 -msgid "Programs" -msgstr "" - -#: ../editors/Viewer.py:230 -msgid "Programs can't be used by other POUs!" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:84 -#: ../IDEFrame.py:553 -#, fuzzy -msgid "Project" -msgstr "Projekt schließen" - -#: ../controls/SearchResultPanel.py:173 -#, python-format -msgid "Project '%s':" -msgstr "" - -#: ../ProjectController.py:1529 -msgid "Project Files" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:78 -#, fuzzy -msgid "Project Name (required):" -msgstr "Projekt nicht angelegt" - -#: ../controls/ProjectPropertiesPanel.py:79 msgid "Project Version (optional):" -msgstr "" - -#: ../PLCControler.py:2916 +msgstr "Projektversion (optional):" + +#: ../PLCControler.py:3164 msgid "" "Project file syntax error:\n" "\n" msgstr "" - -#: ../dialogs/ProjectDialog.py:32 -#, fuzzy +"Syntax-Fehler in Projektdatei:\n" +"\n" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 msgid "Project properties" -msgstr "Eigenschaften" - -#: ../ConfigTreeNode.py:506 -#, fuzzy, python-format -msgid "Project tree layout do not match confnode.xml %s!=%s " -msgstr "Projektbaum Layout entspricht nicht plugin.xml %s!=%s " - -#: ../PLCControler.py:96 +msgstr "Projekteigenschaften" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "Projektbaum stimmt nicht mit confnode.xml {a1}! ={a2} überein" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "Andere Anschlüße umbenennen" + +#: ../PLCControler.py:99 msgid "Properties" msgstr "Eigenschaften" -#: ../plcopen/structures.py:237 -msgid "" -"Pulse timer\n" -"The pulse timer can be used to generate output pulses of a given time duration." -msgstr "" - -#: ../features.py:8 -#, fuzzy +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "Service im lokalen Netzwerk veröffentlichen" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "Pyroausnahme: %s\n" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "Pyro uri :" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "Pyro port :" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "Python code" + +#: ../features.py:33 msgid "Python file" -msgstr "PythonEditor" - -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "Python Datei" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Qualifier" -msgstr "" - -#: ../Beremiz_service.py:328 -#: ../Beremiz.py:329 -#: ../PLCOpenEditor.py:151 +msgstr "Qualifier" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 msgid "Quit" msgstr "Beenden" -#: ../plcopen/structures.py:202 -msgid "" -"RS bistable\n" -"The RS bistable is a latch where the Reset dominates." -msgstr "" - -#: ../plcopen/structures.py:274 -msgid "" -"Ramp\n" -"The RAMP function block is modelled on example given in the standard." -msgstr "" - -#: ../editors/GraphicViewer.py:89 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 msgid "Range:" -msgstr "" - -#: ../ProjectController.py:1525 +msgstr "Bereich:" + +#: ../ProjectController.py:1873 msgid "Raw IEC code" msgstr "Raw IEC code" -#: ../plcopen/structures.py:254 -msgid "" -"Real time clock\n" -"The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." -msgstr "" - -#: ../Beremiz.py:1039 -#, fuzzy, python-format +#: ../BeremizIDE.py:1047 +#, python-format msgid "Really delete node '%s'?" -msgstr "Plugin wirklich löschen ?" - -#: ../IDEFrame.py:340 -#: ../IDEFrame.py:398 +msgstr "Knoten '%s' wirklich löschen?" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 msgid "Redo" -msgstr "" - -#: ../dialogs/SFCTransitionDialog.py:57 -#: ../dialogs/SFCTransitionDialog.py:135 +msgstr "Wiederherstellen" + +#: ../dialogs/SFCTransitionDialog.py:75 msgid "Reference" -msgstr "" - -#: ../IDEFrame.py:408 -#: ../dialogs/DiscoveryDialog.py:105 +msgstr "Referenz" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 msgid "Refresh" msgstr "Refresh" -#: ../dialogs/SearchInProjectDialog.py:73 +#: ../dialogs/SearchInProjectDialog.py:66 msgid "Regular expression" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:91 +msgstr "Regulärer Ausdruck" + +#: ../dialogs/FindInPouDialog.py:96 msgid "Regular expressions" -msgstr "" - -#: ../controls/DebugVariablePanel.py:299 -#: ../editors/Viewer.py:1356 +msgstr "Reguläre Ausdrücke" + +#: ../editors/Viewer.py:1603 msgid "Release value" -msgstr "" +msgstr "Forcen aufheben" #: ../plcopen/iec_std.csv:37 msgid "Remainder (modulo)" -msgstr "" - -#: ../Beremiz.py:1040 +msgstr "Rest (modulo)" + +#: ../BeremizIDE.py:1048 #, python-format msgid "Remove %s node" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:139 -#, fuzzy +msgstr "Knoten %s löschen" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "Datentyp löschen" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "Baustein löschen" + +#: ../dialogs/ActionBlockDialog.py:138 msgid "Remove action" -msgstr "Entferne Plugin" - -#: ../controls/DebugVariablePanel.py:183 -msgid "Remove debug variable" -msgstr "" - -#: ../editors/DataTypeEditor.py:346 -#, fuzzy +msgstr "Aktion löschen" + +#: ../editors/DataTypeEditor.py:353 msgid "Remove element" -msgstr "Entferne Plugin" - -#: ../editors/FileManagementPanel.py:281 +msgstr "Komponent löschen" + +#: ../editors/FileManagementPanel.py:63 msgid "Remove file from left folder" -msgstr "" - -#: ../editors/ResourceEditor.py:252 -#, fuzzy +msgstr "Entfernen Datei aus dem linken Ordner" + +#: ../editors/ResourceEditor.py:269 msgid "Remove instance" -msgstr "Entferne Plugin" - -#: ../canfestival/NetworkEditor.py:87 -#, fuzzy +msgstr "Instanz löschen" + +#: ../canfestival/NetworkEditor.py:104 msgid "Remove slave" -msgstr "Entferne Plugin" - -#: ../editors/ResourceEditor.py:223 +msgstr "Slave löschen" + +#: ../editors/ResourceEditor.py:240 msgid "Remove task" -msgstr "" - -#: ../controls/VariablePanel.py:379 -#, fuzzy +msgstr "Task löschen" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 msgid "Remove variable" -msgstr "Service verfügbar:" - -#: ../IDEFrame.py:1976 +msgstr "Variable löschen" + +#: ../IDEFrame.py:1948 msgid "Rename" -msgstr "" - -#: ../editors/FileManagementPanel.py:399 +msgstr "Umbenennen" + +#: ../editors/FileManagementPanel.py:181 msgid "Replace File" -msgstr "" +msgstr "Datei ersetzen" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "Kabel durch Verbindungen ersetzen" #: ../plcopen/iec_std.csv:89 msgid "Replacement (within)" -msgstr "" +msgstr "Ersetzung (innerhalb)" #: ../dialogs/LDElementDialog.py:76 msgid "Reset" -msgstr "" - -#: ../editors/Viewer.py:521 +msgstr "Reset" + +#: ../editors/Viewer.py:642 msgid "Reset Execution Order" -msgstr "" - -#: ../IDEFrame.py:423 +msgstr "Ausführungsreihenfolge zurücksetzen" + +#: ../IDEFrame.py:451 msgid "Reset Perspective" -msgstr "" +msgstr "Perspektive zurücksetzen" #: ../controls/SearchResultPanel.py:105 msgid "Reset search result" -msgstr "" - -#: ../editors/GraphicViewer.py:137 -msgid "Reset zoom and offset" -msgstr "" - -#: ../PLCControler.py:96 +msgstr "Suchergebniss säubern" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 msgid "Resources" -msgstr "" - -#: ../controls/VariablePanel.py:67 +msgstr "Ressourcen" + +#: ../controls/VariablePanel.py:62 msgid "Retain" -msgstr "" - -#: ../controls/VariablePanel.py:352 +msgstr "Retain" + +#: ../controls/VariablePanel.py:424 msgid "Return Type:" -msgstr "" - -#: ../editors/Viewer.py:430 +msgstr "Rückgabetyp:" + +#: ../editors/Viewer.py:546 msgid "Right" -msgstr "" - -#: ../dialogs/LDPowerRailDialog.py:60 +msgstr "Rechts" + +#: ../dialogs/LDPowerRailDialog.py:64 msgid "Right PowerRail" -msgstr "" - -#: ../editors/Viewer.py:404 -#: ../dialogs/LDElementDialog.py:80 +msgstr "Rechte Stromleitung" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 msgid "Rising Edge" -msgstr "" - -#: ../plcopen/structures.py:212 -msgid "" -"Rising edge detector\n" -"The output produces a single pulse when a rising edge is detected." -msgstr "" +msgstr "Steigende Flanke" #: ../plcopen/iec_std.csv:65 msgid "Rotate left" -msgstr "" +msgstr "Bitweise Linksrotation" #: ../plcopen/iec_std.csv:64 msgid "Rotate right" -msgstr "" +msgstr "Bitweise Rechtsrotation eines Operanden" #: ../plcopen/iec_std.csv:17 msgid "Rounding up/down" -msgstr "" - -#: ../ProjectController.py:1493 +msgstr "Auf-/Abrunden" + +#: ../ProjectController.py:1841 msgid "Run" msgstr "Starte" -#: ../ProjectController.py:841 -#: ../ProjectController.py:850 -#, fuzzy -msgid "Runtime extensions C code generation failed !\n" -msgstr "PlugIns Codeerstellung fehlgeschlagen !\n" - -#: ../canfestival/SlaveEditor.py:34 -#: ../canfestival/NetworkEditor.py:64 +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "C-Code-Erzeugung für Runtime IO-Erweiterungen fehlgeschlagen !\n" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "C-Code-Erzeugung für Runtime Bibliothek fehlgeschlagen !\n" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 msgid "SDO Client" -msgstr "" - -#: ../canfestival/SlaveEditor.py:33 -#: ../canfestival/NetworkEditor.py:63 +msgstr "SDO Client" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 msgid "SDO Server" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouDialog.py:36 +msgstr "SDO Server" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "SFC" -msgstr "" - -#: ../plcopen/structures.py:197 -msgid "" -"SR bistable\n" -"The SR bistable is a latch where the Set dominates." -msgstr "" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +msgstr "AS" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" +"AS-Sprung in der Baustein \"{a1}\" bezieht sich auf nicht vorhandene AS-" +"Schritt \"{a2}\"" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "AS Transition im Baustein \"%s\" muss angeschlossen sein." + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "ST" -msgstr "" - -#: ../PLCOpenEditor.py:347 -#, fuzzy +msgstr "ST" + +#: ../PLCOpenEditor.py:334 msgid "ST files (*.st)|*.st|All files|*.*" -msgstr "SVG files (*.svg)|*.svg|Alle Dateien|*.*" - -#: ../svgui/svgui.py:92 +msgstr "ST Dateien (*.st)|*.st|Alle Dateien|*.*" + +#: ../svgui/svgui.py:128 msgid "SVG files (*.svg)|*.svg|All files|*.*" -msgstr "SVG files (*.svg)|*.svg|Alle Dateien|*.*" - -#: ../features.py:10 +msgstr "SVG Dateien (*.svg)|*.svg|Alle Dateien|*.*" + +#: ../features.py:35 msgid "SVGUI" -msgstr "" - -#: ../Beremiz.py:313 -#: ../Beremiz.py:344 -#: ../PLCOpenEditor.py:134 -#: ../PLCOpenEditor.py:169 +msgstr "SVGUI" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 msgid "Save" -msgstr "" - -#: ../Beremiz.py:345 -#: ../PLCOpenEditor.py:136 -#: ../PLCOpenEditor.py:170 +msgstr "Speichern" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 msgid "Save As..." -msgstr "" - -#: ../Beremiz.py:315 +msgstr "Speichern unter..." + +#: ../BeremizIDE.py:224 msgid "Save as" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:76 +msgstr "Speichern unter" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "Speicherpfad ist der gleiche, wie der Pfad von dem Projekt!\n" + +#: ../dialogs/SearchInProjectDialog.py:69 msgid "Scope" -msgstr "" - -#: ../IDEFrame.py:592 -#: ../dialogs/SearchInProjectDialog.py:105 +msgstr "Geltungsbereich" + +#: ../IDEFrame.py:623 msgid "Search" -msgstr "" - -#: ../IDEFrame.py:360 -#: ../IDEFrame.py:404 -#: ../dialogs/SearchInProjectDialog.py:52 -#, fuzzy +msgstr "Suchen" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 msgid "Search in Project" -msgstr "Projekt schließen" - -#: ../dialogs/DurationEditorDialog.py:46 +msgstr "Im Projekt suchen " + +#: ../dialogs/DurationEditorDialog.py:47 msgid "Seconds:" -msgstr "" - -#: ../IDEFrame.py:366 +msgstr "Sekunden:" + +#: ../IDEFrame.py:388 msgid "Select All" -msgstr "" - -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:330 -#: ../editors/Viewer.py:277 -#, fuzzy +msgstr "Alles auswählen" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Select a variable class:" -msgstr "Service verfügbar:" - -#: ../ProjectController.py:1013 +msgstr "Wählen Sie eine Variablenklasse:" + +#: ../ProjectController.py:1257 msgid "Select an editor:" -msgstr "" - -#: ../controls/PouInstanceVariablesPanel.py:197 +msgstr "Wählen Sie einen Editor:" + +#: ../controls/PouInstanceVariablesPanel.py:281 msgid "Select an instance" -msgstr "" - -#: ../IDEFrame.py:576 +msgstr "Wählen Sie eine Instanz" + +#: ../IDEFrame.py:607 msgid "Select an object" -msgstr "" +msgstr "Wählen Sie ein Objekt" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" +"Das ausgewählte Verzeichnis enthält bereits ein Projekt. Überschreiben?\n" #: ../plcopen/iec_std.csv:70 msgid "Selection" -msgstr "" - -#: ../dialogs/SFCDivergenceDialog.py:62 +msgstr "Auswahl" + +#: ../dialogs/SFCDivergenceDialog.py:65 msgid "Selection Convergence" -msgstr "" - -#: ../dialogs/SFCDivergenceDialog.py:55 +msgstr "Alternativeende" + +#: ../dialogs/SFCDivergenceDialog.py:64 msgid "Selection Divergence" -msgstr "" - -#: ../plcopen/structures.py:207 -msgid "" -"Semaphore\n" -"The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." -msgstr "" - -#: ../dialogs/DiscoveryDialog.py:84 +msgstr "Alternativanfang" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "Service Discovery" + +#: ../dialogs/DiscoveryDialog.py:85 msgid "Services available:" msgstr "Service verfügbar:" -#: ../dialogs/LDElementDialog.py:72 +#: ../dialogs/LDElementDialog.py:76 msgid "Set" -msgstr "" +msgstr "Menge" #: ../plcopen/iec_std.csv:62 msgid "Shift left" -msgstr "" +msgstr "Bitweises Links-Shift" #: ../plcopen/iec_std.csv:63 msgid "Shift right" -msgstr "" - -#: ../ProjectController.py:1519 +msgstr "Bitweises Rechts-Shift eines Operanden" + +#: ../ProjectController.py:1867 msgid "Show IEC code generated by PLCGenerator" msgstr "Zeige IEC Code, der vom PLCGenerator erzeugt wurde" -#: ../canfestival/canfestival.py:288 +#: ../canfestival/canfestival.py:389 msgid "Show Master" msgstr "Zeige Master" -#: ../canfestival/canfestival.py:289 +#: ../canfestival/canfestival.py:390 msgid "Show Master generated by config_utils" msgstr "Zeige Master, der von den config_utils generiert wurde." -#: ../ProjectController.py:1517 +#: ../ProjectController.py:1865 msgid "Show code" msgstr "Zeige Code" -#: ../dialogs/SFCDivergenceDialog.py:74 +#: ../dialogs/SFCDivergenceDialog.py:67 msgid "Simultaneous Convergence" -msgstr "" - -#: ../dialogs/SFCDivergenceDialog.py:68 +msgstr "Parallelende" + +#: ../dialogs/SFCDivergenceDialog.py:66 msgid "Simultaneous Divergence" -msgstr "" +msgstr "Parallelanfang" #: ../plcopen/iec_std.csv:27 msgid "Sine" -msgstr "" - -#: ../editors/ResourceEditor.py:67 +msgstr "Sinus" + +#: ../editors/ResourceEditor.py:68 msgid "Single" -msgstr "" +msgstr "Ereignis" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "Quellecode hat sich nicht verändert, Build ist nicht nötig.\n" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" +"Ereignis für die ereignisgesteuerte Task '{a1}' in der Ressource '{a2}. " +"{a3}' muss definiert werden." #: ../plcopen/iec_std.csv:23 msgid "Square root (base 2)" -msgstr "" - -#: ../plcopen/structures.py:193 +msgstr "Quadratwurzel (Basis 2)" + +#: ../plcopen/definitions.py:48 msgid "Standard function blocks" -msgstr "" - -#: ../Beremiz_service.py:319 -#: ../ProjectController.py:1495 +msgstr "Standardfunktionsblock" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 msgid "Start PLC" msgstr "Starte SPS" -#: ../ProjectController.py:819 +#: ../ProjectController.py:1046 #, python-format msgid "Start build in %s\n" msgstr "Zeige build in %s\n" -#: ../ProjectController.py:1314 +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "Gestartet" + +#: ../ProjectController.py:1648 msgid "Starting PLC\n" msgstr "starte SPS\n" -#: ../Beremiz.py:403 +#: ../BeremizIDE.py:365 msgid "Status ToolBar" -msgstr "" - -#: ../editors/Viewer.py:493 +msgstr "Statusleiste" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 msgid "Step" -msgstr "" - -#: ../ProjectController.py:1498 +msgstr "Schritt" + +#: ../ProjectController.py:1846 msgid "Stop" msgstr "Stop" -#: ../Beremiz_service.py:320 +#: ../Beremiz_service.py:264 msgid "Stop PLC" msgstr "Stop SPS" -#: ../ProjectController.py:1500 +#: ../ProjectController.py:1848 msgid "Stop Running PLC" msgstr "Halte laufende SPS an" -#: ../ProjectController.py:1292 -#, fuzzy +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "Gestoppt" + +#: ../ProjectController.py:1620 msgid "Stopping debugger...\n" -msgstr "Halte Debugger an\n" - -#: ../editors/DataTypeEditor.py:52 +msgstr "Debugger wird gestoppt...\n" + +#: ../editors/DataTypeEditor.py:54 msgid "Structure" -msgstr "" - -#: ../editors/DataTypeEditor.py:52 +msgstr "Struktur" + +#: ../editors/DataTypeEditor.py:54 msgid "Subrange" -msgstr "" +msgstr "Unterbereichstyp" #: ../plcopen/iec_std.csv:35 msgid "Subtraction" -msgstr "" - -#: ../ProjectController.py:915 +msgstr "Subtraktion" + +#: ../ProjectController.py:1085 msgid "Successfully built.\n" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:154 +msgstr "Erfolgreicher Build.\n" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "Perspektive wechseln" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 msgid "Syntax error in regular expression of pattern to search!" -msgstr "" +msgstr "Syntaxfehler im regulären Ausdruck des zu suchenden Musters!" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "TYPE" #: ../plcopen/iec_std.csv:29 msgid "Tangent" -msgstr "" - -#: ../editors/ResourceEditor.py:76 +msgstr "Tangente" + +#: ../editors/ResourceEditor.py:83 msgid "Task" -msgstr "" - -#: ../editors/ResourceEditor.py:218 +msgstr "Task" + +#: ../editors/ResourceEditor.py:235 msgid "Tasks:" -msgstr "" - -#: ../controls/VariablePanel.py:78 +msgstr "Tasks:" + +#: ../controls/VariablePanel.py:73 msgid "Temp" -msgstr "" - -#: ../editors/FileManagementPanel.py:398 +msgstr "Temp" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" +"Der beste Ort, um Fragen über Beremiz / PLCOpenEditor\n" +"zu stellen, ist die Mailingliste des Projekts:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"Dies ist der Haupt-Support-Kanal der Community.\n" +"Um dort Fragen zu stellen, ist es erforderlich,\n" +"die Mailingliste abonniert zu haben.\n" +"\n" +"Hier können Sie die Liste abonnieren:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + +#: ../editors/FileManagementPanel.py:180 #, python-format msgid "" "The file '%s' already exist.\n" "Do you want to replace it?" msgstr "" - -#: ../editors/LDViewer.py:879 +"Die Datei '%s' existiert bereits.\n" +"Möchten Sie es ersetzen?" + +#: ../editors/LDViewer.py:882 msgid "The group of block must be coherent!" -msgstr "" - -#: ../IDEFrame.py:1091 -#: ../Beremiz.py:555 +msgstr "Die Gruppe von Blöcken muss konsistent sein!" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 msgid "There are changes, do you want to save?" msgstr "Es wurden Änderungen gemacht, wollen Sie speichern?" -#: ../IDEFrame.py:1709 -#: ../IDEFrame.py:1728 -#, python-format -msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" -msgstr "" - -#: ../IDEFrame.py:1178 +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" +"Baustein mit dem Namen \"%s\" existiert bereits. Das könnte eine Kollision " +"sein. Möchten Sie fortfahren?" + +#: ../IDEFrame.py:1102 msgid "" "There was a problem printing.\n" "Perhaps your current printer is not set correctly?" msgstr "" - -#: ../editors/LDViewer.py:888 +"Es gab ein Problem beim Drucken.\n" +"Vielleicht ist der Drucker nicht richtig eingestellt." + +#: ../editors/LDViewer.py:891 msgid "This option isn't available yet!" -msgstr "" - -#: ../editors/GraphicViewer.py:278 -msgid "Tick" -msgstr "" +msgstr "Diese Option is noch nicht verfügbar!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "Takt: %d" #: ../plcopen/iec_std.csv:40 msgid "Time" -msgstr "" - -#: ../plcopen/iec_std.csv:40 -#: ../plcopen/iec_std.csv:41 +msgstr "Zeit" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 msgid "Time addition" -msgstr "" +msgstr "Zeitaddition" #: ../plcopen/iec_std.csv:86 msgid "Time concatenation" -msgstr "" - -#: ../plcopen/iec_std.csv:60 -#: ../plcopen/iec_std.csv:61 +msgstr "Zeitkonkatenation" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 msgid "Time division" -msgstr "" - -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:47 -#, fuzzy +msgstr "Zeitdivision" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 msgid "Time multiplication" -msgstr "schließe Applikation" - -#: ../plcopen/iec_std.csv:48 -#: ../plcopen/iec_std.csv:49 +msgstr "Zeitmultiplikation" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 msgid "Time subtraction" -msgstr "" - -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:43 +msgstr "Zeitsubstraktion" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 msgid "Time-of-day addition" -msgstr "" - -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:53 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:55 +msgstr "Tageszeitaddition" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 msgid "Time-of-day subtraction" -msgstr "" - -#: ../editors/Viewer.py:432 +msgstr "Tageszeitsubstraktion" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "Wert umschalten" + +#: ../editors/Viewer.py:548 msgid "Top" -msgstr "" - -#: ../ProjectController.py:1507 +msgstr "Oben" + +#: ../ProjectController.py:1855 msgid "Transfer" -msgstr "Übertrage" - -#: ../ProjectController.py:1509 +msgstr "Übertragen" + +#: ../ProjectController.py:1857 msgid "Transfer PLC" -msgstr "Übertrage SPS" - -#: ../ProjectController.py:1474 +msgstr "SPS übertragen" + +#: ../ProjectController.py:1820 msgid "Transfer completed successfully.\n" msgstr "Übertragung erfolgreich beendet.\n" -#: ../ProjectController.py:1476 +#: ../ProjectController.py:1823 msgid "Transfer failed\n" msgstr "Übertragung gescheitert\n" -#: ../editors/Viewer.py:494 +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 msgid "Transition" -msgstr "" - -#: ../PLCGenerator.py:1212 -#, python-format -msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" -msgstr "" +msgstr "Transition" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" +"Transition \"%s\" muss eine Ausgangsvariable oder Spule enthalten, die sich " +"auf seinen Namen bezieht" #: ../dialogs/PouTransitionDialog.py:84 msgid "Transition Name" -msgstr "" +msgstr "Transitionsbedingungsname" #: ../dialogs/PouTransitionDialog.py:53 msgid "Transition Name:" -msgstr "" - -#: ../PLCGenerator.py:1301 -#, python-format -msgid "Transition with content \"%s\" not connected to a next step in \"%s\" POU" -msgstr "" - -#: ../PLCGenerator.py:1292 -#, python-format -msgid "Transition with content \"%s\" not connected to a previous step in \"%s\" POU" -msgstr "" - -#: ../plcopen/plcopen.py:1442 +msgstr "Transitionsbedingungsname:" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" +"Transition mit Inhalt \"{a1}\" im Baustein \"{a2}\" ist nicht mit dem " +"nächsten Schritt verbunden" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" +"Transition mit Inhalt \"{a1}\" im Baustein \"{a2}\" ist nicht mit dem " +"vorherigen Schritt verbunden" + +#: ../plcopen/plcopen.py:1323 #, python-format msgid "Transition with name %s doesn't exist!" -msgstr "" - -#: ../PLCControler.py:95 +msgstr "Eine Transition mit dem Namen %s existiert nicht!" + +#: ../PLCControler.py:98 msgid "Transitions" -msgstr "" - -#: ../editors/ResourceEditor.py:67 +msgstr "Transitionen" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "Übersetzt von" + +#: ../editors/ResourceEditor.py:68 msgid "Triggering" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 -#: ../editors/ResourceEditor.py:76 -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "Taskttyp" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "Twisted is nicht verfügbar." + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Type" -msgstr "" - -#: ../canfestival/config_utils.py:335 -#: ../canfestival/config_utils.py:617 +msgstr "Typ" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "Datentyp und abgeleitete Datentypen" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 #, python-format msgid "Type conflict for location \"%s\"" msgstr "Typenkonflikt für Ort \"%s\"" #: ../plcopen/iec_std.csv:16 msgid "Type conversion" -msgstr "" - -#: ../editors/DataTypeEditor.py:155 +msgstr "Typumwandlung" + +#: ../editors/DataTypeEditor.py:162 msgid "Type infos:" -msgstr "" - -#: ../dialogs/SFCDivergenceDialog.py:51 -#: ../dialogs/LDPowerRailDialog.py:51 -#: ../dialogs/ConnectionDialog.py:52 -#: ../dialogs/SFCTransitionDialog.py:53 -#: ../dialogs/FBDBlockDialog.py:48 +msgstr "Typinfo:" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "Nur Datentyp" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 msgid "Type:" -msgstr "" - -#: ../canfestival/config_utils.py:455 -#: ../canfestival/config_utils.py:469 +msgstr "Typ:" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 #, python-format msgid "Unable to define PDO mapping for node %02x" msgstr "Unmöglich, PDO-Mapping für %02x zu definieren" -#: ../targets/Xenomai/__init__.py:14 -#, fuzzy, python-format +#: ../targets/Xenomai/__init__.py:39 +#, python-format msgid "Unable to get Xenomai's %s \n" -msgstr "Unmöglich, Xonomai's CFLAGS zu auszulesen\n" - -#: ../PLCGenerator.py:865 -#: ../PLCGenerator.py:924 -#, python-format -msgid "Undefined block type \"%s\" in \"%s\" POU" -msgstr "" - -#: ../PLCGenerator.py:240 +msgstr " %s-Ermittlung für Xenomai fehlgeschlagen\n" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "Undefinierter Bausteintyp \"{a1}\" im Baustein \"{a2}\"" + +#: ../PLCGenerator.py:254 #, python-format msgid "Undefined pou type \"%s\"" -msgstr "" - -#: ../IDEFrame.py:338 -#: ../IDEFrame.py:397 +msgstr "Undefinierter Bausteintyp \"%s\"" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 msgid "Undo" -msgstr "" - -#: ../ProjectController.py:254 +msgstr "Rückgängig" + +#: ../ProjectController.py:423 msgid "Unknown" -msgstr "" - -#: ../editors/Viewer.py:336 -#, fuzzy, python-format +msgstr "Unbekannt" + +#: ../editors/Viewer.py:394 +#, python-format msgid "Unknown variable \"%s\" for this POU!" -msgstr "Debug : Unbekannte Variable %s\n" - -#: ../ProjectController.py:251 -#: ../ProjectController.py:252 +msgstr "Unbekannte Variable \"%s\" für diesen Baustein!" + +#: ../ProjectController.py:420 ../ProjectController.py:421 msgid "Unnamed" -msgstr "" - -#: ../PLCControler.py:305 +msgstr "Unbenannt" + +#: ../PLCControler.py:638 #, python-format msgid "Unnamed%d" -msgstr "" - -#: ../controls/VariablePanel.py:272 +msgstr "Unbenannt%d" + +#: ../controls/VariablePanel.py:284 #, python-format msgid "Unrecognized data size \"%s\"" -msgstr "" - -#: ../plcopen/structures.py:222 -msgid "" -"Up-counter\n" -"The up-counter can be used to signal when a count has reached a maximum value." -msgstr "" - -#: ../plcopen/structures.py:232 -msgid "" -"Up-down counter\n" -"The 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." -msgstr "" - -#: ../controls/VariablePanel.py:709 -#: ../editors/DataTypeEditor.py:623 +msgstr "Nicht erkannte Datengröße \"%s\"" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 msgid "User Data Types" -msgstr "" - -#: ../canfestival/SlaveEditor.py:38 -#: ../canfestival/NetworkEditor.py:68 +msgstr "Benutzerdefinierte Datentypen" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 msgid "User Type" -msgstr "" - -#: ../PLCControler.py:94 +msgstr "Benutzertyp" + +#: ../PLCControler.py:97 msgid "User-defined POUs" -msgstr "" - -#: ../controls/DebugVariablePanel.py:40 -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "Benutzerbausteine" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Value" -msgstr "" - -#: ../editors/GraphicViewer.py:278 -msgid "Values" -msgstr "" - -#: ../editors/DataTypeEditor.py:252 +msgstr "Wert" + +#: ../editors/DataTypeEditor.py:259 msgid "Values:" -msgstr "" - -#: ../controls/DebugVariablePanel.py:40 -#: ../editors/Viewer.py:466 -#: ../dialogs/ActionBlockDialog.py:41 +msgstr "Werte:" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 msgid "Variable" -msgstr "" - -#: ../dialogs/FBDVariableDialog.py:47 -#, fuzzy +msgstr "Variable" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "Variable Drop" + +#: ../dialogs/FBDVariableDialog.py:64 msgid "Variable Properties" -msgstr "Eigenschaften" - -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:330 -#: ../editors/Viewer.py:277 +msgstr "Variableneigenschaften" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Variable class" -msgstr "" - -#: ../editors/TextViewer.py:374 -#: ../editors/Viewer.py:338 +msgstr "Variablenklasse" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 msgid "Variable don't belong to this POU!" -msgstr "" - -#: ../controls/VariablePanel.py:77 +msgstr "Variable gehört nicht zu dieser POU!" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "Variable:" + +#: ../controls/VariablePanel.py:72 msgid "Variables" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:151 +msgstr "Variablen" + +#: ../controls/ProjectPropertiesPanel.py:152 msgid "Vertical:" -msgstr "" - -#: ../wxglade_hmi/wxglade_hmi.py:11 +msgstr "Vertikal:" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "WAMP-Cliet-Start fehlgeschlagen." + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "WAMP Verbindung zu URL : %s\n" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "WAMP Verbindungs-timeout" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "WAMP Verbindung zu '%s' gescheitert.\n" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "WAMP-Import fehlgeschlagen :" + +#: ../wxglade_hmi/wxglade_hmi.py:37 msgid "WXGLADE GUI" msgstr "WXGLADE GUI" -#: ../ProjectController.py:1276 -msgid "Waiting debugger to recover...\n" -msgstr "Warte auf Selbstheilung des Debuggers...\n" - -#: ../editors/LDViewer.py:888 -#: ../dialogs/PouDialog.py:126 +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 msgid "Warning" -msgstr "" - -#: ../ProjectController.py:515 +msgstr "Warnung" + +#: ../ProjectController.py:707 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "Warnungen im ST/IL/SFC Code Generator :\n" -#: ../dialogs/SearchInProjectDialog.py:85 -#, fuzzy +#: ../dialogs/SearchInProjectDialog.py:78 msgid "Whole Project" -msgstr "Projekt schließen" - -#: ../controls/ProjectPropertiesPanel.py:119 +msgstr "Ganze Projekt" + +#: ../controls/ProjectPropertiesPanel.py:120 msgid "Width:" -msgstr "" - -#: ../dialogs/FindInPouDialog.py:86 +msgstr "Breite:" + +#: ../dialogs/FindInPouDialog.py:91 msgid "Wrap search" -msgstr "" - -#: ../features.py:9 +msgstr "Nach dem Ende zum Anfang" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "Geschrieben von" + +#: ../features.py:34 msgid "WxGlade GUI" -msgstr "" - -#: ../svgui/svgui.py:106 +msgstr "WxGlade GUI" + +#: ../svgui/svgui.py:142 msgid "" "You don't have write permissions.\n" "Open Inkscape anyway ?" @@ -3558,7 +3569,7 @@ "Sie haben keine Schreibberechtigung.\n" "soll Inkscape trotzdem geöffnet werden ?" -#: ../wxglade_hmi/wxglade_hmi.py:108 +#: ../wxglade_hmi/wxglade_hmi.py:154 msgid "" "You don't have write permissions.\n" "Open wxGlade anyway ?" @@ -3566,7 +3577,7 @@ "Sie haben keine Schreibberechtigung.\n" "Soll wxGlade dennoch geöffnet werden ?" -#: ../ProjectController.py:220 +#: ../ProjectController.py:371 msgid "" "You must have permission to work on the project\n" "Work on a project copy ?" @@ -3574,98 +3585,132 @@ "Sie müssen Berechtigungen besitzen um mit diesem Projekt arbeiten zu können\n" "Wollen Sie dieses Projekt stattdessen kopieren ?" -#: ../editors/LDViewer.py:883 -msgid "You must select the block or group of blocks around which a branch should be added!" -msgstr "" - -#: ../editors/LDViewer.py:663 +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" +"Sie müssen den Block oder die Gruppe von Blöcken auswählen, um die ein Zweig" +" hinzugefügt werden soll!" + +#: ../editors/LDViewer.py:666 msgid "You must select the wire where a contact should be added!" msgstr "" - -#: ../dialogs/PouNameDialog.py:45 -#: ../dialogs/SFCStepNameDialog.py:47 -#: ../dialogs/SFCStepDialog.py:118 +"Bitte wählen Sie den Draht aus, wo ein Kontakt hinzugefügt werden soll!" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 msgid "You must type a name!" -msgstr "" - -#: ../dialogs/ForceVariableDialog.py:165 +msgstr "Sie müssen einen Namen eingeben!" + +#: ../dialogs/ForceVariableDialog.py:193 msgid "You must type a value!" -msgstr "" - -#: ../IDEFrame.py:414 +msgstr "Sie müssen einen Wert eingeben!" + +#: ../IDEFrame.py:438 msgid "Zoom" -msgstr "" - -#: ../editors/GraphicViewer.py:97 -msgid "Zoom:" -msgstr "" - -#: ../PLCOpenEditor.py:356 +msgstr "Zoom" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "Tage" + +#: ../PLCOpenEditor.py:343 #, python-format msgid "error: %s\n" -msgstr "" - -#: ../util/ProcessLogger.py:161 -#, python-format -msgid "exited with status %s (pid %s)\n" -msgstr "Beendet mit Status %s(pid %s)\n" - -#: ../PLCOpenEditor.py:508 -#: ../PLCOpenEditor.py:510 +msgstr "Fehler: %s\n" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "Beendet mit Status {a1} (pid {a2})\n" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 msgid "file : " msgstr "Datei : " -#: ../dialogs/PouDialog.py:31 -#, fuzzy +#: ../dialogs/PouDialog.py:32 msgid "function" -msgstr "Funktion : " - -#: ../PLCOpenEditor.py:511 +msgstr "Funktion" + +#: ../PLCOpenEditor.py:409 msgid "function : " msgstr "Funktion : " -#: ../dialogs/PouDialog.py:31 -#, fuzzy +#: ../dialogs/PouDialog.py:32 msgid "functionBlock" -msgstr "Funktion : " - -#: ../PLCOpenEditor.py:511 +msgstr "Funktionsblock" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "Stunden" + +#: ../PLCOpenEditor.py:409 msgid "line : " msgstr "Zeile :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "Millisekunden" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "Minuten" + +#: ../dialogs/PouDialog.py:32 msgid "program" -msgstr "" +msgstr "Programm" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "Sekunden" #: ../plcopen/iec_std.csv:84 msgid "string from the middle" -msgstr "" +msgstr "Zeichen aus der Mitte" #: ../plcopen/iec_std.csv:82 msgid "string left of" -msgstr "" +msgstr "Zeichen links von" #: ../plcopen/iec_std.csv:83 msgid "string right of" -msgstr "" - -#: ../PLCOpenEditor.py:354 +msgstr "Zeichen rechts von" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "Updateinformation ist nicht verfügbar." + +#: ../PLCOpenEditor.py:341 #, python-format msgid "warning: %s\n" -msgstr "" +msgstr "Warnung: %s\n" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "{a1} \"{a2}\" kann nicht eingefügt werden als {a3}." + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" +"XML-Datei {a1} folgt nicht XSD-Schema an der Zeile {a2}:\n" +"{a3}" #: Extra XSD strings msgid "CanFestivalSlaveNode" msgstr "CanFestivalSlaveNode" msgid "CAN_Device" -msgstr "CAN_Gerät" +msgstr "CAN Gerät" msgid "CAN_Baudrate" -msgstr "CAN_Baudrate" +msgstr "CAN Baudrate" msgid "NodeId" -msgstr "ZweigId" +msgstr " Node-ID" msgid "Sync_Align" msgstr "Sync_Align" @@ -3683,20 +3728,38 @@ msgstr "CanFestivalInstanz" msgid "CAN_Driver" -msgstr "CAN_Treiber" - -msgid "Debug_mode" -msgstr "Debug_Modus" - -msgid "CExtension" -msgstr "CExtension" +msgstr "CAN Treiber" + +msgid "Generic" +msgstr "Generic" + +msgid "Command" +msgstr "Befehlzeile" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "XenoConfig" + +msgid "Compiler" +msgstr "Compiler" msgid "CFLAGS" msgstr "CFLAGS" +msgid "Linker" +msgstr "Linker" + msgid "LDFLAGS" msgstr "LDFLAGS" +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + msgid "BaseParams" msgstr "BaseParams" @@ -3706,24 +3769,6 @@ msgid "Enabled" msgstr "Aktiviert" -msgid "Linux" -msgstr "Linux" - -msgid "Compiler" -msgstr "Compiler" - -msgid "Linker" -msgstr "Linker" - -msgid "Win32" -msgstr "Win32" - -msgid "Xenomai" -msgstr "Xenomai" - -msgid "XenoConfig" -msgstr "XenoConfig" - msgid "BeremizRoot" msgstr "BeremizRoot" @@ -3731,132 +3776,242 @@ msgstr "ZielTyp" msgid "Libraries" -msgstr "" +msgstr "Bibliotheken" msgid "URI_location" msgstr "URI_location" -#, fuzzy msgid "Disable_Extensions" -msgstr "CExtension" - -#~ msgid "A child with IEC channel %d already exist -> %d\n" -#~ msgstr "Ein Zweig mit IEC-Kanal %d existiert bereits -> %d\n" - -#~ msgid "Add a sub plugin" -#~ msgstr "Unter-Plugin hinzufügen" - -#~ msgid "Append " -#~ msgstr "Hinzufügen" - -#~ msgid "Beremiz\tF1" -#~ msgstr "Beremiz\tF1" - -#~ msgid "Can't find module for target %s!\n" -#~ msgstr "Kann Modul für folgendes Ziel nicht finden: %s!\n" - -#~ msgid "Cannot compare latest build to target. Please build.\n" -#~ msgstr "" -#~ "Kann den letzten Build nicht mit dem Ziel vergleichen. Bitte neu " -#~ "builden.\n" - -#~ msgid "Debug Thread couldn't be killed" -#~ msgstr "Debug Thread konnte nicht beendet werden" - -#~ msgid "Debug data do not match requested variable count %d != %d\n" -#~ msgstr "" -#~ "Debug Daten entsprechen nicht der angeforderten Variablen-Nummer %d != " -#~ "%d\n" - -#~ msgid "Edit CanOpen Network with NetworkEdit" -#~ msgstr "bearbeite CanOpen-Netzwerk mit NetworkEdit" - -#~ msgid "Enable/Disable this plugin" -#~ msgstr "Aktiviere/Deaktiviere dieses PlugIn" - -#~ msgid "Generating plugins C code\n" -#~ msgstr "Generiere Plugin C Code\n" - -#~ msgid "Latest build does not match with target, please transfer.\n" -#~ msgstr "Letzter Build entspricht nicht dem Ziel, bitte Übertragen.\n" - -#~ msgid "Latest build matches target, no transfer needed.\n" -#~ msgstr "Letzter Build entspricht dem Ziel, kein Transfer benötigt.\n" - -#~ msgid "New\tCTRL+N" -#~ msgstr "Neu\tCTRL+N" - -#~ msgid "Open\tCTRL+O" -#~ msgstr "Öffnen\tCTRL+O" - -#~ msgid "Open NetworkEdit" -#~ msgstr "öffne NetworkEdit" - -#~ msgid "Open ObjDictEdit" -#~ msgstr "öffne ObjDictEdit" - -#~ msgid "Plugin : " -#~ msgstr "Plugin : " - -#~ msgid "Quit\tCTRL+Q" -#~ msgstr "Beenden\tCTRL+Q" - -#~ msgid "Redo\tCTRL+Y" -#~ msgstr "Wiederholen\tCTRL+Y" - -#~ msgid "Refresh\tCTRL+R" -#~ msgstr "Refresh\tCTRL+R" - -#~ msgid "Save\tCTRL+S" -#~ msgstr "Speichern\tCTRL+S" - -#~ msgid "Save as\tCTRL+SHIFT+S" -#~ msgstr "Speichern als\tCTRL+SHIFT+S" - -#~ msgid "Simulate" -#~ msgstr "Simuliere" - -#~ msgid "Simulate PLC" -#~ msgstr "Simuliere SPS" - -#~ msgid "Topology" -#~ msgstr "Topologie" - -#~ msgid "Unable to get Xenomai's LDFLAGS\n" -#~ msgstr "Unmöglich, Xenomai's LDFLAGS auszulesen\n" - -#~ msgid "Undo\tCTRL+Z" -#~ msgstr "Undo\tCTRL+Z" - -#~ msgid "Wrong URI, please check it !\n" -#~ msgstr "Falsche URI, bitte überprüfen !\n" - -#~ msgid "" -#~ "You don't have write permissions.\n" -#~ "Open CFileEditor anyway ?" -#~ msgstr "" -#~ "Sie haben keine Schreibberechtigung.\n" -#~ "Soll CFileEditor trotzdem geöffnet werden?" - -#~ msgid "" -#~ "You don't have write permissions.\n" -#~ "Open NetworkEdit anyway ?" -#~ msgstr "" -#~ "Sie haben keine Leseberechtigung.\n" -#~ "Soll Open NetworkEdit trotzdem geöffnet werden ?" - -#~ msgid "" -#~ "You don't have write permissions.\n" -#~ "Open ObjDictEdit anyway ?" -#~ msgstr "" -#~ "Sie haben keine Leseberechtigung.\n" -#~ "Soll ObjDictEdit trotzdem geöffnet werden ?" - -#~ msgid "Enable_Plugins" -#~ msgstr "Enable_Plugins" - -#~ msgid "Rtai" -#~ msgstr "Rtai" - -#~ msgid "rtai_config" -#~ msgstr "rtai_config" +msgstr "Disable_Extensions" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)s" + +msgid "variables" +msgstr "Variablen" + +msgid "variable" +msgstr "Variable" + +msgid "name" +msgstr "Name" + +msgid "type" +msgstr "Typ" + +msgid "class" +msgstr "class" + +msgid "initial" +msgstr "initial" + +msgid "desc" +msgstr "desc" + +msgid "onchange" +msgstr "onchange" + +msgid "opts" +msgstr "opts" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "0 - aktuelle Zeit, 1 - stellen die Zeit von PDT ein" + +msgid "Preset datetime" +msgstr "Zeiteinstellung" + +msgid "Copy of IN" +msgstr "Kopie von IN" + +msgid "Datetime, current or relative to PDT" +msgstr "Zeit, aktuell oder relativ zu PDT" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" +"Die Echtzeituhr hat viele Einsatzmöglichkeiten inklusive Zeitstempel, setzen" +" Datum und Tageszeiten in den Reports, in Alarmmeldungen und so weiter." + +msgid "1 = integrate, 0 = hold" +msgstr "1 = integrieren, 0 = halten" + +msgid "Overriding reset" +msgstr "Reset" + +msgid "Input variable" +msgstr "Eingansvariable" + +msgid "Initial value" +msgstr "Anfangswert" + +msgid "Sampling period" +msgstr "Sampling-Periode" + +msgid "NOT R1" +msgstr "NOT R1" + +msgid "Integrated output" +msgstr "Integrierter Ausgangswert" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "Der Baustein integriert den Wert des Eingangs XIN über die Zeit." + +msgid "0 = reset" +msgstr "0 = reset" + +msgid "Input to be differentiated" +msgstr "Eingabe, die differenziert wird" + +msgid "Differentiated output" +msgstr "Differenzierter Ausgangswert" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" +"Der Ableitungsfunktionsblock erzeugt einen Ausgang XOUT proportional zur " +"Änderungsrate des Eingangs XIN." + +msgid "0 - manual , 1 - automatic" +msgstr "0 - manuell, 1 - automatisch" + +msgid "Process variable" +msgstr "Prozessvariable" + +msgid "Set point" +msgstr "Sollwert" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "Manuelle Ausgabeanpassung" + +msgid "Proportionality constant" +msgstr "Proportionalitätskoeffizient" + +msgid "Reset time" +msgstr "Zeit zurücksetzen" + +msgid "Derivative time constant" +msgstr "Vorhaltzeit, Verstärkungsfaktor des D-Anteils" + +msgid "PV - SP" +msgstr "PV - SP" + +msgid "FB for integral term" +msgstr "Integral, I-Regler" + +msgid "FB for derivative term" +msgstr "Differenzierer" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" +"PID (Proportional, Integral, Derivative) Baustein stellt der klassische PID-" +"Regler zur Verfügung." + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "0 - Ausgang ist X0, 1 - Rampe von X0 nach X1 oder zurück generieren" + +msgid "Ramp duration" +msgstr "Rampenzeit" + +msgid "BUSY = 1 during ramping period" +msgstr "BUSY = 1 während der Rampenzeit" + +msgid "Elapsed time of ramp" +msgstr "Verstrichene Zeit der Rampe" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" +"Der RAMP-Baustein ist nach dem in der Norm angegebenen Beispiel modelliert." + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" +"Der Hysteresebaustein liefert einen booleschen Ausgang XOUT, der durch die " +"Differenz von zwei Gleitkommaeingängen (REAL) XIN1 und XIN2 gesteuert wird." + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "SR ist ein bistabiler Trigger, wo das Set dominiert." + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "RS ist ein bistabiler Trigger, wo das Reset dominiert." + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" +"Das Semaphor bietet einen Mechanismus, um Software-Elementen gegenseitig " +"ausschließlichen Zugriff auf bestimmte Ressourcen zu ermöglichen." + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" +"Die Ausgabe erzeugt einen einzigen Impuls, wenn eine steigende Flanke " +"erkannt wird." + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" +"Die Ausgabe erzeugt einen einzigen Impuls, wenn eine fallende Flanke erkannt" +" wird." + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" +"Der Aufwärtszähler kann verwendet werden, um zu signalisieren, wenn der " +"Zähler den maximalen Wert erreicht hat." + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" +"Der Abwärtszähler kann verwendet werden, um zu signalisieren, wenn der " +"Zähler Null erreicht hat." + +msgid "" +"The 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." +msgstr "" +"Der Auf- und Abwärtszähler verfügt über zwei Eingänge CU und CD. Es kann " +"verwendet werden, um an einem Eingang nach oben und an dem anderen nach " +"unten zu zählen." + +msgid "first input parameter" +msgstr "Erster Eingangsparameter" + +msgid "second input parameter" +msgstr "Zweiter Eingangsparameter" + +msgid "first output parameter" +msgstr "Erster Ausgabeparameter" + +msgid "second output parameter" +msgstr "Zweiter Ausgabeparameter" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "Interner Zustand: 0-Reset, 1-Zählen, 2-Set" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" +"Der Pulsegeber kann verwendet werden, um Impulse einer vorgegebenen " +"Zeitdauer zu erzeugen." + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" +"Der An-Verzögerungszeitgeber kann verwendet werden, um Einschalten des " +"Ausgangs auf eine feste Periode nach Einschalten des Eingangs zu verzögern." + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" +"Der Aus-Verzögerungszeitgeber kann verwendet werden, um Ausschalten des " +"Ausgangs auf eine feste Periode nach Ausschalten des Eingangs zu verzögern." diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_es_ES.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_es_ES.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3914 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Marcial González de Armas , 2017 +# Carlos Guilarte , 2017 +# Nelson Mambre , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Nelson Mambre , 2017\n" +"Language-Team: Spanish (Spain) (https://www.transifex.com/beremiz/teams/75746/es_ES/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es_ES\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" +"\n" +"Ha ocurrido un error desconocido (bug). El informe de error fué guardado en:\n" +"(%s)\n" +"\n" +"Por favor si es tan amable envié el archivo de error a:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"Debe reiniciar el programa.\n" +"\n" +"Rastreo:\n" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr "Externa" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr "EntradaSalida" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr "Entrada" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr " Local" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr "Salida" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr "Temporal" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr "" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr "Generación fallida!\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "\"%s\" Tipo de dato no existente !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "\"%s\" POU ya existente !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "\"%s\" POU no existente !!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" no puede usarse a si mismo!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "\"%s\" config ya existente!" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "\"%s\" configuración ya existente !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "\"%s\" tipo de dato ya existente!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "\"%s\" elemento para esta POU ya existente!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "\"%s\" carpeta no válida para el proyecto Beremiz\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "\"%s\" es una palabra clave. No puede ser usada!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "\"%s\" es un valor inválido!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "\"%s\" no es una carpeta válida" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "\"%s\" no es un identificador válido" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "\"%s\" es usado por uno o más POUs. Deseas continuar?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "\"%s\" POU ya existe!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "\"%s\" valor ya definido\"" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "\"%s\" no es una dimensión válida para el array!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"\"%s\" no es una dimensión válida para el array!\n" +"El valor de la derecha debe ser mayor que el de la izquierda." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "\"{a1}\" llamada a la función en \"{a2}\" POU: No hay entrada conectada" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" ya está en uso por \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" el recurso ya existe en la configuración \"{a2}\" !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" el recurso no existe en la configuración \"{a2}\" !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dd" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "%s Tipos de Datos" + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%s POUs" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "Perfil %s" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "%s El cuerpo no tiene instancias!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "%s El cuerpo no posee texto!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "&Añadir Elemento" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Cerrar" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "&Configuración" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "&TipodeDato" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "&Eliminar" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "&Mostrar" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "&Editar" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "&Archivo" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "&Función" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "&Ayuda" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&Licencia" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "&Programa" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "&Propiedades" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "&Proyectos Recientes" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "&Recursos" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} coincidencia en el proyecto" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} coincidencias en el proyecto" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' ubicado en {a2}\n" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "(%d coincidencias)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr "," + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr ". " + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1d" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"Un POU tiene un elemento llamado \"%s\". Esto puede causar conflicto. ¿Desea" +" Continuar?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "Un POU llamado \"%s\" ya existe!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "Hijo llamado \"{a1}\" ya existe -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "¡Debe seleccionar una localización!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "¡Y existe una tarea con ese nombre!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "¡Variable llamada \"%s\" ya existente en este pou!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "¡Variable llamada \"%s\" ya existente!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "Acerca de" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "Valor absoluto" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "Acción" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "Bloque de acción" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "Nombre de acción" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "Nombre de acción:" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "Acción llamada %s no existe!" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "Acciones" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "Acciones:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Activo" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "Añadir" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "Añadir acción" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "" + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "Ya existe un elemento con el nombre \"%s\" en esta estructura! " + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Ya existe una instancia con este nombre!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "Arco coseno" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "Arco seno" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "Arco tangente" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "Aritmética" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "Asignación" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "Debe seleccionar al menos una variable o expresión!" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "Autor" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "Nombre del autor (opcional):" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "" + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "Bloque" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "" + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "" + +#: ../features.py:32 +msgid "C extension" +msgstr "" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "" + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "" + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "Falla de conexión - No puedo obtener el estado del PLC\n" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "" + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "" + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "" + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "" + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "" + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "" + +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "" + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "" + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "" + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "" + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "" + +#: ../features.py:33 +msgid "Python file" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "" + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "" + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "" + +msgid "CAN_Device" +msgstr "" + +msgid "CAN_Baudrate" +msgstr "" + +msgid "NodeId" +msgstr "" + +msgid "Sync_Align" +msgstr "" + +msgid "Sync_Align_Ratio" +msgstr "" + +msgid "CanFestivalNode" +msgstr "" + +msgid "Sync_TPDOs" +msgstr "" + +msgid "CanFestivalInstance" +msgstr "" + +msgid "CAN_Driver" +msgstr "" + +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "" + +msgid "XenoConfig" +msgstr "" + +msgid "Compiler" +msgstr "" + +msgid "CFLAGS" +msgstr "" + +msgid "Linker" +msgstr "" + +msgid "LDFLAGS" +msgstr "" + +msgid "Linux" +msgstr "" + +msgid "Win32" +msgstr "" + +msgid "BaseParams" +msgstr "" + +msgid "IEC_Channel" +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "BeremizRoot" +msgstr "" + +msgid "TargetType" +msgstr "" + +msgid "Libraries" +msgstr "" + +msgid "URI_location" +msgstr "" + +msgid "Disable_Extensions" +msgstr "" + +msgid "%(codefile_name)s" +msgstr "" + +msgid "variables" +msgstr "" + +msgid "variable" +msgstr "" + +msgid "name" +msgstr "" + +msgid "type" +msgstr "" + +msgid "class" +msgstr "" + +msgid "initial" +msgstr "" + +msgid "desc" +msgstr "" + +msgid "onchange" +msgstr "" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "" + +msgid "second input parameter" +msgstr "" + +msgid "first output parameter" +msgstr "" + +msgid "second output parameter" +msgstr "" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_fr_FR.po --- a/i18n/Beremiz_fr_FR.po Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/Beremiz_fr_FR.po Tue Jan 30 16:06:58 2018 +0100 @@ -1,44 +1,26 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Andrey Skvortsov , 2017 +# Fabien M , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2013-03-26 22:55+0100\n" -"PO-Revision-Date: 2013-03-26 23:08+0100\n" -"Last-Translator: Laurent BESSARD \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Fabien M , 2017\n" +"Language-Team: French (France) (https://www.transifex.com/beremiz/teams/75746/fr_FR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" - -#: ../PLCOpenEditor.py:405 -msgid "" -"\n" -"An error has occurred.\n" -"\n" -"Click OK to save an error report.\n" -"\n" -"Please be kind enough to send this file to:\n" -"edouard.tisserant@gmail.com\n" -"\n" -"Error:\n" -msgstr "" -"\n" -"Une erreur est apparue.\n" -"\n" -"Appuyer sur 'Valider' pour enregistrer un rapport d'erreur.\n" -"\n" -"Envoyez ce fichier à l'adresse :\n" -"edouard.tisserant@gmail.com\n" -"\n" -"Erreur:\n" - -#: ../Beremiz.py:1119 +"Language: fr_FR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 #, python-format msgid "" "\n" @@ -48,223 +30,167 @@ "Please be kind enough to send this file to:\n" "beremiz-devel@lists.sourceforge.net\n" "\n" -"You should now restart Beremiz.\n" +"You should now restart program.\n" "\n" "Traceback:\n" msgstr "" "\n" -"Une erreur inconnue (bug) est apparu. Le rapport d'erreur a été sauvé dans :\n" +"Une exception non gérée (bug) est survenue. Le bug est inscrit dans :\n" "(%s)\n" "\n" -"Envoyez ce fichier à l'adresse :\n" +"S'il vous plaît, soyez sympa et envoyez ce fichier à :\n" "beremiz-devel@lists.sourceforge.net\n" "\n" -"Vous devriez redémarrer Beremiz.\n" -"\n" -"Trace d'exécution:\n" - -#: ../controls/VariablePanel.py:77 +"Vous devriez redémarrer le programme.\n" +"Trace:\n" + +#: ../controls/VariablePanel.py:72 msgid " External" msgstr " Externe" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " InOut" msgstr " Entrée-Sortie" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " Input" msgstr " Entrée" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid " Local" msgstr " Locale" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " Output" msgstr " Sortie" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid " Temp" msgstr " Temporaire" -#: ../PLCOpenEditor.py:415 -msgid " : " -msgstr " : " - -#: ../dialogs/PouTransitionDialog.py:94 -#: ../dialogs/PouActionDialog.py:91 -#: ../dialogs/PouDialog.py:111 -#: ../dialogs/SFCTransitionDialog.py:144 +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 #, python-format msgid " and %s" msgstr " et %s" -#: ../ProjectController.py:917 +#: ../ProjectController.py:1151 msgid " generation failed !\n" msgstr "la construction a échouée !\n" -#: ../plcopen/plcopen.py:1051 +#: ../plcopen/plcopen.py:886 #, python-format msgid "\"%s\" Data Type doesn't exist !!!" msgstr "Le type de donnée \"%s\" n'existe pas !!!" -#: ../plcopen/plcopen.py:1069 +#: ../plcopen/plcopen.py:904 #, python-format msgid "\"%s\" POU already exists !!!" msgstr "Le POU \"%s\" existe déjà !!!" -#: ../plcopen/plcopen.py:1090 +#: ../plcopen/plcopen.py:925 #, python-format msgid "\"%s\" POU doesn't exist !!!" msgstr "Le POU \"%s\" n'existe pas !!!" -#: ../editors/Viewer.py:234 +#: ../editors/Viewer.py:247 #, python-format msgid "\"%s\" can't use itself!" msgstr "\"%s\" ne peut pas s'utiliser lui-même !" -#: ../IDEFrame.py:1587 -#: ../IDEFrame.py:1606 +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 #, python-format msgid "\"%s\" config already exists!" msgstr "La configuration \"%s\" existe déjà !" -#: ../plcopen/plcopen.py:315 +#: ../plcopen/plcopen.py:472 #, python-format msgid "\"%s\" configuration already exists !!!" msgstr "La configuration \"%s\" existe déjà !!!" -#: ../IDEFrame.py:1541 +#: ../IDEFrame.py:1605 #, python-format msgid "\"%s\" data type already exists!" msgstr "Le type de données \"%s\" existe déjà !" -#: ../PLCControler.py:2165 -#: ../PLCControler.py:2169 -#, python-format -msgid "\"%s\" element can't be pasted here!!!" -msgstr "L'élément \"%s\" ne peut être collé ici !!!" - -#: ../editors/TextViewer.py:298 -#: ../editors/TextViewer.py:318 -#: ../editors/Viewer.py:250 -#: ../dialogs/PouTransitionDialog.py:105 -#: ../dialogs/ConnectionDialog.py:157 -#: ../dialogs/PouActionDialog.py:102 -#: ../dialogs/FBDBlockDialog.py:162 +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 #, python-format msgid "\"%s\" element for this pou already exists!" msgstr "Un élément \"%s\" existe déjà dans ce POU !" -#: ../Beremiz.py:921 +#: ../BeremizIDE.py:897 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "Le dossier \"%s\" ne contient pas de projet Beremiz valide\n" -#: ../plcopen/structures.py:105 -#, python-format -msgid "\"%s\" function cancelled in \"%s\" POU: No input connected" -msgstr "L'appel à la fonction \"%s\" dans le POU \"%s\" a été abandonné : aucune entrée connectée" - -#: ../controls/VariablePanel.py:659 -#: ../IDEFrame.py:1532 -#: ../editors/DataTypeEditor.py:554 -#: ../editors/DataTypeEditor.py:583 -#: ../dialogs/PouNameDialog.py:49 -#: ../dialogs/PouTransitionDialog.py:101 -#: ../dialogs/SFCStepNameDialog.py:51 -#: ../dialogs/ConnectionDialog.py:153 -#: ../dialogs/FBDVariableDialog.py:199 -#: ../dialogs/PouActionDialog.py:98 -#: ../dialogs/PouDialog.py:118 -#: ../dialogs/SFCStepDialog.py:122 -#: ../dialogs/FBDBlockDialog.py:158 +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 #, python-format msgid "\"%s\" is a keyword. It can't be used!" msgstr "\"%s\" est un mot réservé. Il ne peut être utilisé !" -#: ../editors/Viewer.py:238 -#, python-format -msgid "\"%s\" is already used by \"%s\"!" -msgstr "\"%s\" est déjà utilisé par \"%s\" !" - -#: ../plcopen/plcopen.py:2836 +#: ../plcopen/plcopen.py:2417 #, python-format msgid "\"%s\" is an invalid value!" msgstr "\"%s\" n'est pas une valeur valide !" -#: ../PLCOpenEditor.py:341 -#: ../PLCOpenEditor.py:378 +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 #, python-format msgid "\"%s\" is not a valid folder!" msgstr "\"%s\" n'est pas un répertoire valide !" -#: ../controls/VariablePanel.py:657 -#: ../IDEFrame.py:1530 -#: ../editors/DataTypeEditor.py:578 -#: ../dialogs/PouNameDialog.py:47 -#: ../dialogs/PouTransitionDialog.py:99 -#: ../dialogs/SFCStepNameDialog.py:49 -#: ../dialogs/ConnectionDialog.py:151 -#: ../dialogs/PouActionDialog.py:96 -#: ../dialogs/PouDialog.py:116 -#: ../dialogs/SFCStepDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:156 +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 #, python-format msgid "\"%s\" is not a valid identifier!" msgstr "\"%s\" n'est pas un identifiant valide !" -#: ../IDEFrame.py:221 -#: ../IDEFrame.py:2313 -#: ../IDEFrame.py:2332 -#, python-format -msgid "\"%s\" is used by one or more POUs. It can't be removed!" -msgstr "Le POU \"%s\" est utilisé par un ou plusieurs POUs. Il ne peut être supprimé !" - -#: ../controls/VariablePanel.py:313 -#: ../IDEFrame.py:1550 -#: ../editors/TextViewer.py:296 -#: ../editors/TextViewer.py:316 -#: ../editors/TextViewer.py:353 -#: ../editors/Viewer.py:248 -#: ../editors/Viewer.py:293 -#: ../editors/Viewer.py:311 -#: ../dialogs/ConnectionDialog.py:155 -#: ../dialogs/PouDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:160 +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "" +"\"%s\" est utilisé par un POU ou plus. Souhaitez vous continuer malgré tout " +"?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 #, python-format msgid "\"%s\" pou already exists!" msgstr "Le POU \"%s\" existe déjà !" -#: ../plcopen/plcopen.py:346 -#, python-format -msgid "\"%s\" resource already exists in \"%s\" configuration !!!" -msgstr "La ressource \"%s\" existe déjà dans la configuration \"%s\" !!!" - -#: ../plcopen/plcopen.py:362 -#, python-format -msgid "\"%s\" resource doesn't exist in \"%s\" configuration !!!" -msgstr "La ressource \"%s\" n'existe pas dans la configuration \"%s\" !!!" - -#: ../dialogs/SFCStepNameDialog.py:57 -#: ../dialogs/SFCStepDialog.py:128 +#: ../dialogs/SFCStepNameDialog.py:58 #, python-format msgid "\"%s\" step already exists!" msgstr "L'étape \"%s\" existe déjà !" -#: ../editors/DataTypeEditor.py:549 +#: ../editors/DataTypeEditor.py:550 #, python-format msgid "\"%s\" value already defined!" msgstr "La valeur \"%s\" est déjà définie !" -#: ../editors/DataTypeEditor.py:744 -#: ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 #, python-format msgid "\"%s\" value isn't a valid array dimension!" msgstr "\"%s\" n'est pas une dimension de tableau valide !" -#: ../editors/DataTypeEditor.py:751 -#: ../dialogs/ArrayTypeDialog.py:103 +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 #, python-format msgid "" "\"%s\" value isn't a valid array dimension!\n" @@ -273,206 +199,256 @@ "\"%s\" n'est pas une dimension de tableau valide !\n" "La valeur de droite doit être supérieur à celle de gauche." -#: ../PLCControler.py:847 -#, python-format -msgid "%s \"%s\" can't be pasted as a %s." -msgstr "Le %s \"%s\" ne peut être collé en tant que %s." - -#: ../PLCControler.py:1476 +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "Fonction \"{a1}\" annulée dans \"{a2}\" POU: Pas d'entrée connectée" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" est déjà utilisé par \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "La ressource \"{a1}\" existe déjà dans la configuration \"{a2}\" !" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "La ressource \"${a1}\" n'existe pas dans la configuration \"{a2}\" !" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dj" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 #, python-format msgid "%s Data Types" msgstr "Types de données de %s" -#: ../editors/GraphicViewer.py:278 -#, python-format -msgid "%s Graphics" -msgstr "Graphique %s" - -#: ../PLCControler.py:1471 +#: ../PLCControler.py:1516 #, python-format msgid "%s POUs" msgstr "POUs de %s" -#: ../canfestival/SlaveEditor.py:46 -#: ../canfestival/NetworkEditor.py:67 +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 #, python-format msgid "%s Profile" msgstr "Profil %s" -#: ../plcopen/plcopen.py:1790 -#: ../plcopen/plcopen.py:1800 -#: ../plcopen/plcopen.py:1810 -#: ../plcopen/plcopen.py:1820 -#: ../plcopen/plcopen.py:1829 +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 #, python-format msgid "%s body don't have instances!" msgstr "Le code d'un %s n'a pas d'instances !" -#: ../plcopen/plcopen.py:1852 -#: ../plcopen/plcopen.py:1859 +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 #, python-format msgid "%s body don't have text!" msgstr "Le code d'un %s n'a pas de texte !" -#: ../IDEFrame.py:369 +#: ../IDEFrame.py:386 msgid "&Add Element" msgstr "&Ajouter un élément" -#: ../IDEFrame.py:339 +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Close" + +#: ../IDEFrame.py:356 msgid "&Configuration" msgstr "&Configuration" -#: ../IDEFrame.py:330 +#: ../IDEFrame.py:345 msgid "&Data Type" msgstr "&Type de donnée" -#: ../IDEFrame.py:373 +#: ../IDEFrame.py:390 msgid "&Delete" msgstr "&Supprimer" -#: ../IDEFrame.py:322 +#: ../IDEFrame.py:337 msgid "&Display" msgstr "&Affichage" -#: ../IDEFrame.py:321 +#: ../IDEFrame.py:336 msgid "&Edit" msgstr "&Editer" -#: ../IDEFrame.py:320 +#: ../IDEFrame.py:335 msgid "&File" msgstr "&Fichier" -#: ../IDEFrame.py:332 +#: ../IDEFrame.py:347 msgid "&Function" msgstr "&Fonction" -#: ../IDEFrame.py:323 +#: ../IDEFrame.py:338 msgid "&Help" msgstr "&Aide" -#: ../IDEFrame.py:336 +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&License" + +#: ../IDEFrame.py:351 msgid "&Program" msgstr "&Programme" -#: ../PLCOpenEditor.py:129 +#: ../PLCOpenEditor.py:127 msgid "&Properties" msgstr "&Propriétés" -#: ../Beremiz.py:312 +#: ../BeremizIDE.py:219 msgid "&Recent Projects" msgstr "Projets &récent" -#: ../Beremiz.py:354 +#: ../IDEFrame.py:353 msgid "&Resource" msgstr "&Ressource" -#: ../controls/SearchResultPanel.py:252 -#, python-format -msgid "'%s' - %d match in project" -msgstr "'%s' - %d correspondance dans le projet" - -#: ../controls/SearchResultPanel.py:254 -#, python-format -msgid "'%s' - %d matches in project" -msgstr "'%s' - %d correspondances dans le projet" - -#: ../connectors/PYRO/__init__.py:51 -#, python-format -msgid "'%s' is located at %s\n" -msgstr "'%s' is disponible à l'adresse %s\n" - -#: ../controls/SearchResultPanel.py:304 +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} concordent dans le projet" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} concordent dans le projet" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' est situé dans {a2}\n" + +#: ../controls/SearchResultPanel.py:291 #, python-format msgid "(%d matches)" msgstr "(%d correspondances)" -#: ../PLCOpenEditor.py:393 -#: ../PLCOpenEditor.py:395 -#: ../PLCOpenEditor.py:396 +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 msgid ", " msgstr ", " -#: ../dialogs/PouTransitionDialog.py:96 -#: ../dialogs/PouActionDialog.py:93 -#: ../dialogs/PouDialog.py:113 -#: ../dialogs/SFCTransitionDialog.py:146 +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 #, python-format msgid ", %s" msgstr ", %s" -#: ../PLCOpenEditor.py:391 +#: ../PLCOpenEditor.py:404 msgid ". " msgstr ". " -#: ../ProjectController.py:1294 -msgid "... debugger recovered\n" -msgstr "... déboggueur operationel\n" - -#: ../IDEFrame.py:1553 -#: ../IDEFrame.py:1595 -#: ../IDEFrame.py:1614 -#: ../dialogs/PouDialog.py:122 -#, python-format -msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" -msgstr "Un POU a un élément nommé \"%s\". Cela peut générer des conflits. Voulez-vous continuer ?" - -#: ../controls/VariablePanel.py:661 -#: ../IDEFrame.py:1565 -#: ../IDEFrame.py:1576 -#: ../dialogs/PouNameDialog.py:51 -#: ../dialogs/PouTransitionDialog.py:103 -#: ../dialogs/SFCStepNameDialog.py:53 -#: ../dialogs/PouActionDialog.py:100 -#: ../dialogs/SFCStepDialog.py:124 +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1j" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"Un POU a un élément nommé \"%s\". Cela peut générer des conflits. Voulez-" +"vous continuer ?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 #, python-format msgid "A POU named \"%s\" already exists!" msgstr "Un POU nommé \"%s\" existe déjà !" -#: ../ConfigTreeNode.py:388 -#, python-format -msgid "A child named \"%s\" already exist -> \"%s\"\n" -msgstr "Un noeud enfant nommé \"%s\" existe déjà -> \"%s\"\n" - -#: ../dialogs/BrowseLocationsDialog.py:212 +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "Un enfant nommé \"{a1}\" existe déjà -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 msgid "A location must be selected!" msgstr "Une adresse doit être sélectionné !" -#: ../controls/VariablePanel.py:663 -#: ../IDEFrame.py:1567 -#: ../IDEFrame.py:1578 -#: ../dialogs/SFCStepNameDialog.py:55 -#: ../dialogs/SFCStepDialog.py:126 +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Une tâche du même nom existe déjà" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 #, python-format msgid "A variable with \"%s\" as name already exists in this pou!" msgstr "Une variable nommée \"%s\" existe déjà dans ce POU !" -#: ../Beremiz.py:364 -#: ../PLCOpenEditor.py:162 +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "Une variable avec le nom \"%s\" existe déjà !" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 msgid "About" msgstr "A propos" -#: ../Beremiz.py:957 -msgid "About Beremiz" -msgstr "A propos de Beremiz" - -#: ../PLCOpenEditor.py:355 -msgid "About PLCOpenEditor" -msgstr "A propos de PLCOpenEditor" - #: ../plcopen/iec_std.csv:22 msgid "Absolute number" msgstr "Nombre absolu" -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCStepDialog.py:69 +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 msgid "Action" msgstr "Action" -#: ../editors/Viewer.py:494 +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 msgid "Action Block" msgstr "Ajouter un bloc fonctionnel" -#: ../dialogs/PouActionDialog.py:81 +#: ../dialogs/PouActionDialog.py:82 msgid "Action Name" msgstr "Nom de l'action" @@ -480,116 +456,110 @@ msgid "Action Name:" msgstr "Nom de l'action :" -#: ../plcopen/plcopen.py:1490 +#: ../plcopen/plcopen.py:1364 #, python-format msgid "Action with name %s doesn't exist!" msgstr "L'action nommée %s n'existe pas !" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Actions" msgstr "Actions" -#: ../dialogs/ActionBlockDialog.py:134 +#: ../dialogs/ActionBlockDialog.py:133 msgid "Actions:" msgstr "Actions :" -#: ../editors/Viewer.py:999 +#: ../editors/Viewer.py:431 msgid "Active" msgstr "Actif" -#: ../canfestival/SlaveEditor.py:57 -#: ../canfestival/NetworkEditor.py:78 -#: ../Beremiz.py:987 -#: ../editors/Viewer.py:527 +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 msgid "Add" msgstr "Ajouter" -#: ../IDEFrame.py:1801 -#: ../IDEFrame.py:1832 +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 msgid "Add Action" msgstr "Ajouter une action" -#: ../features.py:8 +#: ../features.py:32 msgid "Add C code accessing located variables synchronously" -msgstr "Ajoute un code C ayant accès à des variables localisées de façon synchrone" - -#: ../IDEFrame.py:1784 +msgstr "" +"Ajoute un code C ayant accès à des variables localisées de façon synchrone" + +#: ../IDEFrame.py:1876 msgid "Add Configuration" msgstr "Ajouter une configuration" -#: ../IDEFrame.py:1764 +#: ../IDEFrame.py:1856 msgid "Add DataType" msgstr "Ajouter un type de donnée" -#: ../editors/Viewer.py:452 +#: ../editors/Viewer.py:572 msgid "Add Divergence Branch" msgstr "Ajouter une branche à la divergence" -#: ../dialogs/DiscoveryDialog.py:115 +#: ../dialogs/DiscoveryDialog.py:117 msgid "Add IP" msgstr "Ajouter IP" -#: ../IDEFrame.py:1772 +#: ../IDEFrame.py:1864 msgid "Add POU" msgstr "Ajouter un POU" -#: ../features.py:9 +#: ../features.py:33 msgid "Add Python code executed asynchronously" msgstr "Ajoute un code Python executé de façon asynchone" -#: ../IDEFrame.py:1812 -#: ../IDEFrame.py:1858 +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 msgid "Add Resource" msgstr "Ajouter une resource" -#: ../IDEFrame.py:1790 -#: ../IDEFrame.py:1829 +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 msgid "Add Transition" msgstr "Ajouter une transition" -#: ../editors/Viewer.py:441 +#: ../editors/Viewer.py:559 msgid "Add Wire Segment" msgstr "Ajouter un segment au fil" -#: ../editors/SFCViewer.py:359 +#: ../editors/SFCViewer.py:433 msgid "Add a new initial step" msgstr "Ajouter une nouvelle étape initiale" -#: ../editors/Viewer.py:2363 -#: ../editors/SFCViewer.py:696 +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 msgid "Add a new jump" msgstr "Ajouter un nouveau renvoi" -#: ../editors/SFCViewer.py:381 +#: ../editors/SFCViewer.py:455 msgid "Add a new step" msgstr "Ajouter une nouvelle étape" -#: ../features.py:10 +#: ../features.py:34 msgid "Add a simple WxGlade based GUI." msgstr "Ajoute une interface simple utilisant WxGlade" -#: ../dialogs/ActionBlockDialog.py:138 +#: ../dialogs/ActionBlockDialog.py:137 msgid "Add action" msgstr "Ajouter une action" -#: ../editors/DataTypeEditor.py:351 +#: ../editors/DataTypeEditor.py:352 msgid "Add element" msgstr "Ajouter un élément" -#: ../editors/ResourceEditor.py:259 +#: ../editors/ResourceEditor.py:268 msgid "Add instance" msgstr "Ajouter une instance" -#: ../canfestival/NetworkEditor.py:80 +#: ../canfestival/NetworkEditor.py:103 msgid "Add slave" msgstr "Ajouter un esclave" -#: ../editors/ResourceEditor.py:230 +#: ../editors/ResourceEditor.py:239 msgid "Add task" msgstr "Ajouter une tâche" -#: ../controls/VariablePanel.py:380 -#: ../c_ext/CFileEditor.py:517 +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 msgid "Add variable" msgstr "Ajouter une variable" @@ -597,23 +567,23 @@ msgid "Addition" msgstr "Addition" -#: ../plcopen/structures.py:249 +#: ../plcopen/definitions.py:49 msgid "Additional function blocks" msgstr "Blocs fonctionnels additionnels" -#: ../editors/Viewer.py:510 +#: ../editors/Viewer.py:630 msgid "Adjust Block Size" msgstr "Ajuster la taille des blocs" -#: ../editors/Viewer.py:1458 +#: ../editors/Viewer.py:1686 msgid "Alignment" msgstr "Alignement" -#: ../controls/VariablePanel.py:75 -#: ../dialogs/BrowseLocationsDialog.py:34 -#: ../dialogs/BrowseLocationsDialog.py:43 -#: ../dialogs/BrowseLocationsDialog.py:136 -#: ../dialogs/BrowseLocationsDialog.py:139 +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 msgid "All" msgstr "Tout" @@ -621,18 +591,24 @@ msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" msgstr "Tous les fichiers|*.*|Fichiers CSV (*.csv)|*.csv" -#: ../ProjectController.py:1373 +#: ../ProjectController.py:1685 msgid "Already connected. Please disconnect\n" msgstr "Déjà connecté. Veuillez déconnecter\n" -#: ../editors/DataTypeEditor.py:593 +#: ../editors/DataTypeEditor.py:591 #, python-format msgid "An element named \"%s\" already exists in this structure!" msgstr "Un élément nommé \"%s\" existe déjà dans la structure !" -#: ../dialogs/ConnectionDialog.py:98 +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Une instance avec le même nom existe déjà !" + +#: ../dialogs/ConnectionDialog.py:100 msgid "Apply name modification to all continuations with the same name" -msgstr "Appliquer la modification de nom à toutes les prolongements portant ce même nom" +msgstr "" +"Appliquer la modification de nom à toutes les prolongements portant ce même " +"nom" #: ../plcopen/iec_std.csv:31 msgid "Arc cosine" @@ -650,9 +626,8 @@ msgid "Arithmetic" msgstr "Arithmétique" -#: ../controls/VariablePanel.py:732 -#: ../editors/DataTypeEditor.py:54 -#: ../editors/DataTypeEditor.py:634 +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 msgid "Array" msgstr "Tableau" @@ -660,19 +635,19 @@ msgid "Assignment" msgstr "Assignation" -#: ../dialogs/FBDVariableDialog.py:197 +#: ../dialogs/FBDVariableDialog.py:222 msgid "At least a variable or an expression must be selected!" msgstr "Au moins une variable ou une expression doit être sélectionné !" -#: ../controls/ProjectPropertiesPanel.py:99 +#: ../controls/ProjectPropertiesPanel.py:100 msgid "Author" msgstr "Auteur" -#: ../controls/ProjectPropertiesPanel.py:96 +#: ../controls/ProjectPropertiesPanel.py:97 msgid "Author Name (optional):" msgstr "Nom de l'auteur (optionel) :" -#: ../dialogs/FindInPouDialog.py:72 +#: ../dialogs/FindInPouDialog.py:77 msgid "Backward" msgstr "Vers le haut" @@ -684,25 +659,21 @@ msgid "Bad domain name at " msgstr "Mauvais nom de domaine à l'adresse" -#: ../canfestival/config_utils.py:341 -#: ../canfestival/config_utils.py:623 +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 #, python-format msgid "Bad location size : %s" msgstr "Mauvaise taille d'adresse : %s" -#: ../editors/DataTypeEditor.py:174 -#: ../editors/DataTypeEditor.py:204 -#: ../editors/DataTypeEditor.py:296 -#: ../dialogs/ArrayTypeDialog.py:55 +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 msgid "Base Type:" msgstr "Type de base :" -#: ../controls/VariablePanel.py:702 -#: ../editors/DataTypeEditor.py:624 +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 msgid "Base Types" msgstr "Types de base" -#: ../Beremiz.py:511 +#: ../BeremizIDE.py:455 msgid "Beremiz" msgstr "Beremiz" @@ -734,154 +705,173 @@ msgid "Bitwise inverting" msgstr "Inversion bit à bit" -#: ../editors/Viewer.py:464 +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 msgid "Block" msgstr "Block" -#: ../dialogs/FBDBlockDialog.py:38 +#: ../dialogs/FBDBlockDialog.py:60 msgid "Block Properties" msgstr "Propriétés du bloc" -#: ../editors/Viewer.py:433 +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Nom du bloc" + +#: ../editors/Viewer.py:550 msgid "Bottom" msgstr "Bas" -#: ../dialogs/BrowseValuesLibraryDialog.py:37 +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "Cassé" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 #, python-format msgid "Browse %s values library" msgstr "Explorer la liste des valeurs du paramètre '%s'" -#: ../dialogs/BrowseLocationsDialog.py:61 +#: ../dialogs/BrowseLocationsDialog.py:65 msgid "Browse Locations" msgstr "Naviger dans les adresses" -#: ../ProjectController.py:1519 +#: ../ProjectController.py:1832 msgid "Build" msgstr "Compiler" -#: ../ProjectController.py:1079 +#: ../ProjectController.py:1297 msgid "Build directory already clean\n" msgstr "Le répertoire de compilation est déjà nettoyé\n" -#: ../ProjectController.py:1520 +#: ../ProjectController.py:1833 msgid "Build project into build folder" msgstr "Compiler le projet dans le répertoire ce compilation" -#: ../ProjectController.py:937 +#: ../ProjectController.py:1080 msgid "C Build crashed !\n" msgstr "La compilation du C a mal fonctionné !\n" -#: ../ProjectController.py:934 +#: ../ProjectController.py:1077 msgid "C Build failed.\n" msgstr "La compilation du C a échouée !\n" -#: ../c_ext/CFileEditor.py:731 +#: ../c_ext/CFileEditor.py:63 msgid "C code" msgstr "Code C" -#: ../ProjectController.py:922 +#: ../ProjectController.py:1155 msgid "C code generated successfully.\n" msgstr "Code C généré avec succès.\n" -#: ../targets/toolchain_gcc.py:132 +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "La compilation a échouée\n" + +#: ../targets/toolchain_gcc.py:192 #, python-format msgid "C compilation of %s failed.\n" msgstr "La compilation C de %s a échouée.\n" -#: ../features.py:8 +#: ../features.py:32 msgid "C extension" msgstr "Extension C" -#: ../canfestival/NetworkEditor.py:29 +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "C&redits" + +#: ../canfestival/NetworkEditor.py:52 msgid "CANOpen network" msgstr "Réseau CANOpen" -#: ../canfestival/SlaveEditor.py:21 +#: ../canfestival/SlaveEditor.py:44 msgid "CANOpen slave" msgstr "Esclave CANOpen" -#: ../features.py:7 +#: ../features.py:31 msgid "CANopen support" msgstr "Support CANopen" -#: ../plcopen/plcopen.py:1732 -#: ../plcopen/plcopen.py:1746 -#: ../plcopen/plcopen.py:1767 -#: ../plcopen/plcopen.py:1783 +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 msgid "Can only generate execution order on FBD networks!" msgstr "L'ordre d'exécution ne peut être généré que dans les FBD !" -#: ../controls/VariablePanel.py:256 +#: ../controls/VariablePanel.py:267 msgid "Can only give a location to local or global variables" -msgstr "Une adresse ne peut être affecté qu'à des variables locales ou globales" - -#: ../PLCOpenEditor.py:336 +msgstr "" +"Une adresse ne peut être affecté qu'à des variables locales ou globales" + +#: ../PLCOpenEditor.py:344 #, python-format msgid "Can't generate program to file %s!" msgstr "Le programme n'a pu être généré dans le fichier \"%s\" !" -#: ../controls/VariablePanel.py:254 +#: ../controls/VariablePanel.py:265 msgid "Can't give a location to a function block instance" msgstr "Une adresse ne peut être affectée à une instance de Function Block" -#: ../PLCOpenEditor.py:376 +#: ../PLCOpenEditor.py:389 #, python-format msgid "Can't save project to file %s!" msgstr "Le projet n'a pu être sauvé dans le fichier \"%s\" !" -#: ../controls/VariablePanel.py:300 +#: ../controls/VariablePanel.py:313 msgid "Can't set an initial value to a function block instance" -msgstr "Une valeur initiale ne peut être affectée une instance de Function Block" - -#: ../ConfigTreeNode.py:490 -#, python-format -msgid "Cannot create child %s of type %s " -msgstr "Impossible d'ajouter un élément \"%s\" de type \"%s\"" - -#: ../ConfigTreeNode.py:417 +msgstr "" +"Une valeur initiale ne peut être affectée une instance de Function Block" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "Impossible de créer l'enfant {a1} de type {a2}" + +#: ../ConfigTreeNode.py:454 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "Impossible de trouver un numéro IEC inférieur à %d libre\n" -#: ../connectors/PYRO/__init__.py:92 +#: ../connectors/PYRO/__init__.py:131 msgid "Cannot get PLC status - connection failed.\n" msgstr "Impossible d'obtenir le statut de l'automate - la connexion a échoué.\n" -#: ../ProjectController.py:737 +#: ../ProjectController.py:943 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "Impossible d'ouvrir ou d'analyser le fichier VARIABLES.csv !\n" -#: ../canfestival/config_utils.py:371 -#, python-format -msgid "Cannot set bit offset for non bool '%s' variable (ID:%d,Idx:%x,sIdx:%x))" -msgstr "Impossible de définir un numéro de bit sur la variable '%s' non booléenne (ID:%d,Idx:%x,sIdx:%x)" - -#: ../dialogs/FindInPouDialog.py:81 -#: ../dialogs/SearchInProjectDialog.py:67 +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Impossible de configurer l'offset du bit de la variable non booléenne '{a1}'" +" (ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 msgid "Case sensitive" msgstr "Respecter la casse" -#: ../editors/Viewer.py:428 +#: ../editors/Viewer.py:545 msgid "Center" msgstr "Centre" -#: ../Beremiz_service.py:326 +#: ../Beremiz_service.py:268 msgid "Change IP of interface to bind" msgstr "Changer l'adresse IP de l'interface à lier" -#: ../Beremiz_service.py:325 +#: ../Beremiz_service.py:267 msgid "Change Name" msgstr "Changer le nom" -#: ../IDEFrame.py:1850 +#: ../IDEFrame.py:1946 msgid "Change POU Type To" msgstr "Changer le type du POU pour" -#: ../Beremiz_service.py:327 +#: ../Beremiz_service.py:269 msgid "Change Port Number" msgstr "Changer le numéro de port" -#: ../Beremiz_service.py:328 +#: ../Beremiz_service.py:270 msgid "Change working directory" msgstr "Changer le dossier de travail" @@ -889,111 +879,118 @@ msgid "Character string" msgstr "Chaîne de caractères" -#: ../svgui/svgui.py:92 +#: ../svgui/svgui.py:128 msgid "Choose a SVG file" msgstr "Choisissez un fichier SVG" -#: ../ProjectController.py:364 +#: ../ProjectController.py:542 msgid "Choose a directory to save project" msgstr "Choisissez un dossier où enregistrer le projet" -#: ../canfestival/canfestival.py:136 -#: ../PLCOpenEditor.py:294 -#: ../PLCOpenEditor.py:326 -#: ../PLCOpenEditor.py:370 +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 msgid "Choose a file" msgstr "Choisissez un fichier" -#: ../Beremiz.py:858 -#: ../Beremiz.py:893 +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 msgid "Choose a project" msgstr "Choisissez un projet" -#: ../dialogs/BrowseValuesLibraryDialog.py:42 +#: ../dialogs/BrowseValuesLibraryDialog.py:41 #, python-format msgid "Choose a value for %s:" msgstr "Choisissez une valeur pour le paramètre %s :" -#: ../Beremiz_service.py:378 +#: ../Beremiz_service.py:325 msgid "Choose a working directory " msgstr "Choisissez un dossier de travail" -#: ../ProjectController.py:288 +#: ../ProjectController.py:449 msgid "Chosen folder doesn't contain a program. It's not a valid project!" -msgstr "Le répertoire ne contient pas de programme. Ce n'est pas un projet valide !" - -#: ../ProjectController.py:255 +msgstr "" +"Le répertoire ne contient pas de programme. Ce n'est pas un projet valide !" + +#: ../ProjectController.py:416 msgid "Chosen folder isn't empty. You can't use it for a new project!" -msgstr "Le répertoire n'est pas vide. Vous ne pouvez pas l'utiliser pour créer un nouveau projet !" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +msgstr "" +"Le répertoire n'est pas vide. Vous ne pouvez pas l'utiliser pour créer un " +"nouveau projet !" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Class" msgstr "Classe" -#: ../controls/VariablePanel.py:371 +#: ../controls/VariablePanel.py:441 msgid "Class Filter:" msgstr "Filtre de classe :" -#: ../dialogs/FBDVariableDialog.py:62 +#: ../dialogs/FBDVariableDialog.py:70 msgid "Class:" msgstr "Classe :" -#: ../ProjectController.py:1523 +#: ../ProjectController.py:1836 msgid "Clean" msgstr "Nettoyer" -#: ../ProjectController.py:1525 +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Nettoyer les messages de log" + +#: ../ProjectController.py:1838 msgid "Clean project build folder" msgstr "Nettoyer le répertoire de compilation" -#: ../ProjectController.py:1076 +#: ../ProjectController.py:1294 msgid "Cleaning the build directory\n" msgstr "Répertoire de compilation en cours de nettoyage\n" -#: ../IDEFrame.py:416 +#: ../IDEFrame.py:435 msgid "Clear Errors" msgstr "Effacer les erreurs" -#: ../editors/Viewer.py:520 +#: ../editors/Viewer.py:641 msgid "Clear Execution Order" msgstr "Effacer l'ordre d'exécution" -#: ../editors/GraphicViewer.py:125 -msgid "Clear the graph values" -msgstr "Vider les valeurs du graphique" - -#: ../Beremiz.py:633 -#: ../PLCOpenEditor.py:202 +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Fermer" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 msgid "Close Application" msgstr "Fermer l'application" -#: ../IDEFrame.py:972 -#: ../Beremiz.py:321 -#: ../Beremiz.py:587 -#: ../PLCOpenEditor.py:112 +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 msgid "Close Project" msgstr "Fermer le projet" -#: ../Beremiz.py:319 -#: ../PLCOpenEditor.py:110 +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 msgid "Close Tab" msgstr "Fermer l'onglet" -#: ../editors/Viewer.py:480 +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 msgid "Coil" msgstr "Relai" -#: ../editors/Viewer.py:500 -#: ../editors/LDViewer.py:506 +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 msgid "Comment" msgstr "Commentaire" -#: ../controls/ProjectPropertiesPanel.py:94 +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Nom de l'entreprise" + +#: ../controls/ProjectPropertiesPanel.py:95 msgid "Company Name (required):" msgstr "Nom de l'entreprise (obligatoire) :" -#: ../controls/ProjectPropertiesPanel.py:95 +#: ../controls/ProjectPropertiesPanel.py:96 msgid "Company URL (optional):" msgstr "URL de l'entreprise (optionel) :" @@ -1001,7 +998,7 @@ msgid "Comparison" msgstr "Comparaison" -#: ../ProjectController.py:552 +#: ../ProjectController.py:734 msgid "Compiling IEC Program into C code...\n" msgstr "Compilation du program en IEC vers du code C en cours...\n" @@ -1009,90 +1006,93 @@ msgid "Concatenation" msgstr "Concaténation" -#: ../editors/ConfTreeNodeEditor.py:249 +#: ../editors/ConfTreeNodeEditor.py:230 msgid "Config" msgstr "Configuration" -#: ../editors/ProjectNodeEditor.py:13 +#: ../editors/ProjectNodeEditor.py:36 msgid "Config variables" msgstr "Variables de configuration" -#: ../dialogs/SearchInProjectDialog.py:47 +#: ../dialogs/SearchInProjectDialog.py:40 msgid "Configuration" msgstr "Configuration" -#: ../PLCControler.py:96 +#: ../PLCControler.py:99 msgid "Configurations" msgstr "Configurations" -#: ../ProjectController.py:1538 +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Confirmer ou changer de nom de variable" + +#: ../ProjectController.py:1851 msgid "Connect" msgstr "Connecter" -#: ../ProjectController.py:1539 +#: ../ProjectController.py:1852 msgid "Connect to the target PLC" msgstr "Connecter à l'automate cible" -#: ../ProjectController.py:1125 +#: ../ProjectController.py:1354 #, python-format msgid "Connected to URI: %s" msgstr "Connecté à l'URI : %s" -#: ../connectors/PYRO/__init__.py:40 -#, python-format -msgid "Connecting to URI : %s\n" -msgstr "Connection à l'URI %s en cours...\n" - -#: ../editors/Viewer.py:466 -#: ../dialogs/SFCTransitionDialog.py:76 +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 msgid "Connection" msgstr "Connexion" -#: ../dialogs/ConnectionDialog.py:37 +#: ../dialogs/ConnectionDialog.py:53 msgid "Connection Properties" msgstr "Propriétés de la connexion" -#: ../ProjectController.py:1397 +#: ../ProjectController.py:1709 msgid "Connection canceled!\n" msgstr "La connection a été abandonnée !\n" -#: ../ProjectController.py:1422 +#: ../ProjectController.py:1734 #, python-format msgid "Connection failed to %s!\n" msgstr "La connection à \"%s\" a échouée !\n" -#: ../connectors/PYRO/__init__.py:63 +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Connexion perdu\n" + +#: ../connectors/PYRO/__init__.py:102 #, python-format msgid "Connection to '%s' failed.\n" msgstr "La connexion à l'adresse '%s' a échouée.\n" -#: ../editors/Viewer.py:1426 -#: ../dialogs/ConnectionDialog.py:56 +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 msgid "Connector" msgstr "Connecteur" -#: ../dialogs/SFCStepDialog.py:58 +#: ../dialogs/SFCStepDialog.py:66 msgid "Connectors:" msgstr "Connecteurs :" -#: ../Beremiz.py:420 +#: ../BeremizIDE.py:350 msgid "Console" msgstr "Console" -#: ../controls/VariablePanel.py:65 +#: ../controls/VariablePanel.py:60 msgid "Constant" msgstr "Constante" -#: ../editors/Viewer.py:476 +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 msgid "Contact" msgstr "Contact" -#: ../controls/ProjectPropertiesPanel.py:197 +#: ../controls/ProjectPropertiesPanel.py:198 msgid "Content Description (optional):" msgstr "Description du contenu (optionel) :" -#: ../editors/Viewer.py:1427 -#: ../dialogs/ConnectionDialog.py:61 +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 msgid "Continuation" msgstr "Prolongement" @@ -1112,13 +1112,12 @@ msgid "Conversion to time-of-day" msgstr "Conversion en heure de la journée" -#: ../IDEFrame.py:353 -#: ../IDEFrame.py:406 -#: ../editors/Viewer.py:536 +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 msgid "Copy" msgstr "Copier" -#: ../IDEFrame.py:1837 +#: ../IDEFrame.py:1933 msgid "Copy POU" msgstr "Copier ce POU" @@ -1134,55 +1133,59 @@ msgid "Cosine" msgstr "Cosinus" -#: ../ConfigTreeNode.py:602 -#, python-format -msgid "" -"Could not add child \"%s\", type %s :\n" -"%s\n" -msgstr "" -"Impossible d'ajouter le noeud enfant \"%s\", de type %s :\n" -"%s\n" - -#: ../ConfigTreeNode.py:579 -#, python-format -msgid "" -"Couldn't load confnode base parameters %s :\n" -" %s" -msgstr "" -"Impossible de charger les paramètres de base du plugin %s :\n" -" %s" - -#: ../ConfigTreeNode.py:590 -#, python-format -msgid "" -"Couldn't load confnode parameters %s :\n" -" %s" -msgstr "" -"Impossible de charger les paramètres du plugin %s :\n" -" %s" - -#: ../PLCControler.py:819 -#: ../PLCControler.py:856 +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" +"Impossible d'ajouter l'enfant \"{a1}\", de type {a2}:\n" +"{a3}\n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Impossible d'importer l'ancien fichier %s" + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" +"Impossible de charger les parametres de base confnode {a1}:\n" +"{a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" +"Impossible de charger les parametres confnode {a1}:\n" +"{a2}" + +#: ../PLCControler.py:948 msgid "Couldn't paste non-POU object." msgstr "Impossible de coller autre chose qu'un POU." -#: ../ProjectController.py:1344 +#: ../ProjectController.py:1651 msgid "Couldn't start PLC !\n" msgstr "Impossible de démarrer l'automate !\n" -#: ../ProjectController.py:1352 +#: ../ProjectController.py:1659 msgid "Couldn't stop PLC !\n" msgstr "Impossible d'arrêter l'automate !\n" -#: ../ProjectController.py:1321 +#: ../ProjectController.py:1623 msgid "Couldn't stop debugger.\n" msgstr "Impossible d'arrêter le débogage de l'automate !\n" -#: ../svgui/svgui.py:22 +#: ../svgui/svgui.py:49 msgid "Create HMI" msgstr "Créer une IHM" -#: ../dialogs/PouDialog.py:43 +#: ../dialogs/PouDialog.py:46 msgid "Create a new POU" msgstr "Créer un nouveau POU" @@ -1190,84 +1193,79 @@ msgid "Create a new action" msgstr "Créer une nouvelle action" -#: ../IDEFrame.py:142 +#: ../IDEFrame.py:159 msgid "Create a new action block" msgstr "Créer un nouveau bloc d'actions" -#: ../IDEFrame.py:91 -#: ../IDEFrame.py:121 -#: ../IDEFrame.py:154 +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 msgid "Create a new block" msgstr "Créer un nouveau bloc" -#: ../IDEFrame.py:115 +#: ../IDEFrame.py:132 msgid "Create a new branch" msgstr "Créer une nouvelle branche" -#: ../IDEFrame.py:109 +#: ../IDEFrame.py:126 msgid "Create a new coil" msgstr "Créer un nouveau relai" -#: ../IDEFrame.py:85 -#: ../IDEFrame.py:100 -#: ../IDEFrame.py:130 +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 msgid "Create a new comment" msgstr "Créer un nouveau copmmentaire" -#: ../IDEFrame.py:94 -#: ../IDEFrame.py:124 -#: ../IDEFrame.py:157 +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 msgid "Create a new connection" msgstr "Créer une nouvelle connexion" -#: ../IDEFrame.py:112 -#: ../IDEFrame.py:163 +#: ../IDEFrame.py:129 ../IDEFrame.py:180 msgid "Create a new contact" msgstr "Créer un nouveau contact" -#: ../IDEFrame.py:145 +#: ../IDEFrame.py:162 msgid "Create a new divergence" msgstr "Créer une nouvelle divergence" -#: ../dialogs/SFCDivergenceDialog.py:36 +#: ../dialogs/SFCDivergenceDialog.py:53 msgid "Create a new divergence or convergence" msgstr "Créer une nouvelle divergence ou convergence" -#: ../IDEFrame.py:133 +#: ../IDEFrame.py:150 msgid "Create a new initial step" msgstr "Créer une nouvelle étape initiale" -#: ../IDEFrame.py:148 +#: ../IDEFrame.py:165 msgid "Create a new jump" msgstr "Créer un nouveau renvoi" -#: ../IDEFrame.py:103 -#: ../IDEFrame.py:160 +#: ../IDEFrame.py:120 ../IDEFrame.py:177 msgid "Create a new power rail" msgstr "Créer une nouvelle barre d'alimentation" -#: ../IDEFrame.py:106 +#: ../IDEFrame.py:123 msgid "Create a new rung" msgstr "Créer un nouvel échelon" -#: ../IDEFrame.py:136 +#: ../IDEFrame.py:153 msgid "Create a new step" msgstr "Créer une nouvelle étape" -#: ../IDEFrame.py:139 -#: ../dialogs/PouTransitionDialog.py:42 +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 msgid "Create a new transition" msgstr "Créer une nouvelle transition" -#: ../IDEFrame.py:88 -#: ../IDEFrame.py:118 -#: ../IDEFrame.py:151 +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 msgid "Create a new variable" msgstr "Créer une nouvelle variable" -#: ../IDEFrame.py:351 -#: ../IDEFrame.py:405 -#: ../editors/Viewer.py:535 +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Credits" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Répertoire de travail actuel :" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 msgid "Cut" msgstr "Couper" @@ -1275,33 +1273,27 @@ msgid "Cyclic" msgstr "Périodique" -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 #: ../plcopen/iec_std.csv:60 msgid "DEPRECATED" msgstr "OBSOLETE" -#: ../canfestival/SlaveEditor.py:53 -#: ../canfestival/NetworkEditor.py:74 +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 msgid "DS-301 Profile" msgstr "Profil DS-301" -#: ../canfestival/SlaveEditor.py:54 -#: ../canfestival/NetworkEditor.py:75 +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 msgid "DS-302 Profile" msgstr "Profil DS-302" -#: ../dialogs/SearchInProjectDialog.py:43 +#: ../dialogs/SearchInProjectDialog.py:36 msgid "Data Type" msgstr "Type de donnée" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Data Types" msgstr "Types de données" @@ -1309,74 +1301,69 @@ msgid "Data type conversion" msgstr "Conversion entre types de donnée" -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:45 +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 msgid "Date addition" msgstr "Addition de dates" -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:57 -#: ../plcopen/iec_std.csv:58 -#: ../plcopen/iec_std.csv:59 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 msgid "Date and time subtraction" msgstr "Soustraction entre horodatage" -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:51 +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 msgid "Date subtraction" msgstr "Soustraction de date" -#: ../dialogs/DurationEditorDialog.py:43 +#: ../dialogs/DurationEditorDialog.py:44 msgid "Days:" msgstr "Jours :" -#: ../ProjectController.py:1444 +#: ../ProjectController.py:1756 msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" -msgstr "Les informations de débogage ne correspond pas l'automate connecté - Arrêter/transférez/démarrer pour pouvoir débogguer.\n" - -#: ../controls/PouInstanceVariablesPanel.py:59 +msgstr "" +"Les informations de débogage ne correspond pas l'automate connecté - " +"Arrêter/transférez/démarrer pour pouvoir débogguer.\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 msgid "Debug instance" msgstr "Déboguer l'instance" -#: ../editors/Viewer.py:1016 -#: ../editors/Viewer.py:3326 +#: ../editors/Viewer.py:448 #, python-format msgid "Debug: %s" msgstr "Débogage : %s" -#: ../ProjectController.py:1153 +#: ../ProjectController.py:1412 #, python-format msgid "Debug: Unknown variable '%s'\n" msgstr "Débogage : Variable '%s' inconnue\n" -#: ../ProjectController.py:1151 +#: ../ProjectController.py:1410 #, python-format msgid "Debug: Unsupported type to debug '%s'\n" msgstr "Débogage : Type non supporté dans le débogage '%'\n" -#: ../IDEFrame.py:612 +#: ../IDEFrame.py:639 msgid "Debugger" msgstr "Déboggueur" -#: ../ProjectController.py:1311 +#: ../ProjectController.py:1592 msgid "Debugger disabled\n" msgstr "Débogueur désactivé\n" -#: ../ProjectController.py:1441 +#: ../ProjectController.py:1753 msgid "Debugger ready\n" msgstr "Débogueur \n" -#: ../ProjectController.py:1323 +#: ../ProjectController.py:1625 msgid "Debugger stopped.\n" msgstr "Débogueur désactivé\n" -#: ../IDEFrame.py:1866 -#: ../Beremiz.py:991 -#: ../editors/Viewer.py:511 +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 msgid "Delete" msgstr "Supprimer" -#: ../editors/Viewer.py:453 +#: ../editors/Viewer.py:573 msgid "Delete Divergence Branch" msgstr "Supprimer une branche de divergence" @@ -1384,7 +1371,7 @@ msgid "Delete File" msgstr "Supprimer un fichier" -#: ../editors/Viewer.py:442 +#: ../editors/Viewer.py:560 msgid "Delete Wire Segment" msgstr "Supprimer un segment de fil" @@ -1396,32 +1383,27 @@ msgid "Deletion (within)" msgstr "Suppression (au milieu)" -#: ../editors/DataTypeEditor.py:152 +#: ../editors/DataTypeEditor.py:153 msgid "Derivation Type:" msgstr "Type de dérivation :" -#: ../plcopen/structures.py:263 -msgid "" -"Derivative\n" -"The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." -msgstr "" -"Dérivée\n" -"Le Function Block derivative produit une sortie XOUT proportionnelle au rapport de changement de l'entrée XIN." - -#: ../controls/VariablePanel.py:362 +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "" + +#: ../controls/VariablePanel.py:432 msgid "Description:" msgstr "Description :" -#: ../editors/DataTypeEditor.py:320 -#: ../dialogs/ArrayTypeDialog.py:61 +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 msgid "Dimensions:" msgstr "Dimensions :" -#: ../dialogs/FindInPouDialog.py:61 +#: ../dialogs/FindInPouDialog.py:66 msgid "Direction" msgstr "Direction" -#: ../dialogs/BrowseLocationsDialog.py:86 +#: ../dialogs/BrowseLocationsDialog.py:91 msgid "Direction:" msgstr "Direction :" @@ -1429,15 +1411,19 @@ msgid "Directly" msgstr "Direct" -#: ../ProjectController.py:1547 +#: ../ProjectController.py:1860 msgid "Disconnect" msgstr "Déconnecter" -#: ../ProjectController.py:1549 +#: ../ProjectController.py:1862 msgid "Disconnect from PLC" msgstr "Déconnecter l'automate" -#: ../editors/Viewer.py:495 +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "Déconnecté" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 msgid "Divergence" msgstr "Divergence" @@ -1450,40 +1436,31 @@ msgid "Do you really want to delete the file '%s'?" msgstr "Êtes-vous sûr de vouloir supprimer le fichier '%s' ?" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Documentation" msgstr "Documentation" -#: ../PLCOpenEditor.py:330 +#: ../PLCOpenEditor.py:338 msgid "Done" msgstr "Terminé" -#: ../plcopen/structures.py:226 -msgid "" -"Down-counter\n" -"The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." -msgstr "" -"Compteur décrémental\n" -"Le compteur décrémental peut être utilisé pour signaler lorsque le compteur atteint zéro en partant d'une valeur prédéfinie." - -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Duration" msgstr "Durée" -#: ../canfestival/canfestival.py:139 +#: ../canfestival/canfestival.py:165 msgid "EDS files (*.eds)|*.eds|All files|*.*" msgstr "Fichiers EDS (*.eds)|*.eds|Tous les fichiers|*.*" -#: ../editors/Viewer.py:509 +#: ../editors/Viewer.py:629 msgid "Edit Block" msgstr "Editer le block" -#: ../dialogs/LDElementDialog.py:41 +#: ../dialogs/LDElementDialog.py:56 msgid "Edit Coil Values" msgstr "Editer les valeurs du relai" -#: ../dialogs/LDElementDialog.py:38 +#: ../dialogs/LDElementDialog.py:54 msgid "Edit Contact Values" msgstr "Editer les valeurs du contact" @@ -1491,26 +1468,23 @@ msgid "Edit Duration" msgstr "Editer une durée" -#: ../dialogs/SFCStepDialog.py:35 +#: ../dialogs/SFCStepDialog.py:51 msgid "Edit Step" msgstr "Editer l'étape" -#: ../wxglade_hmi/wxglade_hmi.py:12 +#: ../wxglade_hmi/wxglade_hmi.py:38 msgid "Edit a WxWidgets GUI with WXGlade" msgstr "Editer une IHM WxWidgets à l'aide de WXGlade" -#: ../dialogs/ActionBlockDialog.py:122 +#: ../dialogs/ActionBlockDialog.py:121 msgid "Edit action block properties" msgstr "Editer les propriétés du block d'actions" -#: ../dialogs/ArrayTypeDialog.py:45 +#: ../dialogs/ArrayTypeDialog.py:44 msgid "Edit array type properties" msgstr "Editer les propriétés d'un type de données tableau" -#: ../editors/Viewer.py:2186 -#: ../editors/Viewer.py:2188 -#: ../editors/Viewer.py:2706 -#: ../editors/Viewer.py:2708 +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 msgid "Edit comment" msgstr "Editer le commentaire" @@ -1522,47 +1496,51 @@ msgid "Edit item" msgstr "Editer l'élément" -#: ../editors/Viewer.py:2670 +#: ../editors/Viewer.py:3014 msgid "Edit jump target" msgstr "Editer la cible du renvoi" -#: ../ProjectController.py:1561 +#: ../ProjectController.py:1874 msgid "Edit raw IEC code added to code generated by PLCGenerator" msgstr "Editer le code IEC ajouté au code généré par PLCGenerator" -#: ../editors/SFCViewer.py:725 +#: ../editors/SFCViewer.py:799 msgid "Edit step name" msgstr "Editer le nom de l'étape" -#: ../dialogs/SFCTransitionDialog.py:38 +#: ../dialogs/SFCTransitionDialog.py:52 msgid "Edit transition" msgstr "Editer la transition" -#: ../IDEFrame.py:584 +#: ../IDEFrame.py:611 msgid "Editor ToolBar" msgstr "Barre d'outils d'édition" -#: ../ProjectController.py:1039 +#: ../ProjectController.py:1257 msgid "Editor selection" msgstr "Selection d'un éditeur" -#: ../editors/DataTypeEditor.py:347 +#: ../editors/DataTypeEditor.py:348 msgid "Elements :" msgstr "Eléments :" -#: ../IDEFrame.py:348 +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "Vide" + +#: ../IDEFrame.py:365 msgid "Enable Undo/Redo" msgstr "Activer Défaire/Refaire" -#: ../Beremiz_service.py:385 +#: ../Beremiz_service.py:333 msgid "Enter a name " msgstr "Saisissez un nom" -#: ../Beremiz_service.py:370 +#: ../Beremiz_service.py:318 msgid "Enter a port number " msgstr "Saisissez un numéro de port" -#: ../Beremiz_service.py:360 +#: ../Beremiz_service.py:309 msgid "Enter the IP of the interface to bind" msgstr "Saisissez l'adresse IP de l'interface à lier" @@ -1574,67 +1552,50 @@ msgid "Equal to" msgstr "Egal à" -#: ../Beremiz_service.py:271 -#: ../controls/VariablePanel.py:332 -#: ../controls/VariablePanel.py:681 -#: ../controls/DebugVariablePanel.py:379 -#: ../IDEFrame.py:966 -#: ../IDEFrame.py:1553 -#: ../IDEFrame.py:1590 -#: ../IDEFrame.py:1595 -#: ../IDEFrame.py:1609 -#: ../IDEFrame.py:1614 -#: ../IDEFrame.py:2290 -#: ../Beremiz.py:1131 -#: ../PLCOpenEditor.py:337 -#: ../PLCOpenEditor.py:342 -#: ../PLCOpenEditor.py:416 -#: ../PLCOpenEditor.py:426 -#: ../editors/TextViewer.py:369 -#: ../editors/DataTypeEditor.py:549 -#: ../editors/DataTypeEditor.py:554 -#: ../editors/DataTypeEditor.py:578 -#: ../editors/DataTypeEditor.py:583 -#: ../editors/DataTypeEditor.py:593 -#: ../editors/DataTypeEditor.py:744 -#: ../editors/DataTypeEditor.py:751 -#: ../editors/Viewer.py:365 -#: ../editors/LDViewer.py:666 -#: ../editors/LDViewer.py:882 -#: ../editors/LDViewer.py:886 -#: ../ProjectController.py:225 -#: ../dialogs/PouNameDialog.py:53 -#: ../dialogs/PouTransitionDialog.py:107 -#: ../dialogs/BrowseLocationsDialog.py:212 -#: ../dialogs/ProjectDialog.py:71 -#: ../dialogs/SFCStepNameDialog.py:59 -#: ../dialogs/ConnectionDialog.py:159 -#: ../dialogs/FBDVariableDialog.py:201 -#: ../dialogs/PouActionDialog.py:104 +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 #: ../dialogs/BrowseValuesLibraryDialog.py:83 -#: ../dialogs/PouDialog.py:132 -#: ../dialogs/SFCTransitionDialog.py:147 -#: ../dialogs/DurationEditorDialog.py:121 -#: ../dialogs/DurationEditorDialog.py:163 -#: ../dialogs/SearchInProjectDialog.py:157 -#: ../dialogs/SFCStepDialog.py:130 -#: ../dialogs/ArrayTypeDialog.py:97 -#: ../dialogs/ArrayTypeDialog.py:103 -#: ../dialogs/FBDBlockDialog.py:164 -#: ../dialogs/ForceVariableDialog.py:179 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 msgid "Error" msgstr "Erreur" -#: ../ProjectController.py:601 -msgid "Error : At least one configuration and one resource must be declared in PLC !\n" -msgstr "Erreur : Au moins une configuration ou une ressource doit être déclarée dans l'automate !\n" - -#: ../ProjectController.py:593 +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" +"Erreur : Au moins une configuration ou une ressource doit être déclarée dans" +" l'automate !\n" + +#: ../ProjectController.py:781 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "Erreur : Le compilateur d'IEC en C a retourné %d\n" -#: ../ProjectController.py:534 +#: ../ProjectController.py:712 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" @@ -1643,38 +1604,37 @@ "Erreur dans le générateur de code ST/IL/SFC :\n" "%s\n" -#: ../ConfigTreeNode.py:183 +#: ../ConfigTreeNode.py:216 #, python-format msgid "Error while saving \"%s\"\n" msgstr "Erreur lors de l'enregistrement de \"%s\"\n" -#: ../canfestival/canfestival.py:144 +#: ../canfestival/canfestival.py:170 msgid "Error: Export slave failed\n" msgstr "Erreur : L'export de l'esclave a échoué\n" -#: ../canfestival/canfestival.py:345 +#: ../canfestival/canfestival.py:371 msgid "Error: No Master generated\n" msgstr "Erreur : Aucun maître généré\n" -#: ../canfestival/canfestival.py:340 +#: ../canfestival/canfestival.py:366 msgid "Error: No PLC built\n" msgstr "Erreur : Aucun automate compilé\n" -#: ../ProjectController.py:1416 +#: ../ProjectController.py:1728 #, python-format msgid "Exception while connecting %s!\n" msgstr "Une exception est apparu au cours de la connexion %s !\n" -#: ../dialogs/FBDBlockDialog.py:95 +#: ../dialogs/FBDBlockDialog.py:120 msgid "Execution Control:" msgstr "Contrôle d'exécution :" -#: ../dialogs/FBDVariableDialog.py:76 -#: ../dialogs/FBDBlockDialog.py:87 +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 msgid "Execution Order:" msgstr "Ordre d'exécution :" -#: ../features.py:11 +#: ../features.py:35 msgid "Experimental web based HMI" msgstr "IHM expérimentale utilisant les technologies web" @@ -1686,80 +1646,77 @@ msgid "Exponentiation" msgstr "Exponentiel" -#: ../canfestival/canfestival.py:150 +#: ../canfestival/canfestival.py:176 msgid "Export CanOpen slave to EDS file" msgstr "Exporter un esclave CANopen sous la forme d'un fichier EDS" -#: ../controls/DebugVariablePanel.py:1472 -#: ../editors/GraphicViewer.py:144 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 msgid "Export graph values to clipboard" msgstr "Exporter les valeurs du graphique vers le presse-papier" -#: ../canfestival/canfestival.py:149 +#: ../canfestival/canfestival.py:175 msgid "Export slave" msgstr "Exporter un esclave" -#: ../dialogs/FBDVariableDialog.py:69 +#: ../dialogs/FBDVariableDialog.py:90 msgid "Expression:" msgstr "Expression :" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid "External" msgstr "Externe" -#: ../ProjectController.py:605 +#: ../ProjectController.py:802 msgid "Extracting Located Variables...\n" msgstr "Extraction des variables adressées en cours...\n" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "FBD" msgstr "FBD" -#: ../ProjectController.py:1480 +#: ../ProjectController.py:1791 msgid "Failed : Must build before transfer.\n" msgstr "Echec : Le projet doit être compilé avant d'être transféré.\n" -#: ../editors/Viewer.py:404 -#: ../dialogs/LDElementDialog.py:84 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 msgid "Falling Edge" msgstr "Front descendant" -#: ../plcopen/structures.py:216 -msgid "" -"Falling edge detector\n" -"The output produces a single pulse when a falling edge is detected." -msgstr "" -"Détecteur de front descendant\n" -"La sortie produit une impulsion unique lorsqu'un front descendant est détecté." - -#: ../ProjectController.py:927 +#: ../ProjectController.py:1070 msgid "Fatal : cannot get builder.\n" msgstr "Erreur fatale : impossible de trouver un compilateur.\n" -#: ../dialogs/DurationEditorDialog.py:160 +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "Recherche %s" + +#: ../dialogs/DurationEditorDialog.py:164 #, python-format msgid "Field %s hasn't a valid value!" msgstr "Le champ %s n'a pas une valeur valide !" -#: ../dialogs/DurationEditorDialog.py:162 +#: ../dialogs/DurationEditorDialog.py:166 #, python-format msgid "Fields %s haven't a valid value!" msgstr "Les champs %s n'ont pas une valeur valide !" -#: ../IDEFrame.py:358 -#: ../dialogs/FindInPouDialog.py:30 -#: ../dialogs/FindInPouDialog.py:99 +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "Le fichier '%s' existe déjà !" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 msgid "Find" msgstr "Rechercher" -#: ../IDEFrame.py:360 +#: ../IDEFrame.py:377 msgid "Find Next" msgstr "Recherche suivante" -#: ../IDEFrame.py:362 +#: ../IDEFrame.py:379 msgid "Find Previous" msgstr "Recherche précédente" @@ -1767,16 +1724,15 @@ msgid "Find position" msgstr "Trouver la position" -#: ../dialogs/FindInPouDialog.py:51 +#: ../dialogs/FindInPouDialog.py:55 msgid "Find:" msgstr "Rechercher :" -#: ../connectors/PYRO/__init__.py:125 +#: ../connectors/PYRO/__init__.py:163 msgid "Force runtime reload\n" msgstr "Redémarrage du runtime forcé\n" -#: ../controls/DebugVariablePanel.py:1934 -#: ../editors/Viewer.py:1385 +#: ../editors/Viewer.py:1600 msgid "Force value" msgstr "Forcer la valeur" @@ -1784,84 +1740,78 @@ msgid "Forcing Variable Value" msgstr "Forcer la valeur de la variable" -#: ../dialogs/PouTransitionDialog.py:97 -#: ../dialogs/ProjectDialog.py:70 -#: ../dialogs/PouActionDialog.py:94 -#: ../dialogs/PouDialog.py:114 -#: ../dialogs/SFCTransitionDialog.py:147 +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 #, python-format msgid "Form isn't complete. %s must be filled!" msgstr "Le formulaire est incomplet. %s doit être complété !" -#: ../dialogs/ConnectionDialog.py:149 -#: ../dialogs/FBDBlockDialog.py:154 +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 msgid "Form isn't complete. Name must be filled!" msgstr "Le formulaire est incomplet. Le nom doit être complété !" -#: ../dialogs/SearchInProjectDialog.py:145 -msgid "Form isn't complete. Pattern to search must be filled!" -msgstr "Le formulaire est incomplet. Le modèle à chercher doit être complété !" - -#: ../dialogs/FBDBlockDialog.py:152 +#: ../dialogs/FBDBlockDialog.py:232 msgid "Form isn't complete. Valid block type must be selected!" -msgstr "Le formulaire est incomplet. Un type de bloc valide doit être sélectionné !" - -#: ../dialogs/FindInPouDialog.py:67 +msgstr "" +"Le formulaire est incomplet. Un type de bloc valide doit être sélectionné !" + +#: ../dialogs/FindInPouDialog.py:72 msgid "Forward" msgstr "Vers le bas" -#: ../dialogs/SearchInProjectDialog.py:44 +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 msgid "Function" msgstr "Fonction" -#: ../IDEFrame.py:334 +#: ../IDEFrame.py:349 msgid "Function &Block" msgstr "&Bloc Fonctionnel" -#: ../IDEFrame.py:1845 -#: ../dialogs/SearchInProjectDialog.py:45 +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 msgid "Function Block" msgstr "Bloc fonctionnel" -#: ../controls/VariablePanel.py:744 +#: ../controls/VariablePanel.py:854 msgid "Function Block Types" msgstr "Types de blocs fonctionnels" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "Function Blocks" msgstr "Blocs fonctionnels" -#: ../editors/Viewer.py:236 +#: ../editors/Viewer.py:249 msgid "Function Blocks can't be used in Functions!" msgstr "Les blocs fonctionnels ne peuvent être utilisés dans des functions !" -#: ../PLCControler.py:2180 +#: ../PLCControler.py:2343 #, python-format msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" msgstr "Le bloc fonctionnel \"%s\" ne peuvent être collés dans une function !" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "Functions" msgstr "Fonctions" -#: ../PLCOpenEditor.py:119 +#: ../PLCOpenEditor.py:117 msgid "Generate Program" msgstr "Générer le program" -#: ../ProjectController.py:524 +#: ../ProjectController.py:703 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "Création du code ST/IL/SFC de l'automate IEC-61131 en cours...\n" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid "Global" msgstr "Globale" -#: ../controls/DebugVariablePanel.py:1471 -#: ../editors/GraphicViewer.py:131 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 msgid "Go to current value" msgstr "Aller à la valeur actuelle" -#: ../controls/ProjectPropertiesPanel.py:173 +#: ../controls/ProjectPropertiesPanel.py:174 msgid "Graphics" msgstr "Graphiques" @@ -1873,11 +1823,15 @@ msgid "Greater than or equal to" msgstr "Supérieur ou égal à" -#: ../controls/ProjectPropertiesPanel.py:134 +#: ../controls/ProjectPropertiesPanel.py:135 msgid "Grid Resolution:" msgstr "Résolution de la grille :" -#: ../controls/ProjectPropertiesPanel.py:120 +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "Port d'interface HTTP:" + +#: ../controls/ProjectPropertiesPanel.py:121 msgid "Height:" msgstr "Hauteur :" @@ -1885,107 +1839,94 @@ msgid "Home Directory:" msgstr "Répertoire utilisateur :" -#: ../controls/ProjectPropertiesPanel.py:150 +#: ../controls/ProjectPropertiesPanel.py:151 msgid "Horizontal:" msgstr "Horizontal :" -#: ../dialogs/DurationEditorDialog.py:44 +#: ../dialogs/DurationEditorDialog.py:45 msgid "Hours:" msgstr "Heures :" -#: ../plcopen/structures.py:278 -msgid "" -"Hysteresis\n" -"The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." -msgstr "" -"Hystérésis\n" -"Le bloc functionnel hystérésis fourni un booléen en sortie suivant une courbe d'hystérésis entre les deux entrées réelles (REAL) XIN1 et XIN2." - -#: ../ProjectController.py:851 -msgid "IEC-61131-3 code generation failed !\n" -msgstr "La création du code IEC-61131-3 a échouée !\n" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "IL" msgstr "IL" -#: ../Beremiz_service.py:361 -#: ../Beremiz_service.py:362 +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "IP" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 msgid "IP is not valid!" msgstr "l'IP est invalide !" -#: ../svgui/svgui.py:17 -#: ../svgui/svgui.py:18 +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 msgid "Import SVG" msgstr "Importer un SVG" -#: ../controls/VariablePanel.py:76 -#: ../editors/Viewer.py:1412 -#: ../dialogs/FBDVariableDialog.py:34 +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 msgid "InOut" msgstr "Entrée-Sortie" -#: ../editors/Viewer.py:999 +#: ../editors/Viewer.py:431 msgid "Inactive" msgstr "Inactif" -#: ../controls/VariablePanel.py:265 -#, python-format -msgid "Incompatible data types between \"%s\" and \"%s\"" -msgstr "Types de donnée imcompatible entre \"%s\" et \"%s\"" - -#: ../controls/VariablePanel.py:274 -#, python-format -msgid "Incompatible size of data between \"%s\" and \"%s\"" -msgstr "Taille de donnée incompatible entre \"%s\" et \"%s\"" - -#: ../controls/VariablePanel.py:270 +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "Type de données incompatible entre \"{a1}\" et \"{a2}\"" + +#: ../controls/VariablePanel.py:282 #, python-format msgid "Incompatible size of data between \"%s\" and \"BOOL\"" msgstr "Taille de donnée incompatible entre \"%s\" et \"BOOL\"" -#: ../dialogs/ActionBlockDialog.py:37 +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "Taille de données incompatible entre \"{a1}\" et \"{a2}\"" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Indicator" msgstr "Indicateur" -#: ../editors/Viewer.py:491 +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:611 msgid "Initial Step" msgstr "Étape initiale" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:50 +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 msgid "Initial Value" msgstr "Valeur initiale" -#: ../editors/DataTypeEditor.py:184 -#: ../editors/DataTypeEditor.py:215 -#: ../editors/DataTypeEditor.py:271 -#: ../editors/DataTypeEditor.py:309 +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 msgid "Initial Value:" msgstr "Valeur initiale :" -#: ../svgui/svgui.py:21 +#: ../svgui/svgui.py:48 msgid "Inkscape" msgstr "Inkscape" -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCTransitionDialog.py:66 -#: ../dialogs/SFCTransitionDialog.py:137 +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 msgid "Inline" msgstr "Inline" -#: ../controls/VariablePanel.py:76 -#: ../editors/Viewer.py:1410 -#: ../dialogs/BrowseLocationsDialog.py:35 -#: ../dialogs/FBDVariableDialog.py:33 -#: ../dialogs/SFCStepDialog.py:61 +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Input" msgstr "Entrée" -#: ../dialogs/FBDBlockDialog.py:78 +#: ../dialogs/FBDBlockDialog.py:96 msgid "Inputs:" msgstr "Entrées :" @@ -1993,24 +1934,16 @@ msgid "Insertion (into)" msgstr "Insertion (au milieu)" -#: ../plcopen/plcopen.py:1843 +#: ../plcopen/plcopen.py:1696 #, python-format msgid "Instance with id %d doesn't exist!" msgstr "L'instance dont l'id est %d n'existe pas !" -#: ../editors/ResourceEditor.py:255 +#: ../editors/ResourceEditor.py:264 msgid "Instances:" msgstr "Instances :" -#: ../plcopen/structures.py:258 -msgid "" -"Integral\n" -"The integral function block integrates the value of input XIN over time." -msgstr "" -"Intégrale\n" -"Le bloc fonctionnel INTEGRAL intègre les valeurs de l'entrée XIN en fonction du temps." - -#: ../controls/VariablePanel.py:75 +#: ../controls/VariablePanel.py:70 msgid "Interface" msgstr "Interface" @@ -2022,40 +1955,42 @@ msgid "Interval" msgstr "Interval" -#: ../PLCControler.py:2157 -#: ../PLCControler.py:2195 +#: ../PLCControler.py:2331 msgid "Invalid plcopen element(s)!!!" msgstr "Les éléments plcopen ne sont pas valides !!! " -#: ../canfestival/config_utils.py:376 -#: ../canfestival/config_utils.py:637 -#, python-format -msgid "Invalid type \"%s\"-> %d != %d for location\"%s\"" -msgstr "Type invalide \"%s\"-> %d != %d pour cette adresse \"%s\"" - -#: ../dialogs/ForceVariableDialog.py:177 -#, python-format -msgid "Invalid value \"%s\" for \"%s\" variable!" -msgstr "Valeur \"%s\" invalide pour une variable de type \"%s\" !" - -#: ../controls/DebugVariablePanel.py:319 -#: ../controls/DebugVariablePanel.py:322 +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "Type invalide \"{a1}\"-> {a2} != {a3} pour \"{a4}\"" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "Type invalide \"{a1}\"-> {a2} != {a3} pour \"{a4}\"" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 #, python-format msgid "Invalid value \"%s\" for debug variable" msgstr "Chemin de variable à déboguer \"%s\" invalide" -#: ../controls/VariablePanel.py:244 -#: ../controls/VariablePanel.py:247 +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 #, python-format msgid "Invalid value \"%s\" for variable grid element" msgstr "Valeur \"%s\" invalide pour un élément de la grille de variables" -#: ../editors/Viewer.py:221 -#: ../editors/Viewer.py:224 +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 #, python-format msgid "Invalid value \"%s\" for viewer block" msgstr "Valeur \"%s\" invalide pour un élément graphique" +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "Valeur invalide \"{a1}\" pour la variable \"{a2}\"" + #: ../dialogs/DurationEditorDialog.py:121 msgid "" "Invalid value!\n" @@ -2064,56 +1999,51 @@ "Valeur invalide !\n" "Vous devez rentrer une valeur numérique." -#: ../editors/Viewer.py:496 +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 msgid "Jump" msgstr "Renvoi" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "LD" msgstr "LD" -#: ../editors/LDViewer.py:215 -#: ../editors/LDViewer.py:231 +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 #, python-format msgid "Ladder element with id %d is on more than one rung." msgstr "L'élément de LD dont l'id est %d apparait dans plusieurs échelons. " -#: ../dialogs/PouTransitionDialog.py:86 -#: ../dialogs/PouActionDialog.py:83 -#: ../dialogs/PouDialog.py:102 +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 msgid "Language" msgstr "Langue" -#: ../controls/ProjectPropertiesPanel.py:186 +#: ../controls/ProjectPropertiesPanel.py:187 msgid "Language (optional):" msgstr "Langue (optionnel) :" -#: ../dialogs/PouTransitionDialog.py:60 -#: ../dialogs/PouActionDialog.py:56 -#: ../dialogs/PouDialog.py:71 +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 msgid "Language:" msgstr "Langue :" -#: ../ProjectController.py:1486 +#: ../ProjectController.py:1797 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "La dernière compilation correspond à la cible actuelle...\n" -#: ../Beremiz_service.py:331 +#: ../Beremiz_service.py:273 msgid "Launch WX GUI inspector" msgstr "Lancer un inspecteur d'IHM WX" -#: ../Beremiz_service.py:330 +#: ../Beremiz_service.py:272 msgid "Launch a live Python shell" msgstr "Lancer une console Python" -#: ../editors/Viewer.py:427 +#: ../editors/Viewer.py:544 msgid "Left" msgstr "Gauche" -#: ../dialogs/LDPowerRailDialog.py:55 +#: ../dialogs/LDPowerRailDialog.py:63 msgid "Left PowerRail" msgstr "Barre d'alimentation à gauche" @@ -2129,36 +2059,39 @@ msgid "Less than or equal to" msgstr "Inférieur ou égal à" -#: ../IDEFrame.py:604 +#: ../IDEFrame.py:631 msgid "Library" msgstr "Librairie" +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "Licence" + #: ../plcopen/iec_std.csv:73 msgid "Limitation" msgstr "Limitation" -#: ../targets/toolchain_gcc.py:142 +#: ../targets/toolchain_gcc.py:202 msgid "Linking :\n" msgstr "Linkage :\n" -#: ../controls/VariablePanel.py:77 -#: ../dialogs/DiscoveryDialog.py:110 +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 msgid "Local" msgstr "Locale" -#: ../canfestival/canfestival.py:322 +#: ../canfestival/canfestival.py:348 msgid "Local entries" msgstr "Entrées locales" -#: ../ProjectController.py:1391 +#: ../ProjectController.py:1703 msgid "Local service discovery failed!\n" msgstr "Echec de la sélection d'un service!\n" -#: ../controls/VariablePanel.py:58 +#: ../controls/VariablePanel.py:53 msgid "Location" msgstr "Adresse" -#: ../dialogs/BrowseLocationsDialog.py:68 +#: ../dialogs/BrowseLocationsDialog.py:72 msgid "Locations available:" msgstr "Adresses disponibles :" @@ -2166,54 +2099,56 @@ msgid "Logarithm to base 10" msgstr "Logarithme de base 10" -#: ../connectors/PYRO/__init__.py:55 +#: ../connectors/PYRO/__init__.py:94 #, python-format msgid "MDNS resolution failure for '%s'\n" msgstr "Echec de la résolution MDNS pour '%s'\n" -#: ../canfestival/SlaveEditor.py:41 -#: ../canfestival/NetworkEditor.py:62 +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 msgid "Map Variable" msgstr "Variable mappable" -#: ../features.py:7 +#: ../features.py:31 msgid "Map located variables over CANopen" msgstr "Mappe des variables localisées sur un bus CANopen" -#: ../canfestival/NetworkEditor.py:83 +#: ../canfestival/NetworkEditor.py:106 msgid "Master" msgstr "Maître" -#: ../ConfigTreeNode.py:500 -#, python-format -msgid "Max count (%d) reached for this confnode of type %s " -msgstr "Nombre limite(%d) atteint pour les plugin de type %s" +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" +"Valeur maximal du compteur atteinte ({a1}) pour ce confnode de type {a2}" #: ../plcopen/iec_std.csv:71 msgid "Maximum" msgstr "Maximum" -#: ../editors/DataTypeEditor.py:238 +#: ../editors/DataTypeEditor.py:239 msgid "Maximum:" msgstr "Maximum :" -#: ../dialogs/BrowseLocationsDialog.py:37 +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Memory" msgstr "Mémoire" -#: ../IDEFrame.py:572 +#: ../IDEFrame.py:599 msgid "Menu ToolBar" msgstr "Barre d'outils du menu principal" -#: ../dialogs/DurationEditorDialog.py:48 +#: ../dialogs/DurationEditorDialog.py:49 msgid "Microseconds:" msgstr "Microsecondes :" -#: ../editors/Viewer.py:432 +#: ../editors/Viewer.py:549 msgid "Middle" msgstr "Milieu" -#: ../dialogs/DurationEditorDialog.py:47 +#: ../dialogs/DurationEditorDialog.py:48 msgid "Milliseconds:" msgstr "Millisecondes :" @@ -2221,76 +2156,68 @@ msgid "Minimum" msgstr "Minimum" -#: ../editors/DataTypeEditor.py:225 +#: ../editors/DataTypeEditor.py:226 msgid "Minimum:" msgstr "Minimum :" -#: ../dialogs/DurationEditorDialog.py:45 +#: ../dialogs/DurationEditorDialog.py:46 msgid "Minutes:" msgstr "Minutes :" -#: ../controls/ProjectPropertiesPanel.py:210 +#: ../controls/ProjectPropertiesPanel.py:211 msgid "Miscellaneous" msgstr "Divers" -#: ../dialogs/LDElementDialog.py:59 +#: ../dialogs/LDElementDialog.py:63 msgid "Modifier:" msgstr "Modificateur :" -#: ../PLCGenerator.py:732 -#: ../PLCGenerator.py:975 -#, python-format -msgid "More than one connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "Plusieurs connecteurs trouvés pour le prolongement \"%s\" dans le POU \"%s\"" - -#: ../dialogs/ActionBlockDialog.py:141 +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" +"Plus d'un connecteur correspondant à \"{a1}\" trouvé dans la continuité de " +"\"{a2}\" POU" + +#: ../dialogs/ActionBlockDialog.py:140 msgid "Move action down" msgstr "Déplacer une action vers le bas" -#: ../dialogs/ActionBlockDialog.py:140 +#: ../dialogs/ActionBlockDialog.py:139 msgid "Move action up" msgstr "Déplacer une action vers le haut" -#: ../controls/DebugVariablePanel.py:1532 -msgid "Move debug variable down" -msgstr "Déplacer une variable à déboguer vers le bas" - -#: ../controls/DebugVariablePanel.py:1531 -msgid "Move debug variable up" -msgstr "Déplacer une variable à déboguer vers le haut" - #: ../controls/CustomEditableListBox.py:43 msgid "Move down" msgstr "Déplacer vers le haut" -#: ../editors/DataTypeEditor.py:354 +#: ../editors/DataTypeEditor.py:355 msgid "Move element down" msgstr "Déplcer un élément vers le bas" -#: ../editors/DataTypeEditor.py:353 +#: ../editors/DataTypeEditor.py:354 msgid "Move element up" msgstr "Déplacer un élément vers le haut" -#: ../editors/ResourceEditor.py:262 +#: ../editors/ResourceEditor.py:271 msgid "Move instance down" msgstr "Déplacer une instance vers le bas" -#: ../editors/ResourceEditor.py:261 +#: ../editors/ResourceEditor.py:270 msgid "Move instance up" msgstr "Déplacer une instance vers le haut" -#: ../editors/ResourceEditor.py:233 +#: ../editors/ResourceEditor.py:242 msgid "Move task down" msgstr "Déplcer une tâche vers le bas" -#: ../editors/ResourceEditor.py:232 +#: ../editors/ResourceEditor.py:241 msgid "Move task up" msgstr "Déplacer une tâche vers le haut" -#: ../IDEFrame.py:82 -#: ../IDEFrame.py:97 -#: ../IDEFrame.py:127 -#: ../IDEFrame.py:168 +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 msgid "Move the view" msgstr "Déplacer la vue" @@ -2298,13 +2225,11 @@ msgid "Move up" msgstr "Déplacer vers le bas" -#: ../controls/VariablePanel.py:383 -#: ../c_ext/CFileEditor.py:520 +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 msgid "Move variable down" msgstr "Déplacer une variable vers le bas" -#: ../controls/VariablePanel.py:382 -#: ../c_ext/CFileEditor.py:519 +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 msgid "Move variable up" msgstr "Déplacer une variable vers le haut" @@ -2320,23 +2245,22 @@ msgid "My Computer:" msgstr "Poste de travail :" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:50 -#: ../editors/ResourceEditor.py:68 -#: ../editors/ResourceEditor.py:77 +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NOM" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Name" msgstr "Nom" -#: ../Beremiz_service.py:386 +#: ../Beremiz_service.py:334 msgid "Name must not be null!" msgstr "Le nom ne doit pas être vide !" -#: ../dialogs/ConnectionDialog.py:65 -#: ../dialogs/FBDVariableDialog.py:89 -#: ../dialogs/LDElementDialog.py:88 -#: ../dialogs/SFCStepDialog.py:51 -#: ../dialogs/FBDBlockDialog.py:70 +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 msgid "Name:" msgstr "Nom :" @@ -2344,15 +2268,20 @@ msgid "Natural logarithm" msgstr "Logarithme népérien" -#: ../editors/Viewer.py:402 -#: ../dialogs/LDElementDialog.py:67 +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 msgid "Negated" msgstr "Inversé" -#: ../Beremiz.py:309 -#: ../Beremiz.py:344 -#: ../PLCOpenEditor.py:106 -#: ../PLCOpenEditor.py:148 +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "Service Web Nevow échoué." + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "Import Nevow/Athena échoué:" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 msgid "New" msgstr "Nouveau" @@ -2360,30 +2289,25 @@ msgid "New item" msgstr "Nouvel élément" -#: ../editors/Viewer.py:401 +#: ../editors/Viewer.py:518 msgid "No Modifier" msgstr "Pas de modificateur" -#: ../PLCControler.py:3054 -msgid "No PLC project found" -msgstr "Pas de projet d'automate trouvé" - -#: ../ProjectController.py:1513 +#: ../ProjectController.py:1826 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "Aucun automate à transférer (la compilation a-t-elle réussi ?)\n" -#: ../PLCGenerator.py:1360 +#: ../PLCGenerator.py:1631 #, python-format msgid "No body defined in \"%s\" POU" msgstr "Pas de code défini dans le POU \"%s\"" -#: ../PLCGenerator.py:751 -#: ../PLCGenerator.py:984 -#, python-format -msgid "No connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "Pas de connecteur trouvé pour le prolongement \"%s\" dans le POU \"%s\"" - -#: ../PLCOpenEditor.py:349 +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "Pas de connecteur correspondant à \"{a1}\" dans la continuité de \"{a2}\" POU" + +#: ../PLCOpenEditor.py:357 msgid "" "No documentation available.\n" "Coming soon." @@ -2391,66 +2315,72 @@ "Pas de documentation.\n" "Bientôt disponible." -#: ../PLCGenerator.py:773 +#: ../PLCGenerator.py:829 #, python-format msgid "No informations found for \"%s\" block" msgstr "Aucune information trouvée pour le block \"%s\"" -#: ../plcopen/structures.py:166 -msgid "No output variable found" -msgstr "Pas de variable de sortie trouvée." +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" +"Pas de variable de sortie {a1} trouvée dans le bloc {a2} dans POU {a3}. La " +"connection doit-être cassée" #: ../controls/SearchResultPanel.py:169 msgid "No search results available." msgstr "Pas de résultat de recherche disponible." -#: ../svgui/svgui.py:98 +#: ../svgui/svgui.py:134 #, python-format msgid "No such SVG file: %s\n" msgstr "Fichier SVG inconnu : %s\n" -#: ../canfestival/config_utils.py:632 -#, python-format -msgid "No such index/subindex (%x,%x) (variable %s)" -msgstr "indice et sous-indice inconnu (%x,%x) (variable %s)" - -#: ../canfestival/config_utils.py:361 -#, python-format -msgid "No such index/subindex (%x,%x) in ID : %d (variable %s)" -msgstr "indice et sous-indice inconnu (%x,%x) pour l'ID : %d (variable %s)" +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "Pas de tel index/sous-index ({a1},{a2}) (variable {a3})" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" +"Pas de tel index/sous-index ({a1},{a2}) avec l'ID : {a3} (variable {a4})" #: ../dialogs/BrowseValuesLibraryDialog.py:83 msgid "No valid value selected!" msgstr "Aucune valeur valide sélectionnée !" -#: ../PLCGenerator.py:1358 +#: ../PLCGenerator.py:1629 #, python-format msgid "No variable defined in \"%s\" POU" msgstr "Pas de varaibles définies dans le POU \"%s\"" -#: ../canfestival/config_utils.py:354 -#, python-format -msgid "Non existing node ID : %d (variable %s)" -msgstr "Le node ID n'existe pas : %d (variable %s)" - -#: ../controls/VariablePanel.py:69 +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "Node ID inexistant : {a1} (variable {a2})" + +#: ../controls/VariablePanel.py:64 msgid "Non-Retain" msgstr "Non-Persistante" -#: ../dialogs/LDElementDialog.py:62 +#: ../dialogs/LDElementDialog.py:75 msgid "Normal" msgstr "Normal" -#: ../canfestival/config_utils.py:383 -#, python-format -msgid "Not PDO mappable variable : '%s' (ID:%d,Idx:%x,sIdx:%x))" -msgstr "Variable non mappable dans un PDO : '%s' (ID:%d,Idx:%x,sIdx:%x))" +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "Variable non mappable PDO : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" #: ../plcopen/iec_std.csv:80 msgid "Not equal to" msgstr "Non égal à" -#: ../dialogs/SFCDivergenceDialog.py:80 +#: ../dialogs/SFCDivergenceDialog.py:89 msgid "Number of sequences:" msgstr "Nombre de branches :" @@ -2458,227 +2388,238 @@ msgid "Numerical" msgstr "Numérique" -#: ../plcopen/structures.py:246 -msgid "" -"Off-delay timer\n" -"The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." -msgstr "" -"Temporisation avec retard à l'extinction\n" -"La temporisation avec retard à l'extinction peut être utilisé pour retarder le passage de la sortie à l'état faux, d'une période fixe après le passage de l'entrée à l'état faux" - -#: ../plcopen/structures.py:241 -msgid "" -"On-delay timer\n" -"The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." -msgstr "" -"Temporisation avec retard à l'allumage\n" -"La temporisation avec retard à l'allumage peut être utilisé pour retarder le passage de la sortie à l'état vrai, d'une période fixe après le passage de l'entrée à l'état vrai" - -#: ../dialogs/SearchInProjectDialog.py:93 +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:84 msgid "Only Elements" msgstr "Uniquement les éléments" -#: ../Beremiz.py:311 -#: ../Beremiz.py:345 -#: ../PLCOpenEditor.py:108 -#: ../PLCOpenEditor.py:149 +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 msgid "Open" msgstr "Ouvrir" -#: ../svgui/svgui.py:107 +#: ../svgui/svgui.py:143 msgid "Open Inkscape" msgstr "Ouverture de Inkscape" -#: ../ProjectController.py:1565 +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" +"Cadriciel Open Source pour l'automatisme, IDE implémenté en IEC 61131 avec " +"un lot d'extensions constament croissant et un executable PLC flexible." + +#: ../ProjectController.py:1878 msgid "Open a file explorer to manage project files" msgstr "Ouvrir un explorateur de fichier pour gérer les fichiers de projet" -#: ../wxglade_hmi/wxglade_hmi.py:109 +#: ../wxglade_hmi/wxglade_hmi.py:155 msgid "Open wxGlade" msgstr "Ouverture de wxGlade" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Option" msgstr "Option" -#: ../dialogs/FindInPouDialog.py:76 +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 msgid "Options" msgstr "Options" -#: ../controls/ProjectPropertiesPanel.py:97 +#: ../controls/ProjectPropertiesPanel.py:98 msgid "Organization (optional):" msgstr "Groupe (optionnel) :" -#: ../canfestival/SlaveEditor.py:51 -#: ../canfestival/NetworkEditor.py:72 +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 msgid "Other Profile" msgstr "Autre profil" -#: ../controls/VariablePanel.py:76 -#: ../editors/Viewer.py:1411 -#: ../dialogs/BrowseLocationsDialog.py:36 -#: ../dialogs/FBDVariableDialog.py:35 -#: ../dialogs/SFCStepDialog.py:65 +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Output" msgstr "Sortie" -#: ../canfestival/SlaveEditor.py:40 -#: ../canfestival/NetworkEditor.py:61 +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 msgid "PDO Receive" msgstr "PDO reçu" -#: ../canfestival/SlaveEditor.py:39 -#: ../canfestival/NetworkEditor.py:60 +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 msgid "PDO Transmit" msgstr "PDO transmis" -#: ../plcopen/structures.py:268 -msgid "" -"PID\n" -"The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." -msgstr "" -"PID\n" -"Le bloc fonctionnel PID (Proportionnel, Intégrale, Dérivée) fournit un controller de boucle fermé classique à trois paramètres." - -#: ../targets/toolchain_gcc.py:107 +#: ../targets/toolchain_gcc.py:167 msgid "PLC :\n" msgstr "Automate :\n" -#: ../Beremiz.py:425 +#: ../BeremizIDE.py:355 msgid "PLC Log" msgstr "Log de l'automate" -#: ../PLCOpenEditor.py:294 -#: ../PLCOpenEditor.py:370 +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "Génération du code PLC échouée !\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "Le PLC est vide ou déjà lancé." + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "PLC non démarré" + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" +"Erreur de syntaxe PLC à la ligne {a1}:\n" +"{a2}" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" msgstr "Fichiers PLCOpen (*.xml)|*.xml|Tous les fichiers|*.*" -#: ../PLCOpenEditor.py:156 -#: ../PLCOpenEditor.py:212 +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 msgid "PLCOpenEditor" msgstr "PLCOpenEditor" -#: ../dialogs/PouDialog.py:98 +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" +"PLCOpenEditor fait partie du projet Beremiz.\n" +"\n" +"Beremiz est un " + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "PORT" + +#: ../dialogs/PouDialog.py:101 msgid "POU Name" msgstr "Nom du POU" -#: ../dialogs/PouDialog.py:56 +#: ../dialogs/PouDialog.py:58 msgid "POU Name:" msgstr "Nom du POU :" -#: ../dialogs/PouDialog.py:100 +#: ../dialogs/PouDialog.py:103 msgid "POU Type" msgstr "Type du POU" -#: ../dialogs/PouDialog.py:63 +#: ../dialogs/PouDialog.py:65 msgid "POU Type:" msgstr "Type du POU :" -#: ../Beremiz.py:324 -#: ../PLCOpenEditor.py:122 +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "Connexion de PYRO à l'URI:%s\n" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "PYRO utilise les certificats dans '%s'\n" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 msgid "Page Setup" msgstr "Mise en page..." -#: ../controls/ProjectPropertiesPanel.py:110 +#: ../controls/ProjectPropertiesPanel.py:111 msgid "Page Size (optional):" msgstr "Taille de la page (optionnel) :" -#: ../IDEFrame.py:2492 +#: ../IDEFrame.py:2613 #, python-format msgid "Page: %d" msgstr "Page: %d" -#: ../controls/PouInstanceVariablesPanel.py:48 +#: ../controls/PouInstanceVariablesPanel.py:124 msgid "Parent instance" msgstr "Instance parent" -#: ../IDEFrame.py:355 -#: ../IDEFrame.py:407 -#: ../editors/Viewer.py:537 +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 msgid "Paste" msgstr "Coller" -#: ../IDEFrame.py:1776 +#: ../IDEFrame.py:1868 msgid "Paste POU" msgstr "Coller un POU" -#: ../dialogs/SearchInProjectDialog.py:64 +#: ../dialogs/SearchInProjectDialog.py:56 msgid "Pattern to search:" msgstr "Modèle à rechercher :" -#: ../dialogs/LDPowerRailDialog.py:64 +#: ../dialogs/LDPowerRailDialog.py:74 msgid "Pin number:" msgstr "Nombre de pattes :" -#: ../editors/Viewer.py:2363 -#: ../editors/Viewer.py:2670 -#: ../editors/SFCViewer.py:696 +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 msgid "Please choose a target" msgstr "Choisissez une cible" -#: ../editors/Viewer.py:2186 -#: ../editors/Viewer.py:2188 -#: ../editors/Viewer.py:2706 -#: ../editors/Viewer.py:2708 +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "Entrer un nom de bloc s'il vous plaît" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 msgid "Please enter comment text" msgstr "Saisissez le texte du commentaire" -#: ../editors/SFCViewer.py:359 -#: ../editors/SFCViewer.py:381 -#: ../editors/SFCViewer.py:725 +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 msgid "Please enter step name" msgstr "Saisissez le nom de l'étape" +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "Saisissez le texte" + #: ../dialogs/ForceVariableDialog.py:163 #, python-format msgid "Please enter value for a \"%s\" variable:" msgstr "Veuillez entrer la valeur pour une variable de type \"%s\" :" -#: ../Beremiz_service.py:371 +#: ../Beremiz_service.py:319 msgid "Port number must be 0 <= port <= 65535!" msgstr "Le numéro de port doit être compris entre 0 et 65535 !" -#: ../Beremiz_service.py:371 +#: ../Beremiz_service.py:319 msgid "Port number must be an integer!" msgstr "Le numéro de port doit être un entier !" -#: ../editors/GraphicViewer.py:105 -msgid "Position:" -msgstr "Position :" - -#: ../editors/Viewer.py:475 +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 msgid "Power Rail" msgstr "Barre d'alimentation" -#: ../dialogs/LDPowerRailDialog.py:36 +#: ../dialogs/LDPowerRailDialog.py:51 msgid "Power Rail Properties" msgstr "Propriétés de la barre d'alimentation" -#: ../Beremiz.py:326 -#: ../PLCOpenEditor.py:124 +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 msgid "Preview" msgstr "Aperçu avant impression" -#: ../dialogs/SFCDivergenceDialog.py:93 -#: ../dialogs/LDPowerRailDialog.py:78 -#: ../dialogs/ConnectionDialog.py:78 -#: ../dialogs/FBDVariableDialog.py:97 -#: ../dialogs/SFCTransitionDialog.py:96 -#: ../dialogs/LDElementDialog.py:101 -#: ../dialogs/SFCStepDialog.py:79 -#: ../dialogs/FBDBlockDialog.py:103 +#: ../dialogs/BlockPreviewDialog.py:57 msgid "Preview:" msgstr "Aperçu :" -#: ../Beremiz.py:328 -#: ../Beremiz.py:348 -#: ../PLCOpenEditor.py:126 -#: ../PLCOpenEditor.py:152 +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 msgid "Print" msgstr "Imprimer" -#: ../IDEFrame.py:1038 +#: ../IDEFrame.py:1079 msgid "Print preview" msgstr "Aperçu avant impression" @@ -2686,46 +2627,53 @@ msgid "Priority" msgstr "Priorité" -#: ../dialogs/SFCTransitionDialog.py:83 +#: ../dialogs/SFCTransitionDialog.py:90 msgid "Priority:" msgstr "Priorité :" -#: ../runtime/PLCObject.py:318 +#: ../runtime/PLCObject.py:369 #, python-format msgid "Problem starting PLC : error %d" msgstr "Problème au démarrage du PLC : erreur %d" -#: ../controls/ProjectPropertiesPanel.py:80 +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "Nom de produit" + +#: ../controls/ProjectPropertiesPanel.py:81 msgid "Product Name (required):" msgstr "Nom du produit (obligatoire) :" -#: ../controls/ProjectPropertiesPanel.py:82 +#: ../controls/ProjectPropertiesPanel.py:83 msgid "Product Release (optional):" msgstr "Publication du produit (optionnel) :" -#: ../controls/ProjectPropertiesPanel.py:81 +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "Version du produit" + +#: ../controls/ProjectPropertiesPanel.py:82 msgid "Product Version (required):" msgstr "Version du produit (obligatoire) :" -#: ../IDEFrame.py:1848 -#: ../dialogs/SearchInProjectDialog.py:46 +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 msgid "Program" msgstr "Programme" -#: ../PLCOpenEditor.py:339 +#: ../PLCOpenEditor.py:347 msgid "Program was successfully generated!" msgstr "Le programme a été généré avec succès !" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Programs" msgstr "Programmes" -#: ../editors/Viewer.py:230 +#: ../editors/Viewer.py:243 msgid "Programs can't be used by other POUs!" msgstr "Les programmes ne peuvent être utilisés par les autres POUs !" -#: ../controls/ProjectPropertiesPanel.py:84 -#: ../IDEFrame.py:557 +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 msgid "Project" msgstr "Projet" @@ -2734,19 +2682,23 @@ msgid "Project '%s':" msgstr "Projet '%s' :" -#: ../ProjectController.py:1564 +#: ../ProjectController.py:1877 msgid "Project Files" msgstr "Fichiers de projet" -#: ../controls/ProjectPropertiesPanel.py:78 +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "Nom du projet" + +#: ../controls/ProjectPropertiesPanel.py:79 msgid "Project Name (required):" msgstr "Nom du projet (obligatoire) :" -#: ../controls/ProjectPropertiesPanel.py:79 +#: ../controls/ProjectPropertiesPanel.py:80 msgid "Project Version (optional):" msgstr "Version du projet (optionnel) :" -#: ../PLCControler.py:3041 +#: ../PLCControler.py:3164 msgid "" "Project file syntax error:\n" "\n" @@ -2754,113 +2706,90 @@ "Erreur de syntaxe dans le fichier du projet :\n" "\n" -#: ../editors/ProjectNodeEditor.py:14 -#: ../dialogs/ProjectDialog.py:32 +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 msgid "Project properties" msgstr "Propriétés du projet" -#: ../ConfigTreeNode.py:526 -#, python-format -msgid "Project tree layout do not match confnode.xml %s!=%s " -msgstr "L'organisation du projet ne correspond pas à plugin.xml %s!=%s" - -#: ../dialogs/ConnectionDialog.py:96 +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:98 msgid "Propagate Name" msgstr "Propager le nom" -#: ../PLCControler.py:96 +#: ../PLCControler.py:99 msgid "Properties" msgstr "Propriétés" -#: ../plcopen/structures.py:236 -msgid "" -"Pulse timer\n" -"The pulse timer can be used to generate output pulses of a given time duration." -msgstr "" -"Temporisation à impulsion\n" -"La temporisation à impulsion peut être utilisée pour générer sur la sortie des impulsions d'une durée déterminée." - -#: ../py_ext/PythonEditor.py:61 +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "Pyro port :" + +#: ../py_ext/PythonEditor.py:81 msgid "Python code" msgstr "Code Python" -#: ../features.py:9 +#: ../features.py:33 msgid "Python file" msgstr "Fichier Python" -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Qualifier" msgstr "Qualificatif" -#: ../Beremiz_service.py:333 -#: ../Beremiz.py:331 -#: ../PLCOpenEditor.py:132 +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 msgid "Quit" msgstr "Quitter" -#: ../plcopen/structures.py:201 -msgid "" -"RS bistable\n" -"The RS bistable is a latch where the Reset dominates." -msgstr "" -"Bascule RS\n" -"La bascule RS est une bascule où le Reset est dominant." - -#: ../plcopen/structures.py:273 -msgid "" -"Ramp\n" -"The RAMP function block is modelled on example given in the standard." -msgstr "" -"Rampe\n" -"Le bloc fonctionnel RAMP est basé sur l'exemple du standard." - -#: ../controls/DebugVariablePanel.py:1462 -#: ../editors/GraphicViewer.py:89 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 msgid "Range:" msgstr "Echelle :" -#: ../ProjectController.py:1560 +#: ../ProjectController.py:1873 msgid "Raw IEC code" msgstr "Ajout code IEC" -#: ../plcopen/structures.py:253 -msgid "" -"Real time clock\n" -"The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." -msgstr "" -"Horloge temps réel\n" -"L'horloge temps réel est utilisée dans de nombreux cas tels que l'horodatage, la définition des dates et heures dans des rapports de commandes, dans des messages d'alarme et bien d'autres." - -#: ../Beremiz.py:1072 +#: ../BeremizIDE.py:1047 #, python-format msgid "Really delete node '%s'?" msgstr "Êtes-vous sûr de vouloir supprimer le noeud '%s' ?" -#: ../IDEFrame.py:345 -#: ../IDEFrame.py:403 +#: ../IDEFrame.py:362 ../IDEFrame.py:422 msgid "Redo" msgstr "Refaire" -#: ../dialogs/SFCTransitionDialog.py:57 -#: ../dialogs/SFCTransitionDialog.py:135 +#: ../dialogs/SFCTransitionDialog.py:75 msgid "Reference" msgstr "Référence" -#: ../IDEFrame.py:413 -#: ../dialogs/DiscoveryDialog.py:105 +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 msgid "Refresh" msgstr "Actualiser" -#: ../dialogs/SearchInProjectDialog.py:73 +#: ../dialogs/SearchInProjectDialog.py:66 msgid "Regular expression" msgstr "Expression régulière" -#: ../dialogs/FindInPouDialog.py:91 +#: ../dialogs/FindInPouDialog.py:96 msgid "Regular expressions" msgstr "Expressions régulières" -#: ../controls/DebugVariablePanel.py:1938 -#: ../editors/Viewer.py:1388 +#: ../editors/Viewer.py:1603 msgid "Release value" msgstr "Relacher la valeur" @@ -2868,20 +2797,24 @@ msgid "Remainder (modulo)" msgstr "Modulo" -#: ../Beremiz.py:1073 +#: ../BeremizIDE.py:1048 #, python-format msgid "Remove %s node" msgstr "Enlever un noeud %s" -#: ../dialogs/ActionBlockDialog.py:139 +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "Supprimer le Datatype" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "Supprimer le Pou" + +#: ../dialogs/ActionBlockDialog.py:138 msgid "Remove action" msgstr "Supprimer une action" -#: ../controls/DebugVariablePanel.py:1530 -msgid "Remove debug variable" -msgstr "Supprimer une variable à déboguer" - -#: ../editors/DataTypeEditor.py:352 +#: ../editors/DataTypeEditor.py:353 msgid "Remove element" msgstr "Supprimer un élément" @@ -2889,24 +2822,23 @@ msgid "Remove file from left folder" msgstr "Supprimer un fichier du dossier de gauche" -#: ../editors/ResourceEditor.py:260 +#: ../editors/ResourceEditor.py:269 msgid "Remove instance" msgstr "Supprimer une instance" -#: ../canfestival/NetworkEditor.py:81 +#: ../canfestival/NetworkEditor.py:104 msgid "Remove slave" msgstr "Enlever l'esclave" -#: ../editors/ResourceEditor.py:231 +#: ../editors/ResourceEditor.py:240 msgid "Remove task" msgstr "Supprimer la tâche" -#: ../controls/VariablePanel.py:381 -#: ../c_ext/CFileEditor.py:518 +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 msgid "Remove variable" msgstr "Supprimer une variable" -#: ../IDEFrame.py:1852 +#: ../IDEFrame.py:1948 msgid "Rename" msgstr "Renommer" @@ -2914,6 +2846,10 @@ msgid "Replace File" msgstr "Remplacer un fichier" +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "Remplacer le fil par les connexions" + #: ../plcopen/iec_std.csv:89 msgid "Replacement (within)" msgstr "Remplacement (au milieu)" @@ -2922,11 +2858,11 @@ msgid "Reset" msgstr "Mise à zéro" -#: ../editors/Viewer.py:521 +#: ../editors/Viewer.py:642 msgid "Reset Execution Order" msgstr "Réinitialiser l'order d'exécution" -#: ../IDEFrame.py:428 +#: ../IDEFrame.py:451 msgid "Reset Perspective" msgstr "Réinitialiser l'interface" @@ -2934,43 +2870,30 @@ msgid "Reset search result" msgstr "Réinitialiser le résultat de la recherche" -#: ../editors/GraphicViewer.py:137 -msgid "Reset zoom and offset" -msgstr "Réinitialisation du zoom et de l'offset" - -#: ../PLCControler.py:96 +#: ../BeremizIDE.py:979 ../PLCControler.py:99 msgid "Resources" msgstr "Ressources" -#: ../controls/VariablePanel.py:67 +#: ../controls/VariablePanel.py:62 msgid "Retain" msgstr "Persistante" -#: ../controls/VariablePanel.py:354 +#: ../controls/VariablePanel.py:424 msgid "Return Type:" msgstr "Type de retour :" -#: ../editors/Viewer.py:429 +#: ../editors/Viewer.py:546 msgid "Right" msgstr "Droite" -#: ../dialogs/LDPowerRailDialog.py:60 +#: ../dialogs/LDPowerRailDialog.py:64 msgid "Right PowerRail" msgstr "Barre d'alimentation à droite" -#: ../editors/Viewer.py:403 -#: ../dialogs/LDElementDialog.py:80 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 msgid "Rising Edge" msgstr "Front montant" -#: ../plcopen/structures.py:211 -msgid "" -"Rising edge detector\n" -"The output produces a single pulse when a rising edge is detected." -msgstr "" -"Détecteur de front montant\n" -"La sortie produit une impulsion unique lorsqu'un front montant est détecté." - #: ../plcopen/iec_std.csv:65 msgid "Rotate left" msgstr "Rotation à gauche" @@ -2983,140 +2906,138 @@ msgid "Rounding up/down" msgstr "Arrondi" -#: ../ProjectController.py:1528 +#: ../ProjectController.py:1841 msgid "Run" msgstr "Exécuter" -#: ../ProjectController.py:865 -#: ../ProjectController.py:874 -msgid "Runtime extensions C code generation failed !\n" -msgstr "La génération du code des plugins a échoué !\n" - -#: ../canfestival/SlaveEditor.py:38 -#: ../canfestival/NetworkEditor.py:59 +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 msgid "SDO Client" msgstr "Client SDO" -#: ../canfestival/SlaveEditor.py:37 -#: ../canfestival/NetworkEditor.py:58 +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 msgid "SDO Server" msgstr "Serveur SDO" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "SFC" msgstr "SFC" -#: ../plcopen/structures.py:196 -msgid "" -"SR bistable\n" -"The SR bistable is a latch where the Set dominates." -msgstr "" -"Bascule SR\n" -"La bascule SR est une bascule où le Set est dominant." - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "ST" msgstr "ST" -#: ../PLCOpenEditor.py:326 +#: ../PLCOpenEditor.py:334 msgid "ST files (*.st)|*.st|All files|*.*" msgstr "Fichiers ST (*.st)|*.st|Tous les fichiers|*.*" -#: ../svgui/svgui.py:92 +#: ../svgui/svgui.py:128 msgid "SVG files (*.svg)|*.svg|All files|*.*" msgstr "Fichiers SVG (*.svg)|*.svg|Tous les fichiers|*.*" -#: ../features.py:11 +#: ../features.py:35 msgid "SVGUI" msgstr "SVGUI" -#: ../Beremiz.py:315 -#: ../Beremiz.py:346 -#: ../PLCOpenEditor.py:115 -#: ../PLCOpenEditor.py:150 +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 msgid "Save" msgstr "Enregistrer" -#: ../Beremiz.py:347 -#: ../PLCOpenEditor.py:117 -#: ../PLCOpenEditor.py:151 +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 msgid "Save As..." msgstr "Enregistrer sous..." -#: ../Beremiz.py:317 +#: ../BeremizIDE.py:224 msgid "Save as" msgstr "Enregistrer sous..." -#: ../dialogs/SearchInProjectDialog.py:76 +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:69 msgid "Scope" msgstr "Contexte" -#: ../IDEFrame.py:596 -#: ../dialogs/SearchInProjectDialog.py:105 +#: ../IDEFrame.py:623 msgid "Search" msgstr "Rechercher" -#: ../IDEFrame.py:365 -#: ../IDEFrame.py:409 -#: ../dialogs/SearchInProjectDialog.py:52 +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 msgid "Search in Project" msgstr "Rechercher dans le projet" -#: ../dialogs/DurationEditorDialog.py:46 +#: ../dialogs/DurationEditorDialog.py:47 msgid "Seconds:" msgstr "Secondes :" -#: ../IDEFrame.py:371 +#: ../IDEFrame.py:388 msgid "Select All" msgstr "Tout sélectionner" -#: ../controls/LocationCellEditor.py:97 -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:323 -#: ../editors/Viewer.py:275 +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Select a variable class:" msgstr "Sélectionner une direction pour la variable :" -#: ../ProjectController.py:1039 +#: ../ProjectController.py:1257 msgid "Select an editor:" msgstr "Sélectionner un éditeur :" -#: ../controls/PouInstanceVariablesPanel.py:209 +#: ../controls/PouInstanceVariablesPanel.py:281 msgid "Select an instance" msgstr "Sélectionnez une instance" -#: ../IDEFrame.py:580 +#: ../IDEFrame.py:607 msgid "Select an object" msgstr "Sélectionner un objet" +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" + #: ../plcopen/iec_std.csv:70 msgid "Selection" msgstr "Sélection" -#: ../dialogs/SFCDivergenceDialog.py:62 +#: ../dialogs/SFCDivergenceDialog.py:65 msgid "Selection Convergence" msgstr "Convergence simple" -#: ../dialogs/SFCDivergenceDialog.py:55 +#: ../dialogs/SFCDivergenceDialog.py:64 msgid "Selection Divergence" msgstr "Divergence simple" -#: ../plcopen/structures.py:206 -msgid "" -"Semaphore\n" -"The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." -msgstr "" -"Sémaphore\n" -"La sémaphore fournit un mécanisme permettant à des éléments du programme d'accéder de façon exclusive à certaines resources." - -#: ../dialogs/DiscoveryDialog.py:84 +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "Recherche de service" + +#: ../dialogs/DiscoveryDialog.py:85 msgid "Services available:" msgstr "Services disponibles:" -#: ../dialogs/LDElementDialog.py:72 +#: ../dialogs/LDElementDialog.py:76 msgid "Set" msgstr "Mise à 1" @@ -3128,27 +3049,27 @@ msgid "Shift right" msgstr "Décalage à droite" -#: ../ProjectController.py:1554 +#: ../ProjectController.py:1867 msgid "Show IEC code generated by PLCGenerator" msgstr "Afficher le code IEC généré par PLCGenerator" -#: ../canfestival/canfestival.py:363 +#: ../canfestival/canfestival.py:389 msgid "Show Master" msgstr "Afficher le maître" -#: ../canfestival/canfestival.py:364 +#: ../canfestival/canfestival.py:390 msgid "Show Master generated by config_utils" msgstr "Afficher le maître généré par config_utils" -#: ../ProjectController.py:1552 +#: ../ProjectController.py:1865 msgid "Show code" msgstr "Afficher le code" -#: ../dialogs/SFCDivergenceDialog.py:74 +#: ../dialogs/SFCDivergenceDialog.py:67 msgid "Simultaneous Convergence" msgstr "Convergence double" -#: ../dialogs/SFCDivergenceDialog.py:68 +#: ../dialogs/SFCDivergenceDialog.py:66 msgid "Simultaneous Divergence" msgstr "Divergence double" @@ -3160,49 +3081,67 @@ msgid "Single" msgstr "Evènement" +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + #: ../plcopen/iec_std.csv:23 msgid "Square root (base 2)" msgstr "Racine carré (base 2)" -#: ../plcopen/structures.py:192 +#: ../plcopen/definitions.py:48 msgid "Standard function blocks" msgstr "Blocs fonctionnels standards" -#: ../Beremiz_service.py:321 -#: ../ProjectController.py:1530 +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 msgid "Start PLC" msgstr "Démarrer l'automate" -#: ../ProjectController.py:843 +#: ../ProjectController.py:1046 #, python-format msgid "Start build in %s\n" msgstr "Début de la compilation dans %s\n" -#: ../ProjectController.py:1341 +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "Démarré" + +#: ../ProjectController.py:1648 msgid "Starting PLC\n" msgstr "Démarrer l'automate\n" -#: ../Beremiz.py:435 +#: ../BeremizIDE.py:365 msgid "Status ToolBar" msgstr "Barre d'outils de statut" -#: ../editors/Viewer.py:492 +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 msgid "Step" msgstr "Étape" -#: ../ProjectController.py:1533 +#: ../ProjectController.py:1846 msgid "Stop" msgstr "Arrêter" -#: ../Beremiz_service.py:322 +#: ../Beremiz_service.py:264 msgid "Stop PLC" msgstr "Arrêter l'automate" -#: ../ProjectController.py:1535 +#: ../ProjectController.py:1848 msgid "Stop Running PLC" msgstr "Arrêter l'automate en cours d'exécution" -#: ../ProjectController.py:1318 +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "Arrêté" + +#: ../ProjectController.py:1620 msgid "Stopping debugger...\n" msgstr "Arrêt du débogage en cours\n" @@ -3218,30 +3157,51 @@ msgid "Subtraction" msgstr "Soustraction" -#: ../ProjectController.py:942 +#: ../ProjectController.py:1085 msgid "Successfully built.\n" msgstr "Compilé avec succès.\n" -#: ../dialogs/SearchInProjectDialog.py:154 +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 msgid "Syntax error in regular expression of pattern to search!" -msgstr "Erreur de syntaxe dans l'expression régulière du modèle à rechercher !" +msgstr "" +"Erreur de syntaxe dans l'expression régulière du modèle à rechercher !" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "TYPE" #: ../plcopen/iec_std.csv:29 msgid "Tangent" msgstr "Tangente" -#: ../editors/ResourceEditor.py:77 +#: ../editors/ResourceEditor.py:83 msgid "Task" msgstr "Tâche" -#: ../editors/ResourceEditor.py:226 +#: ../editors/ResourceEditor.py:235 msgid "Tasks:" msgstr "Tâches :" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid "Temp" msgstr "Temporaire" +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + #: ../editors/FileManagementPanel.py:180 #, python-format msgid "" @@ -3255,18 +3215,20 @@ msgid "The group of block must be coherent!" msgstr "Le groupe de blocs doit être cohérent !" -#: ../IDEFrame.py:974 -#: ../Beremiz.py:590 +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 msgid "There are changes, do you want to save?" msgstr "Le projet a été modifié. Voulez-vous l'enregistrer ?" -#: ../IDEFrame.py:1590 -#: ../IDEFrame.py:1609 -#, python-format -msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" -msgstr "Un POU a pour nom \"%s\". Cela peut générer des conflits. Voulez-vous continuer ?" - -#: ../IDEFrame.py:1061 +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" +"Un POU a pour nom \"%s\". Cela peut générer des conflits. Voulez-vous " +"continuer ?" + +#: ../IDEFrame.py:1102 msgid "" "There was a problem printing.\n" "Perhaps your current printer is not set correctly?" @@ -3278,16 +3240,16 @@ msgid "This option isn't available yet!" msgstr "Cette option n'a pas encore disponible" -#: ../editors/GraphicViewer.py:278 -msgid "Tick" -msgstr "Tick" +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "" #: ../plcopen/iec_std.csv:40 msgid "Time" msgstr "Temps" -#: ../plcopen/iec_std.csv:40 -#: ../plcopen/iec_std.csv:41 +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 msgid "Time addition" msgstr "Addition de durée" @@ -3295,61 +3257,64 @@ msgid "Time concatenation" msgstr "Concaténation de date et de durée" -#: ../plcopen/iec_std.csv:60 -#: ../plcopen/iec_std.csv:61 +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 msgid "Time division" msgstr "Division de durée" -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:47 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 msgid "Time multiplication" msgstr "Multiplication de durée" -#: ../plcopen/iec_std.csv:48 -#: ../plcopen/iec_std.csv:49 +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 msgid "Time subtraction" msgstr "Soustraction de durée" -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:43 +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 msgid "Time-of-day addition" msgstr "Addition d'horodatage" -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:53 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:55 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 msgid "Time-of-day subtraction" msgstr "Soustraction d'horodatage" -#: ../editors/Viewer.py:431 +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:548 msgid "Top" msgstr "Haut" -#: ../ProjectController.py:1542 +#: ../ProjectController.py:1855 msgid "Transfer" msgstr "Transférer" -#: ../ProjectController.py:1544 +#: ../ProjectController.py:1857 msgid "Transfer PLC" msgstr "Transférer l'automate" -#: ../ProjectController.py:1509 +#: ../ProjectController.py:1820 msgid "Transfer completed successfully.\n" msgstr "Transfert effectué avec succès.\n" -#: ../ProjectController.py:1511 +#: ../ProjectController.py:1823 msgid "Transfer failed\n" msgstr "Le transfert a échoué\n" -#: ../editors/Viewer.py:493 +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 msgid "Transition" msgstr "Transition" -#: ../PLCGenerator.py:1252 -#, python-format -msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" -msgstr "Le code de la transition \"%s\" doit contenir une variable de sortie ou un relai dont la référence est son nom" +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" +"Le code de la transition \"%s\" doit contenir une variable de sortie ou un " +"relai dont la référence est son nom" #: ../dialogs/PouTransitionDialog.py:84 msgid "Transition Name" @@ -3359,43 +3324,50 @@ msgid "Transition Name:" msgstr "Nom de la transition :" -#: ../PLCGenerator.py:1340 -#, python-format -msgid "Transition with content \"%s\" not connected to a next step in \"%s\" POU" -msgstr "La transition contenant \"%s\" n'est pas connectée à une étape en sortie dans le POU \"%s\" !" - -#: ../PLCGenerator.py:1331 -#, python-format -msgid "Transition with content \"%s\" not connected to a previous step in \"%s\" POU" -msgstr "La transition contenant \"%s\" n'est pas connectée à une étape en entrée dans le POU \"%s\" !" - -#: ../plcopen/plcopen.py:1447 +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 #, python-format msgid "Transition with name %s doesn't exist!" msgstr "La transition nommée %s n'existe pas !" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Transitions" msgstr "Transitions" +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + #: ../editors/ResourceEditor.py:68 msgid "Triggering" msgstr "Activation" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:50 -#: ../editors/ResourceEditor.py:77 -#: ../dialogs/ActionBlockDialog.py:37 +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Type" msgstr "Type" -#: ../dialogs/BrowseLocationsDialog.py:44 +#: ../dialogs/BrowseLocationsDialog.py:49 msgid "Type and derivated" msgstr "Type et ses dérivés" -#: ../canfestival/config_utils.py:335 -#: ../canfestival/config_utils.py:617 +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 #, python-format msgid "Type conflict for location \"%s\"" msgstr "Conflit entre types pour l'adresse \"%s\"" @@ -3404,181 +3376,179 @@ msgid "Type conversion" msgstr "Conversion de type" -#: ../editors/DataTypeEditor.py:161 +#: ../editors/DataTypeEditor.py:162 msgid "Type infos:" msgstr "Propriétés du type :" -#: ../dialogs/BrowseLocationsDialog.py:45 +#: ../dialogs/BrowseLocationsDialog.py:50 msgid "Type strict" msgstr "Type uniquement" -#: ../dialogs/SFCDivergenceDialog.py:51 -#: ../dialogs/LDPowerRailDialog.py:51 -#: ../dialogs/BrowseLocationsDialog.py:95 -#: ../dialogs/ConnectionDialog.py:52 -#: ../dialogs/SFCTransitionDialog.py:53 -#: ../dialogs/FBDBlockDialog.py:48 +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 msgid "Type:" msgstr "Type :" -#: ../canfestival/config_utils.py:455 -#: ../canfestival/config_utils.py:469 +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 #, python-format msgid "Unable to define PDO mapping for node %02x" msgstr "Impossible de définir le mappage des PDO pour le noeud %02x" -#: ../targets/Xenomai/__init__.py:14 +#: ../targets/Xenomai/__init__.py:39 #, python-format msgid "Unable to get Xenomai's %s \n" msgstr "Unable to get Xenomai's %s \n" -#: ../PLCGenerator.py:904 -#: ../PLCGenerator.py:963 -#, python-format -msgid "Undefined block type \"%s\" in \"%s\" POU" -msgstr "Type de block \"%s\" indéfini dans le POU \"%s\"" - -#: ../PLCGenerator.py:252 +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 #, python-format msgid "Undefined pou type \"%s\"" msgstr "Type de POU \"%s\" indéterminé !" -#: ../IDEFrame.py:343 -#: ../IDEFrame.py:402 +#: ../IDEFrame.py:360 ../IDEFrame.py:421 msgid "Undo" msgstr "Défaire" -#: ../ProjectController.py:262 +#: ../ProjectController.py:423 msgid "Unknown" msgstr "Inconnu" -#: ../editors/Viewer.py:335 +#: ../editors/Viewer.py:394 #, python-format msgid "Unknown variable \"%s\" for this POU!" msgstr "Variable \"%s\" inconnue dans ce POU !" -#: ../ProjectController.py:259 -#: ../ProjectController.py:260 +#: ../ProjectController.py:420 ../ProjectController.py:421 msgid "Unnamed" msgstr "SansNom" -#: ../PLCControler.py:305 +#: ../PLCControler.py:638 #, python-format msgid "Unnamed%d" msgstr "Sansnom%d" -#: ../controls/VariablePanel.py:272 +#: ../controls/VariablePanel.py:284 #, python-format msgid "Unrecognized data size \"%s\"" msgstr "Taille de donnée \"%s\" non identifié !" -#: ../plcopen/structures.py:221 -msgid "" -"Up-counter\n" -"The up-counter can be used to signal when a count has reached a maximum value." -msgstr "" -"Compteur incrémental\n" -"Le compteur incrémental peut être utilisé pour signaler lorsque le compteur a atteint la valeur maximale." - -#: ../plcopen/structures.py:231 -msgid "" -"Up-down counter\n" -"The 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." -msgstr "" -"Compteur bidirectionnel\n" -"Le compteur bidirectionnel a deux entrées CU et CD. Il peut être utilisé pour compter de façon incrémentale ou décrémentale sur l'une ou l'autre des entrées." - -#: ../controls/VariablePanel.py:712 -#: ../editors/DataTypeEditor.py:631 +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 msgid "User Data Types" msgstr "Types de donnée du projet" -#: ../canfestival/SlaveEditor.py:42 -#: ../canfestival/NetworkEditor.py:63 +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 msgid "User Type" msgstr "Type utilisateur" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "User-defined POUs" msgstr "POUs du projet" -#: ../controls/DebugVariablePanel.py:58 -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Value" msgstr "Valeur" -#: ../editors/GraphicViewer.py:278 -msgid "Values" -msgstr "Valeurs" - -#: ../editors/DataTypeEditor.py:258 +#: ../editors/DataTypeEditor.py:259 msgid "Values:" msgstr "Valeurs" -#: ../controls/DebugVariablePanel.py:58 -#: ../editors/Viewer.py:465 -#: ../dialogs/ActionBlockDialog.py:41 +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 msgid "Variable" msgstr "Variable" -#: ../dialogs/FBDVariableDialog.py:47 +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 msgid "Variable Properties" msgstr "Propriétés de la variable" -#: ../controls/LocationCellEditor.py:97 -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:323 -#: ../editors/Viewer.py:275 +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Variable class" msgstr "Direction de la variable" -#: ../editors/TextViewer.py:367 -#: ../editors/Viewer.py:337 +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 msgid "Variable don't belong to this POU!" msgstr "La variable n'appartient pas à ce POU !" -#: ../controls/VariablePanel.py:77 +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "Variable:" + +#: ../controls/VariablePanel.py:72 msgid "Variables" msgstr "Variables" -#: ../controls/ProjectPropertiesPanel.py:151 +#: ../controls/ProjectPropertiesPanel.py:152 msgid "Vertical:" msgstr "Vertical :" -#: ../wxglade_hmi/wxglade_hmi.py:11 +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:37 msgid "WXGLADE GUI" msgstr "IHM WXGlade" -#: ../ProjectController.py:1302 -msgid "Waiting debugger to recover...\n" -msgstr "En attente de la mise en route du déboggueur...\n" - -#: ../editors/LDViewer.py:891 -#: ../dialogs/PouDialog.py:126 +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 msgid "Warning" msgstr "Attention" -#: ../ProjectController.py:529 +#: ../ProjectController.py:707 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "Mises en garde du generateur de code ST/IL/SFC :\n" -#: ../dialogs/SearchInProjectDialog.py:85 +#: ../dialogs/SearchInProjectDialog.py:78 msgid "Whole Project" msgstr "Tout le projet" -#: ../controls/ProjectPropertiesPanel.py:119 +#: ../controls/ProjectPropertiesPanel.py:120 msgid "Width:" msgstr "Longueur :" -#: ../dialogs/FindInPouDialog.py:86 +#: ../dialogs/FindInPouDialog.py:91 msgid "Wrap search" msgstr "Boucler" -#: ../features.py:10 +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "Écrit par" + +#: ../features.py:34 msgid "WxGlade GUI" msgstr "Interface WxGlade" -#: ../svgui/svgui.py:106 +#: ../svgui/svgui.py:142 msgid "" "You don't have write permissions.\n" "Open Inkscape anyway ?" @@ -3586,7 +3556,7 @@ "Vous n'avez pas les permissions d'écriture.\n" "Ouvrir Inkscape tout de même ?" -#: ../wxglade_hmi/wxglade_hmi.py:108 +#: ../wxglade_hmi/wxglade_hmi.py:154 msgid "" "You don't have write permissions.\n" "Open wxGlade anyway ?" @@ -3594,7 +3564,7 @@ "Vous n'avez pas les permissions d'écriture.\n" "Ouvrir wxGlade tout de même ?" -#: ../ProjectController.py:224 +#: ../ProjectController.py:371 msgid "" "You must have permission to work on the project\n" "Work on a project copy ?" @@ -3603,66 +3573,84 @@ "Travailler sur une copie du projet ?" #: ../editors/LDViewer.py:886 -msgid "You must select the block or group of blocks around which a branch should be added!" -msgstr "Vous devez sélectionné le bloc ou le group autour duquel un ebranche doit être ajoutée !" +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" +"Vous devez sélectionné le bloc ou le group autour duquel un ebranche doit " +"être ajoutée !" #: ../editors/LDViewer.py:666 msgid "You must select the wire where a contact should be added!" -msgstr "Vous devez sélectionner le fil sur lequel le contact doit être ajouté !" - -#: ../dialogs/PouNameDialog.py:45 -#: ../dialogs/SFCStepNameDialog.py:47 -#: ../dialogs/SFCStepDialog.py:118 +msgstr "" +"Vous devez sélectionner le fil sur lequel le contact doit être ajouté !" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 msgid "You must type a name!" msgstr "Vous devez saisir un nom !" -#: ../dialogs/ForceVariableDialog.py:175 +#: ../dialogs/ForceVariableDialog.py:193 msgid "You must type a value!" msgstr "Vous devez saisir une valeur !" -#: ../IDEFrame.py:419 +#: ../IDEFrame.py:438 msgid "Zoom" msgstr "Zoom" -#: ../editors/GraphicViewer.py:97 -msgid "Zoom:" -msgstr "Zoom :" - -#: ../PLCOpenEditor.py:335 +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "jours" + +#: ../PLCOpenEditor.py:343 #, python-format msgid "error: %s\n" msgstr "erreur: %s\n" -#: ../util/ProcessLogger.py:161 -#, python-format -msgid "exited with status %s (pid %s)\n" -msgstr "a quitté avec le status %s (pid %s)\n" - -#: ../PLCOpenEditor.py:393 -#: ../PLCOpenEditor.py:395 +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 msgid "file : " msgstr "fichier :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "function" msgstr "fonction" -#: ../PLCOpenEditor.py:396 +#: ../PLCOpenEditor.py:409 msgid "function : " msgstr "fonction :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "functionBlock" msgstr "Bloc fonctionnel" -#: ../PLCOpenEditor.py:396 +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "heures" + +#: ../PLCOpenEditor.py:409 msgid "line : " msgstr "ligne :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "minutes" + +#: ../dialogs/PouDialog.py:32 msgid "program" msgstr "programme" +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "secondes" + #: ../plcopen/iec_std.csv:84 msgid "string from the middle" msgstr "Caractères du milieu" @@ -3675,11 +3663,27 @@ msgid "string right of" msgstr "Caractères à droite de" -#: ../PLCOpenEditor.py:333 +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "Information de mise à jour non disponible." + +#: ../PLCOpenEditor.py:341 #, python-format msgid "warning: %s\n" msgstr "attention: %s\n" +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + #: Extra XSD strings msgid "CanFestivalSlaveNode" msgstr "Noeud esclave CanFestival" @@ -3711,15 +3715,36 @@ msgid "CAN_Driver" msgstr "Driver CAN" -msgid "CExtension" -msgstr "Extension C" +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "Config Xenomai" + +msgid "Compiler" +msgstr "Compileur" msgid "CFLAGS" msgstr "CFLAGS" +msgid "Linker" +msgstr "Linkeur" + msgid "LDFLAGS" msgstr "LDFLAGS" +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + msgid "BaseParams" msgstr "Paramètres de base" @@ -3729,24 +3754,6 @@ msgid "Enabled" msgstr "Actif" -msgid "Linux" -msgstr "Linux" - -msgid "Compiler" -msgstr "Compileur" - -msgid "Linker" -msgstr "Linkeur" - -msgid "Win32" -msgstr "Win32" - -msgid "Xenomai" -msgstr "Xenomai" - -msgid "XenoConfig" -msgstr "Config Xenomai" - msgid "BeremizRoot" msgstr "Racine de Beremiz" @@ -3762,201 +3769,206 @@ msgid "Disable_Extensions" msgstr "Disable_Extensions" -#~ msgid "Debug connect matching running PLC\n" -#~ msgstr "L'automate connecté correspond au project ouvert.\n" - -#~ msgid "File '%s' already exists!" -#~ msgstr "Le fichier '%s' existe déjà !" - -#~ msgid "Function Blocks can't be used in Transitions!" -#~ msgstr "" -#~ "Les blocs fonctionnels ne peuvent être utilisés dans des transitions" - -#~ msgid "No running PLC" -#~ msgstr "Aucun automate en cours d'exécution" - -#~ msgid "Node infos" -#~ msgstr "Propriétés du noeud" - -#~ msgid "PLC is %s\n" -#~ msgstr "L'automate est dans l'état %s\n" - -#~ msgid "Debug_mode" -#~ msgstr "Mode de débogage" - -#, fuzzy -#~ msgid "Close Project\tCTRL+SHIFT+W" -#~ msgstr "" -#~ "#-#-#-#-# Beremiz_fr_FR.po (PACKAGE VERSION) #-#-#-#-#\n" -#~ "Fermer le project\tCTRL+SHIFT+W\n" -#~ "#-#-#-#-# PLCOpenEditor_fr_FR.po (PACKAGE VERSION) #-#-#-#-#\n" -#~ "Fermer le projet\tCTRL+SHIFT+W" - -#~ msgid "New\tCTRL+N" -#~ msgstr "Nouveau\tCTRL+N" - -#~ msgid "Open\tCTRL+O" -#~ msgstr "Ouvrir\tCTRL+O" - -#, fuzzy -#~ msgid "Preview\tCTRL+SHIFT+P" -#~ msgstr "" -#~ "#-#-#-#-# Beremiz_fr_FR.po (PACKAGE VERSION) #-#-#-#-#\n" -#~ "Preview\tCTRL+SHIFT+P\n" -#~ "#-#-#-#-# PLCOpenEditor_fr_FR.po (PACKAGE VERSION) #-#-#-#-#\n" -#~ "Aperçu avant impression\tCTRL+SHIFT+S" - -#, fuzzy -#~ msgid "Print\tCTRL+P" -#~ msgstr "" -#~ "#-#-#-#-# Beremiz_fr_FR.po (PACKAGE VERSION) #-#-#-#-#\n" -#~ "Imprimer\tCTRL+P\n" -#~ "#-#-#-#-# PLCOpenEditor_fr_FR.po (PACKAGE VERSION) #-#-#-#-#\n" -#~ "Imprimer...\tCTRL+Q" - -#~ msgid "Quit\tCTRL+Q" -#~ msgstr "Quitter\tCTRL+Q" - -#~ msgid "Save\tCTRL+S" -#~ msgstr "Enregistrer\tCTRL+S" - -#~ msgid "Save as\tCTRL+SHIFT+S" -#~ msgstr "Enregistrer sous...\tCTRL+SHIFT+S" - -#~ msgid "Copy\tCTRL+C" -#~ msgstr "Copier\tCtrl+C" - -#~ msgid "Cut\tCTRL+X" -#~ msgstr "Couper\tCTRL+X" - -#~ msgid "Find\tCTRL+F" -#~ msgstr "Rechercher...\tCTRL+Z" - -#~ msgid "PLCOpenEditor\tF1" -#~ msgstr "PLCOpenEditor\tF1" - -#~ msgid "Paste\tCTRL+V" -#~ msgstr "Coller\tCTRL+V" - -#~ msgid "Redo\tCTRL+Y" -#~ msgstr "Refaire\tCTRL+Y" - -#~ msgid "Refresh\tCTRL+R" -#~ msgstr "Actualiser\tCTRL+R" - -#~ msgid "Save As...\tCTRL+SHIFT+S" -#~ msgstr "Enregistrer sous...\tCTRL+SHIFT+S" - -#~ msgid "Search in Project\tCTRL+SHIFT+F" -#~ msgstr "Rechercher dans le projet\tCTRL+SHIFT+F" - -#~ msgid "Undo\tCTRL+Z" -#~ msgstr "Défaire\tCTRL+Z" - -#~ msgid "Add a sub confnode" -#~ msgstr "Add a sub confnode" - -#~ msgid "Append " -#~ msgstr "Ajouter " - -#~ msgid "Delete this confnode" -#~ msgstr "Supprimer ce plugin" - -#~ msgid "Edit CanOpen Network with NetworkEdit" -#~ msgstr "Editer le réseau CANOpen à l'aide de NetworkEdit" - -#~ msgid "Edit Python File" -#~ msgstr "Editer le fichier Python" - -#~ msgid "Edit network" -#~ msgstr "Editer le réseau" - -#~ msgid "Enable/Disable this confnode" -#~ msgstr "Activer/Désactiver le plugin" - -#~ msgid "Please enter a name for confnode:" -#~ msgstr "Saisissez un nom pour le plugin :" - -#~ msgid "Project not created" -#~ msgstr "Le projet n'a pu être créé" - -#~ msgid "Topology" -#~ msgstr "Topologie" - -#~ msgid "Wrong URI, please check it !\n" -#~ msgstr "URI inconnue, veuillez vérifier l'adresse !\n" - -#~ msgid "Add a new data type" -#~ msgstr "Ajouter un nouveau type de données" - -#~ msgid "Add new configuration" -#~ msgstr "Ajouter une nouvelle configuration" - -#~ msgid "Add new resource" -#~ msgstr "Ajouter une nouvelle resource" - -#~ msgid "Block Types" -#~ msgstr "Types de blocs" - -#~ msgid "Delete Task" -#~ msgstr "Supprimer une tâche" - -#~ msgid "Graphic Panel" -#~ msgstr "Graphique" - -#~ msgid "Instances" -#~ msgstr "Instances" - -#~ msgid "Invalid value \"%s\" for location" -#~ msgstr "Adresse \"%s\" invalide " - -#~ msgid "Please enter configuration name" -#~ msgstr "Saisissez le nom de la configuration" - -#~ msgid "Please enter data type name" -#~ msgstr "Saisissez le nom du type de donnée" - -#~ msgid "Please enter resource name" -#~ msgstr "Saisissez le nom de la ressource" - -#~ msgid "Please enter text" -#~ msgstr "Saisissez le texte" - -#~ msgid "Plugins" -#~ msgstr "Plugins" - -#~ msgid "Create a new POU from" -#~ msgstr "Créer un nouveau POU à partir de" - -#~ msgid "Please enter POU name" -#~ msgstr "Saisissez le nom du POU" - -#~ msgid "Scaling:" -#~ msgstr "Echelle :" - -#~ msgid "X Scale:" -#~ msgstr "Echelle X :" - -#~ msgid "Y Scale:" -#~ msgstr "Echelle Y :" - -#~ msgid "No" -#~ msgstr "Non" - -#~ msgid "Yes" -#~ msgstr "Oui" - -#, fuzzy -#~ msgid "A pou with \"%s\" as name exists!" -#~ msgstr "Un POU nommé \"%s\" existe déjà !" - -#~ msgid "Close\tCTRL+Q" -#~ msgstr "Fermer\tCTRL+Q" - -#~ msgid "" -#~ "A variable is defined with \"%s\" as name. It can generate a conflict. Do " -#~ "you wish to continue?" -#~ msgstr "Une variable" - -#~ msgid "Create A New POU From" -#~ msgstr "Créer un nouveau POU à partir de" +msgid "%(codefile_name)s" +msgstr "" + +msgid "variables" +msgstr "variables" + +msgid "variable" +msgstr "variable" + +msgid "name" +msgstr "nom" + +msgid "type" +msgstr "type" + +msgid "class" +msgstr "classe" + +msgid "initial" +msgstr "initiale" + +msgid "desc" +msgstr "desc" + +msgid "onchange" +msgstr "" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "Copie de IN" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "Variable d'entrée" + +msgid "Initial value" +msgstr "Valeur initiale" + +msgid "Sampling period" +msgstr "Période d'échantillonnage" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "0 = reset" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "Temps de reset" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "Premier paramètre d'entrée" + +msgid "second input parameter" +msgstr "Second paramètre d'entrée" + +msgid "first output parameter" +msgstr "Premier paramètre de sortie" + +msgid "second output parameter" +msgstr "Second paramètre de sortie" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_hu_HU.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_hu_HU.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3947 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Gábor Véninger , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Gábor Véninger , 2017\n" +"Language-Team: Hungarian (Hungary) (https://www.transifex.com/beremiz/teams/75746/hu_HU/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: hu_HU\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" +"\n" +"Egy kezeletlen kivétel (bug) történt. Hibajelentés elmentve ide:\n" +"(%s)\n" +"\n" +"Kérem legyen szíves ezt a fájlt elküldeni erre a címre:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"A programot újra kell indítania.\n" +"\n" +"Nyomonkövetés:\n" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr "Külső" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr "Be/Ki" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr "Bemenet" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr "Helyi" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr "Kimenet" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr "Ideiglenes" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr " és %s" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr "létrehozás hibás !\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "\"%s\" Adat típus nem létezik !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "\"%s\" POU már létezik !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "\"%s\" POU nem létezik !!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" sajátmaga nem használható!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "\"%s\" konfiguráció már létezik!" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "\"%s\" konfiguráció már létezik !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "\"%s\" adat típus már létezik!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "A(z) \"%s\" elem ehhez a POU-hoz már létezik!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "A(z) \"%s\" könyvtár egy nem érvényes Beremiz projekt\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "A(z) \"%s\" egy kulcsszó. Nem használható!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "A(z) \"%s\" egy nem érvényes érték!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "A(z) \"%s\" egy nem érvényes könyvtár!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "A(z) \"%s\" egy nem érvényes azonosító!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "" +"A(z) \"%s\" már használatban van egy vagy több POU-ban. Biztos folytatni " +"akarja?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "\"%s\" POU már létezik!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "\"%s\" lépés már létezik!" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "\"%s\" érték már definiálva van!" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "\"%s\" érték nem valós tömb dimenzió!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"\"%s\" érték nem valós tömb dimenzió!\n" +"A jobb oldali érték nagyobb kell legyen mint a bal oldali." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dd" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "%s Adat Típusok" + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%s POU-k" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "%s Profil" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "%s testnek nincsenek példányai!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "%s testben nincs szöveg!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "&Elem Hozzáadása" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Bezárás" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "&Konfiguráció" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "&Adat Típus" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "&Törlés" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "&Nézet" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "&Szerkesztés" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "&Fájl" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "&Funkció" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "&Segítség" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&Licensz" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "&Program" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "&Tulajdonságok" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "&Legutóbbi Projektek" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "&Erőforrás" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} egyezés a projektben" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} egyezések a projektben" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "(%d találat)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr ", " + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr ". " + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1n" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1ó" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1p" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1mp" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "A \"%s\" nevű POU már létezik!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "Egy helyet ki kell választani!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Egy feladat ezzel a névvel már létezik!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "Egy változó a \"%s\" névvel már létezik ebben a POU-ban!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "Egy változó a \"%s\" névvel már létezik!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "Névjegy" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "Abszolut érték" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "Művelet" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "Művelet Blokk" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "Művelet Név" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "Művelet Név:" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "Művelet név %s nem létezik!" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "Műveletek" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "Műveletek:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Aktív" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "Hozzáadás" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "Művelet hozzáadás" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "" +"A beazonosított változók szinkronizált eléréséhez tartózó C forráskód " +"hozzáadása" + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "Konfiguráció Hozzáadás" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "Adattípus Hozzáadás" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "Elágazás Hozzáadás" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "IP Hozzáadás" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "POU Hozzáadás" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "Aszinkron futású Python kód Hozzáadás" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "Erőforrás hozzáadás" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "Átváltás Hozzáadás" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "Vezeték Szakasz Hozzáadás" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "Új Kezdeti Lépés Hozzáadás" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "Új elugrás hozzáadás" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "Új lépés hozzáadás" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "Egyszerű WxGlade alapú GUI hozzáadás" + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "Művelet hozzáadás" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "Elem hozzáadás" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "Új példány hozzáadás" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "Szolga hozzáadás" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "Feladat hozzáadás" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "Változó hozzáadás" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "Összeadás" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "További funkció blokkok" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "Blokk méret beállítás" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "Elrendezés" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "Minden" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "Minden fájl (*.*)|*.*|CSV files (*.csv)|*.csv" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "Már csatalakoztatva van. Kérem szétcsatlakoztatni.\n" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "Ez az elem név már létezik ebben a struktúrában: \"%s\"" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Ilyen nevű példány már létezik!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "A névváltoztatás elfogadása az összes ilyen névre." + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "Arc cosine" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "Arc sine" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "Arc tangent" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "Aritmetika" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "Tömb" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "Hozzárendelés" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "Legalább egy változót vagy kifejezést ki kell választani!" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "Szerző" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "Szerző Neve (opcionális):" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "Viszafele" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "" + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "Alaptípus:" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "Alaptípusok" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "Beremiz" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "Bináris kiválasztás (1 / 2)" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "Bit eltolás" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "Bitenként" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "Bitenkénti ÉS" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "Bitenkénti VAGY" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "Bitenkénti XOR" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "Bitenkénti invertálás" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "Blokk" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "Blokk Tulajdonságok" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Blokk neve" + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "Alja" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "" + +#: ../features.py:32 +msgid "C extension" +msgstr "" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "CANOpen hálózat" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "CANOpen szolga" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "CANOpen támogatás" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "" + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "" + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "Közép" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "IP cím megváltoztatása a hozzákötött interfészen" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "Név megváltoztatása" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "POU típus megváltoztatása" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "Port szám megváltoztatása" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "Munkakönyvtár megváltoztatása" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "Karakter sztring" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "Válasszon SVG fájlt" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "Válasszon mappát a projekt mentéshez" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "Válasszon fájlt" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "Válasszon projektet" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "Válasszon értéket a %s-nek:" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "Válasszon munkakönyvtárat" + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "A kiválasztott mappa nem tartalmaz érvényes projektet!" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "A kiválasztott mappa nem üres. Nem használható új projekthez." + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "Osztály" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "Osztály szűrő:" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "Osztály:" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "Takarítás" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Log üzenetek kitakarítása" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "Project fordítás mappa takarítása" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "Fordítási mappa kitakarítása\n" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "Hibák törlése" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "Végrehajtási Sorrend Törlése" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Bezárás" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "Alkalmazás Bezárás" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "Projekt Bezárás" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "Fül Bezárás" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "Tekercs" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "Megjegyzés" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Közösségi támogatás" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Vállalat Neve" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "Vállalat Neve (kötelező):" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "Vállalat Weboldal (opcionális):" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "Összehasonlítás" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "IEC Program fordítása C kódra...\n" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "Összefűzés" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "Konfiguráció" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "Változók konfigurálása" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "Konfiguráció" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "Konfigurációk" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Erősítse meg vagy változtassa meg a változó nevét" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "Kapcsolódás" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "Kapcsolódás a cél PLC-hez" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "Az URI: %s csatlakoztatva" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "Kapcsolat" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "Kapcsolat Tulajdonságok" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "Kapcsolódás megszakítva!\n" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "Kapcsolódás a %s-hez nem sikerült!\n" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Kapcsolat elvesztve!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "Kapcsolódás a '%s'-hez nem sikerült.\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "Csatlakozó" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "Csatlakozók:" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "Konzol" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "Konstans" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "Kapcsolat" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "Tartalom leírása (opcionális)" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "Folytatás" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "Konvertálás BCD-ből" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "Konvertálás BCD-be" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "Konvertálás dátummá" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "Konvertálás napszakká (TOD)" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "Másolás" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "POU másolás" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "Másolás bal oldali mappából jobbra" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "Másolás jobb oldali mappából balra" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "Koszinusz" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Régi %s fájlt nem lehet beimportálni." + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "Nem POU típusú objektum beillesztése nem lehetséges." + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "PLC nem indult el!\n" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "PLC nem állt le!\n" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "Hibakereső nem áll le.\n" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "HMI létrehozása" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "Új Program Szervezési Egység (POU) létrehozása" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "Új művelet létrehozása" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "Új művelek blokk létrehozása" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "Új blokk létrehozása" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "Új ág létrehozása" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "Új tekercs létrehozása" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "Új megjegyzés létrehozása" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "Új kapcsolat léterhozása" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "Új kötés létrehozása" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "Új leágazás létrehozása" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "Új leágazás vagy összefutás léterhozása" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "Új kezdeti lépés létrehozása" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "Új ugrás létrehozása" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "Új áramsin létrehozása" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "Új rung létrehozása" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "Új lépés létrehozása" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "Új átmenet létrehozása" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "Új változó létrehozása" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Köszönet" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Aktuális munka könyvtár:" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "Kivágás" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "Ciklikus" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "ELAVULT" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "DS-301 Profil" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "DS-302 Profil" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "Adattípus" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "Adattípusok" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "Adattípus konverzió" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "Dátom hozzáadás" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "Dátum és idő kivonás" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "Dátum kivonás" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "Napok:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" +"Hibakeresés nem egyezik a PLC-vel - megállítás / letöltés / újraindítás " +"szükséges, hogy engedélyezve legyen.\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "Példány hibakeresése" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "Hibakeresés: %s" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "Hibakeresés: Ismeretlen változó '%s'\n" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "Hibakeresés: Nem támogatott típus '%s'\n" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "Hibakereső" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "Hibakereső letiltva\n" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "Hibakereső kész\n" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "Hibakereső megállt.\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "Törlés" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "Elágazás törlése" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "File törlése" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "Vezeték szakasz törlése" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "Elem törlése" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "Törlés (ebben)" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Leírás" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "Leírás:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "Méretek:" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "Irány" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "Irány:" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "Közvetlenül" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "Szétkapcsolás" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "Szétkapcsolás PLC-ről" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "Szétkapcsolva" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "Elágazás" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "Osztás" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "Biztos, hogy törölni akarja a '%s' fájlt?" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "Dokumentáció" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "Kész" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "Időtartam" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "EDS fájlok (*.eds)|*.eds|Minden fájl|*.*" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "Blokk Szerkesztése" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "Tekercs Értékének Szerkesztése" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "Kötés Értékének Szerkesztése" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "Időtartam Szerkesztése" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "Lépés Szerkesztése" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "WxWidgets GUI szerkesztése WXGlade segítségével" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "Művelet blokk tulajdonságok szerkesztése" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "Tömb tulajdonságok szerkesztése" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "Megjegyzés szerkesztése" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "Fájl szerkesztése" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "Elem szerkesztése" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "Elugrás céljának szerkesztése" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "PLCGenerator által létrehozott nyers IEC kód szerkesztése" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "Lépés név szerkesztése" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "Váltás szerkesztése" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "Szerkesztő Eszköztár" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "Szerkesztő kiválasztás" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "Elemek :" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "Üres" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "Vissza/Újra Engedélyezés" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "Adjon meg egy nevet" + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "Adjon meg egy port számot" + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "Adja az kötéshez használt interfész IP címét" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "Felsorolva" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "Egyenlő" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "Hiba" + +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" +"Hiba: Legalább egy konfiguráció és egy erőforrást deklarálni kell a PLC-" +"ben!\n" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "Hiba: IEC-ről C-re fordítás hiba %d\n" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" +"Hiba az ST/IL/SFC kód generátorban:\n" +"%s\n" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "Hiba a \"%s\" mentése közben\n" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "Hiba: Szolga exportálás nem sikerült\n" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "Hiba: Nincs Master létrehozva\n" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "Hiba: Nincs lefordított PLC\n" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "Végrehajtás vezérlés:" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "Végrehajtási Sorrend:" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "Kísérleti web alapú HMI" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "Kitevő" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "Hatvány" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "CanOpen slave exportálása EDS fájlba" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "Grafikus értékek vágólapra másolása" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "Szolga exportálása" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "Kifejezés:" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "Külső" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "A megtalált változók kinyerése...\n" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "FBD" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "Sikertelen: Le kell fordítani az átküldés előtt.\n" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "Lefutóél" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "Hiba: fordító nem található.\n" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "%s Letöltése" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "Keresés" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "Következő keresése" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "Előző keresése" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "Pozíció keresése" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "Keresés:" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "Futásidejű program kényszerítet újratöltése\n" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "Kényszerített érték" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "Változó érték kényszerítése" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "Előre" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "Funkció" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "Funkcióblokk" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "Funkcióblokk" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "Funkcióblokk Típusok" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "Funkcióblokkok" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "Funkcióblokkok nem használhatók a függvényekben!" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "Funkcióblokk \"%s\" nem illeszthető be a függvénybe!!!" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "Függvények" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "Program Létrehozás" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "SoftPLC IEC-61131 ST/IL/SFC kód létrehozása...\n" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "Globális" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "Menj az aktuális értékhez" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "Grafika" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "Nagyobb" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "Nagyobb vagy egyenlő" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "Rács Felbontás:" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "HTTP interfész port:" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "Magasság:" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "Saját Könyvtár:" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "Vízszintes:" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "Órák:" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "IL" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "IP" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "IP cím nem valós!" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "SVG importálás" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "BeKi" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "Inaktív" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "\"{a1}\" és \"{a2}\" nem kompatibilis adattípusok." + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "\"%s\" és \"BOOL\" nem kompatibilis adatméretűek." + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "\"{a1}\" és \"{a2}\" nem kompatibilis méretűek" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "Kijelző" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "Kezdeti" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "Kezdeti lépés" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "Kezdőérték" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "Kezdőérték:" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "Inkscape" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "Inline" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "Bemenet" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "Bemenetek:" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "Beszúrás (ide)" + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "Az %d azonosító nem létezik!" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "Példányok:" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "Interfész" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "Megszakítás" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "Intervallum" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "Érvénytelen plcopen elem(ek)!!!" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "Ugrás" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "LD" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "A %d azonosítójú létra elem több rung-ban van használva." + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "Nyelv" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "Nyelv (opcionális):" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "Nyelv:" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" +"A legutolsó fordítás már megegyezik az aktuális céllal. Mindenképpen " +"átküldésre kerül...\n" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "WX GUI elemző indítása" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "Élő Python shell indítása" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "Bal" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "Bal ÁramSin" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "String hossza" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "Kisebb" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "Kisebb vagy egyenlő" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "Könyvtár" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "Licensz" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "Határolás" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "Összefűzés:\n" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "Helyi" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "Helyi bejegyzések" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "Helyi szolgáltatás felderítése nem sikerült!\n" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "Hely" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "Elérhető helyek:" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "10-es alapú logaritmus" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "MDNS feloldás nem sikerült a '%s'-ra\n" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "Változó Hozzárendelés" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "CANopen buszon feldezett változók hozzárendelése" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "Fő" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "Maximum" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "Maximum:" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "Memória" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "Menü Eszköztár" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "Mikroszekundum:" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "Középső" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "Ezredmásodperc" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "Minimum" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "Minimum:" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "Perc:" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "Egyéb" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "Módosító:" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "Tevékenység mozgatás le" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "Tevékenység mozgatás fel" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "Mozgatás le" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "Mozgatás fel" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "Elem mozgatás fel" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "Elem mozgatás le" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "Példány mozgatás fel" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "Feladat mozgatás le" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "Feladat mozgatás fel" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "Nézet mozgatása" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "Mozgatás fel" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "Váltózó mozgatás le" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "Változó mozgatás fel" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "Multiplexer (1 az N-hez kiválasztás)" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "Szorzás" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "Sajátgép:" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NÉV" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "Név" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "Név nem lehet üres!" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "Név:" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "Természetes alapú logaritmus" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "Negált" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "Új" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "Új elem" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "Nincs módosító" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "Nincs letöltendő PLC (sikeres volt a fordítás?)\n" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "POU \"%s\"-ban nincs semmi definiálva." + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" +"Dokumentáció nem elérhető\n" +"Hamarosan érkezik." + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "Nincs elérhető keresési eredmény." + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "Nincs ilyen SVG fájl: %s\n" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "Nem megőrzött" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "Normál" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "Nem egyenlő" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "Szekvenciák száma:" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "Numerikus" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "Változás esetén" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "Csak elemek" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "Megnyit" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "Inkscape Megnyitás" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" +"Nyílt forráskódú automatizálási keretrendszer , IEC 61131 fejlesztő " +"környezet megvalósítva folyamatosan növekvő számű bővítményekkel, és " +"rugalmas futásidejű PLC-vel." + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "Fájlkezelő megnyitása a projektek kezeléséhez" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "wxGlade Megnyitása" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "Beállítás" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "Beállítások" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "Szervezet (opcionális)" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "Másik Profil" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "Kimenet" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "PDO Fogadás" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "PDO Küldés" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "PLC :\n" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "PLC Log" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "PLC kód létrehozás nem sikerült!\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "A PLC üres vagy már el van indítva." + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "PLC nincs elindítva." + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" +"PLC szintaktikai hiba az {a1} sorban:\n" +"{a2}" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "PLCOpen fájlok (*.xml)|*.xml|Minden fájl|*.*" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "PLCOpenEditor" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" +"A PLCOpenEditor része a Beremiz projektnek.\n" +"\n" +"A beremiz egy ..." + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "PORT" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "POU Név" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "POU Név:" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "POU Típus" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "POU Típus:" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "Oldalbeállítás" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "Oldalméret (opcionális):" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "Oldal: %d" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "Szülő példány" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "Beillesztés" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "POU beillesztés" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "Keresési minta:" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "Tüske száma:" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "Kérem válasszon célt" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "Kérem adja meg a blokk nevét" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "Kérem adja meg a megjegyzés szövegét" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "Kérem adja meg a lépés nevét" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "Kérem adja meg a szöveget" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "Kérem adjon értéket a '%s' változónak:" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "Port száma 0 és 65535 közé kell essen." + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "Port száma egész szám kell legyen!" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "Áramsin" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "Áramsin Tulajdonságok" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "Előnézet" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "Előnézet:" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "Nyomtatás" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "Nyomtatási kép" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "Prioritás" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "Prioritás:" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "PLC indítási hiba: hiba %d" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "Terméknév" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "Terméknév (kötelező):" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "Termék kiadás (opcionális):" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "Termék Verzió" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "Termék Verzió (kötelező):" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "Program" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "Program sikeresen létrehozva!" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "Programok" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "A program nem használható más POU-k által!" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "Projekt" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "Projekt '%s':" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "Projekt Fájlok" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "Projektnév" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "Projektnév (kötelező):" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "Projekt Verzió (opcionális):" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" +"Projekt fájl szintaktikai hiba:\n" +"\n" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "Projekt tulajdonságok" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "Projekt fa elrendezése nem egyezik meg a confnode.xml -el {a1}!={a2}" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "Terjesztési Név" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "Tulajdonságok" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "Kiadási szolgáltatás a helyi hálózaton" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "Python kód" + +#: ../features.py:33 +msgid "Python file" +msgstr "Píthon fájl" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "Kilépés" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "Tartomány:" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "Nyers IEC kód" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "Valóban törli a '%s' csomópontot?" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "Újra" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "Referencia" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "Frissítés" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "Reguláris kifejezés" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "Reguláris kifejezések" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "Maradék (modulo)" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "Node %s eltávolítása" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "Adattípus eltávolítása" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "POU eltávolítása" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "Művelel eltávolítása" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "Elem eltávolítása" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "Bal oldali mappából a fájl eltávolítása" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "Példány eltávolítása" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "Szolga eltávolítása" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "Feladat eltávolítása" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "Változó eltávolítása" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "Átnevezés" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "Fájl lecserélése" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "Reset" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "Végrehajtási sorrend törlése" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "Perspektíva törlése" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "Keresési eredmény törlése" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "Erőforrások" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "Megőriz" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "Visszatérési Típus:" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "Jobb" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "Jobb Áramsin" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "Felfutóél" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "Forgatás balra" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "Forgatás jobbra" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "Kerekítés fel/le" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "Fut" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "Futás idejú IO bővítmények C kódjának létrehozása nem sikerült!\n" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "Futás idejű könyvtárak C forráskódjának generálása nem sikerült!\n" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "SFC" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "ST" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "Mentés" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "Mentés másként..." + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "Mentés mint" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "Mentési elérési út ugyanaz mint a projekt elérési újta!\n" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "Keresés" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "Keresés a Projektben" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "Másodpercek:" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "Mindet kiválaszt" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "Változó osztály kiválasztás:" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "Szerkesztő kiválasztás:" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "Példány kiválasztás" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "Objektum kiválasztás" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" +"A kiválasztott könyvtálr már tartalmaz egy másik projektet. Felülírjam?\n" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "Kiválasztás" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "Szolgáltatás Felfedezés" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "Elérhető szolgáltatások:" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "Beállítás" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "Balra léptetés" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "Jobbra léptetés" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "PLCGenerator által létrehozott IEC kód mutatása" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "Master mutatása" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "Mutassa a config_utils által létrehozott Master-t" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "Kód mutatása" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "Szinusz" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "Egyes" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "Forrás nem változott, nincs újrafordítás.\n" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "PLC indítása" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "Fordítás indítása a %s-ban\n" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "Elindult" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "PLC elindul\n" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "Státusz Eszköztár" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "Lépés" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "Állj" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "PLC Állj" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "Futó PLC megállítása" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "Megállt" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "Hibakereső megállítása...\n" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "Struktúra" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "Altartomány" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "Kivonás" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "Sikeres fordítás.\n" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "Perspektíva váltás" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "Szintaktikai hiba a keresési reguláris kifejezés mintában." + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "TÍPUS" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "Tangens" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "Feladat" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "Feladatok:" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "Ideiglenes" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" +"A legjobb hely a kérdések feltevésére a Beremiz/PLCOpenEditor kapcsán\n" +"a projekt levelezési listája: beremiz-devel@lists.sourceforge.net\n" +"\n" +"Ez a fő támogatási csatorna.\n" +"Az üzenet küldéshez szükséges a regisztráció a levelező listára.\n" +"\n" +"Itt lehet regisztrálni:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "Változások történtek, menteni akarja?" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "Ez az opció még nem létezik!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "Ütem: %d" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "Idő" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "Idő összeadás" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "Idő összefűzés" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "Idő osztás" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "Idő szorzás" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "Idő kivonás" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "Napszak TOD összeadás" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "Napszak TOD kivonás" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "Érték átbillentés" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "Felső" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "Átvitel" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "PLC átvitel" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "Átvitel sikeresen befejezve.\n" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "Átvitel nem sikerült\n" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "Átváltás" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "Triggerelés" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "Típus" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "Típus konvertálás" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "Típus:" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "Vissza" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "Ismeretlen" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "Névtelen" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "Névtelen %d" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "Felhasználói Adat Típus" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "Érték" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "Értékek:" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "Változó" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "Változó Tulajdonságok" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "Változó osztály" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "Változó nem ehhez a POU-hoz tartozik!" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "Változó:" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "Változók" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "Függőleges" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "WXGLADE GUI" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "Figyelmeztetés" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "Teljes Projekt" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "Szélesség:" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "Írta" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "" + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "" + +msgid "CAN_Device" +msgstr "" + +msgid "CAN_Baudrate" +msgstr "" + +msgid "NodeId" +msgstr "" + +msgid "Sync_Align" +msgstr "" + +msgid "Sync_Align_Ratio" +msgstr "" + +msgid "CanFestivalNode" +msgstr "" + +msgid "Sync_TPDOs" +msgstr "" + +msgid "CanFestivalInstance" +msgstr "" + +msgid "CAN_Driver" +msgstr "" + +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "" + +msgid "XenoConfig" +msgstr "" + +msgid "Compiler" +msgstr "" + +msgid "CFLAGS" +msgstr "" + +msgid "Linker" +msgstr "" + +msgid "LDFLAGS" +msgstr "" + +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + +msgid "BaseParams" +msgstr "" + +msgid "IEC_Channel" +msgstr "IEC_Csatorna" + +msgid "Enabled" +msgstr "Engedélyezve" + +msgid "BeremizRoot" +msgstr "BeremizGyökér" + +msgid "TargetType" +msgstr "CélTípus" + +msgid "Libraries" +msgstr "Könyvtárak" + +msgid "URI_location" +msgstr "URI helye" + +msgid "Disable_Extensions" +msgstr "Bővítmények Tiltása" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)s" + +msgid "variables" +msgstr "változók" + +msgid "variable" +msgstr "változó" + +msgid "name" +msgstr "név" + +msgid "type" +msgstr "típus" + +msgid "class" +msgstr "osztály" + +msgid "initial" +msgstr "kezdő" + +msgid "desc" +msgstr "leírás" + +msgid "onchange" +msgstr "változáskor" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "0 - kézi , 1 - automata" + +msgid "Process variable" +msgstr "Folyamat változó" + +msgid "Set point" +msgstr "Alapjel" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "Arányos tag állandó" + +msgid "Reset time" +msgstr "Reset idő" + +msgid "Derivative time constant" +msgstr "Differenciáló tag állandó" + +msgid "PV - SP" +msgstr "PV - SP" + +msgid "FB for integral term" +msgstr "FB az integráló taghoz" + +msgid "FB for derivative term" +msgstr "FB a differenciáló taghoz" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" +"The PID (arányos, integráló, differenciáló) funkció blokk megvalósítja a " +"klasszikus 3 tagó zárt hurkú szabályzót." + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "Rámpázás ideje" + +msgid "BUSY = 1 during ramping period" +msgstr "BUSY = 1 a rámpázási periódus közben" + +msgid "Elapsed time of ramp" +msgstr "Rámpázásból eltelt idő" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "első bemeneti paraméter" + +msgid "second input parameter" +msgstr "második bemeneti paraméter" + +msgid "first output parameter" +msgstr "első kimeneti paraméter" + +msgid "second output parameter" +msgstr "második kimeneti paraméter" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "belső állapot: 0-törlés, 1-számlálás, 2-beállítás" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" +"Különböző hosszúságú kimeneti impulzusok előállítására az impulzus időzítő " +"használható." + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_it_IT.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_it_IT.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3927 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Luca Magnabosco , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Luca Magnabosco , 2017\n" +"Language-Team: Italian (Italy) (https://www.transifex.com/beremiz/teams/75746/it_IT/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: it_IT\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr " External" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr " InOut" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr " Input" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr " Local" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr " Output" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr " Temp" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr " e %s" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr " generazione fallita!\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "Il tipo di dato \"%s\" non esiste !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "La POU \"%s\" esiste già !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "La POU \"%s\" non esiste !!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" non può utilizzare se stesso!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "La config \"%s\" esiste già!" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "La configurazione \"%s\" esiste già !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "Il tipo di dato \"%s\" esiste già!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "L'elemento \"%s\" per questa pou esiste già!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "La cartella \"%s\" non è un valido progetto Beremiz\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "\"%s\" è una parola chiave. Non può essere usata!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "\"%s\" è un valore non valido!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "\"%s\" non è una cartella valida!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "\"%s\" non è un identificatore valido!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "%s\" è utilizzato da una o più POU. Vuoi continuare?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "Il POU \"%s\" esiste già!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "Il passo \"%s\" esiste già!" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "Il valore \"%s\" è già definito!" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "Il valore \"%s\" non è una dimensione valida per un array!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"Il valore \"%s\" non rappresenta una dimensione valida per un array!\n" +"Il valore di destra deve essere maggiore del valore di sinistra." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "Funzione \"{a1}\" cancellata nel POU \"{a2}\": Nessun input connesso" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" è già utilizzato da \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "La risorsa \"{a1}\" esiste già nella configurazione di \"{a2}\" !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "La risorsa \"{a1}\" non esiste nella configurazione di \"{a2}\" !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03g ms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%d d" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%d h" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%d m" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%d ms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%d s" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "%s Data Types" + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%s POUs" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "%s Profilo" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "Il corpo di %s non contiene istanze!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "Il corpo di %s non contiene testo!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "&Aggiungi Elemento" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Chiudi" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "&Configurazione" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "&Data Type" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "&Cancella" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "&Visualizzazione" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "&Modifica" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "&File" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "&Funzione" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "&Help" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&Licenza" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "&Programma" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "&Proprietà" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "Progetti &recenti" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "&Risorse" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "{a1}' - {a2} corrispondenza nel progetto" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} corrisponde nel progetto" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' si trova in {a2}\n" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "(%d corrisponde)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr ", " + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr ". " + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1d" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"Una POU contiene un elemento con nome \"%s\". Questo può causare un " +"conflitto.Vuoi continuare?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "Esiste già una POU con nome \"%s\"!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "Esiste già un figlio con nome \"{a1}\" -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "Deve essere selezionata una locazione!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Esiste già un task con lo stesso nome!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "Una variabile con \"1%s\" come nome è già esistente in questo POU!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "Esiste già una variabile con il nome \"%s\"!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "Informazioni" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "Numero assoluto" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "Azione" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "Blocco azione" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "Nome dell'azione" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "Nome dell'azione:" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "L'azione con il nome %s non esiste!" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "Azioni" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "Azioni:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Attivo" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "Aggiungi" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "Aggiungi Azione" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "" +"Aggiungi codice C con accesso in modo sincrono alle variabili localizzate." + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "Aggiungi Configurazione" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "Aggiungi DataType" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "Aggiuni un ramo divergente" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "Aggiungi IP" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "Aggiungi POU" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "Aggiungi codice Python eseguito asincronomamente" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "Aggiungi Risorsa" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "Aggiungi Transazione" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "Aggiungi un segmento di cavo" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "Aggiungi un nuovo salto iniziale" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "Aggiungi un nuovo salto" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "Aggiungi un nuovo passo" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "Aggiungi una semplice GUI basata su WxGlade." + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "Aggiungi azione" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "Aggiungi elemento" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "Aggiungi instanza" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "Aggiungi slave" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "Aggiungi task" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "Aggiungere variabile" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "Somma" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "Blocchi funzione aggiuntivi" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "Modificare la dimensione del blocco" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "Allineamento" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "Tutti" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "Tutti i file (*.*)|*.*|CSV file (*.csv)|*.csv" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "Già connesso. Per favore disconnettere\n" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "Un elemento con nome \"%s\" esiste già in questa struttura!" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Un instanza con lo stesso nome esiste già!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "Applicare la modifica del nome a tutte le istanze con lo stesso nome" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "Arcocoseno" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "Arcoseno" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "Arcotangente" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "Aritmetica" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "Array" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "Assegnazione" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "Deve essere selezionata almeno una variabile o un'espressione!" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "Autore" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "Nome autore (opzionale):" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "Indietro" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "Nome di dominio scorretto (circolare) a" + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "Nome di dominio scorretto a " + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "Dimensione della locazione sbagliata; %s" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "Tipo base:" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "Tipi base" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "Beremiz" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "Bit-shift" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "Bit a bit" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "AND bit a bit" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "OR bit a bit" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "XOR bit a bit" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "Inversione bit a bit" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "Blocco" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "Proprietà blocco" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Nome blocco" + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "Sotto" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "Rotto" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "Compila" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "Cartella di compilazione già vuota\n" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "Compila il progetto nella cartella di compilazione" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "La compilazione C si è bloccata inaspettatamente !\n" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "Compilazione C fallita.\n" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "Codice C" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "Codice C generato con successo.\n" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "Compilazione C fallita.\n" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "La compilazione C di %s è fallita,\n" + +#: ../features.py:32 +msgid "C extension" +msgstr "Estensione C" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "Ringraziamenti" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "Rete CANOpen" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "Slave CANOpen" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "Supporto CANopen" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "" + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "" + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "Cambiare Nome" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "Stringa di caratteri" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "Scegliere un file SVG" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "Scegliere una cartella per salvare il progetto" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "Scegliere n file" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "Scegliere un progetto" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "Scegliere un valore per %s:" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "Selezionare una cartella di lavoro " + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "Classe" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "Filtro Classi:" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "Classe:" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "Pulisci" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Cancella i messaggi di log" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "Pulisci la cartella di compilazione del progetto" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "Cancellando la cartella di compilazione\n" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "Cancella gli Errori" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "Pulisci l'Ordine di Esecuzione" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Chiusi" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "Chiudi Applicazione" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "Chiudi Progetto" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "Chiudi Tab" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "Bobina" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "Commento" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Supporto della community" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Nome dell'azienda" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "Nome dell'azienda (necessario):" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "Indirizzo web dell'azienda (opzionale):" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "Comparazione" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "Compilando il IEC Programma IEC in codice C...\n" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "Concatenazione" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "Configurazione" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "Configurazione varialibi" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "Configurazione" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "Configurazioni" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Conferma o cambia il nome della variabile" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "Connetti" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "Connetti al PLC target" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "Connetti all URI: %s" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "Connessione" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "Proprietà della Connessione" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "Connessione cancellata!\n" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "Connessione a %s fallita!\n" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Connessione persa!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "Connessione a '%s' fallita.\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "Connettore" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "Connettori:" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "Console" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "Costante" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "Contatto" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "Descrizione del contenuto (opzionale):" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "Continuazione" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "Conversione da BCD" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "Conversione a BCD" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "Conversione a data" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "Conversione a 'ora del giorno'" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "Copia" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "Copia POU" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "Copia il file dalla cartella di sinistra a quella di destra" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "Copia il file dalla cartella di destra a quella di sinistra" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "Coseno" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" +"Non è possibile aggiungere un figlio \"{a1}\",tipo {a2}:\n" +"{a3}\n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Impossibile importare il vecchio file %s." + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" +"Impossibile caricare i parametri confnode di base {a1} :\n" +" {a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" +"Impossibile caricare i parametri confnode {a1} :\n" +" {a2}" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "Impossibile incollare un oggetto non-POU." + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "Impossibile avviare il PLC !\n" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "Impossibile arrestare il PLC !\n" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "Impossibile arrestare il debugger.\n" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "Creare HMI" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "Creare un nuovo POU" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "Creare una nuova azione" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "Creare un nuovo blocco azione" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "Creare un nuovo blocco" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "Creare una nuova diramazione" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "Creare una nuovo relè" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "Creare un nuovo commento" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "Creare una nuova connessione" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "Creare un nuovo contatto" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "Creare una nuova divergenza" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "Creare una nuova divergenza o convergenza" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "Creare un nuovo passo iniziale" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "Creare un nuovo salto" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "Creare una nuova barra di alimentazione" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "Crea un nuovo anello" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "Creare un nuovo passo" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "Creare una nuova transizione" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "Creare una nuova variabile" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Ringraziamenti" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Cartella di lavoro corrente :" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "Taglia" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "Ciclico" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "DEPRECATO" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "Profilo DS-301" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "Profilo DS-302" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "Tipo di dato" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "Tipi di dato" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "Conversione tipi di dato" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "Addizione della data" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "Sottrazione tra data e tempo" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "Sottrazzione della data" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "Giorni:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" +"Il debug non corrisponde al PLC arresta/trasferisci/avvia per riabilitare\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "Istanza di Debug" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "Debug: %s" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "Debug: Variabile sconosciuta '%s'\n" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "Debug: Tipo non supportato per il debug '%s'\n" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "Debugger" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "Debugger disabilitato\n" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "Debugger pronto\n" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "Debugger arrestato.\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "Eliminare" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "Eliminare la ramificazione divergente" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "Eliminare File" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "Eliminare un segmento di filo" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "Eliminare elemento" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "Eliminazione (nei limiti)" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "Tipo di derivazione:" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Descrizione" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "Descrizione:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "Dimensioni:" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "Direzione" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "Direzione:" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "Direttamente" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "Disconnetti" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "Disconnetti dal PLC" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "Disconnesso" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "Divergenza" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "Divisione" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "Vuoi veramente rimuovere il file '%s'?" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "Documentazione" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "Fatto" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "Durata" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "EDS files (*.eds)|*.eds|All files|*.*" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "Edita il Blocco" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "Edita i valori del relè" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "Edita i valori del contatto" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "Edita la durata" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "Edita il passo" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "Edita una GUI WxWidgets con WXGlade" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "Edita le proprietà del blocco azione" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "Edita le proprietà del tipo array" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "Edita commento" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "Edita file" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "Edita elemento" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "Edita la destinazione del salto" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "" +"Edita il codice IEC grezzo aggiunto al codice generato da PLCGenerator" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "Edita il nome del passo" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "Edita transizione" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "Editor ToolBar" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "Selezione dell'editor" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "Elementi :" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "Vuoto" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "Abilita Annulla/Ripeti" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "Inserisci un nome " + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "Inserisci il numero di una porta " + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "Inserisci l'IP dell'interfaccia da collegare" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "Enum" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "Uguale a" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "Errore" + +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" +"Errore : Almeno una configurazione ed una risorsa devono essere dichiarate " +"nel PLC!\n" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "Errore : il compilatore da IEC a C ha ritornato il codice %d\n" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "" + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "Salto" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "LD" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "Lingua" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "Lingua (opzionale):" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "Lingua:" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "Sinistra" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "Barra di potenza di sinistra" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "Lunghezza della stringa" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "Inferiore a" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "Inferiore o uguale a" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "Libreria" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "Licenza" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "Limitazione" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "Locale" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "Valori locali" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "Master" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "Massimo" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "Massimo:" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "Memoria" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "Microsecondi:" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "Centro" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "Millisecondi:" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "Minimo" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "Minimo:" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "Minuti:" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "Miscellanea" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "Modificatore:" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "Azione muovi verso il basso" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "Azione muovi verso l'alto" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "Muovi verso il basso" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "Muovi elemento verso il basso" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "Muovi elemento verso l'alto" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "Muovi l'istanza versio il basso" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "Muovi l'istanza verso l'alto" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "Muovi il task verso il basso" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "Muovi il task verso l'alto" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "Muovi la vista" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "Muovi vero l'alto" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "Muovi la variabile verso il basso" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "Muovi la variabile verso l'alto" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "Moltiplicatore (seleziona 1 of N)" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "Moltiplicazione" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "Il mio Computer:" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NOME" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "Nome" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "Il nome non deve essere vuoto!" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "Nome:" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "Logaritmo naturale" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "Negato" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "Nuovo" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "Nuovo elemento" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "No modificatore" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "" + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "Normale" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "Non uguale a" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "Numero di sequenze:" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "Numerico" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "Solo elementi" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "Apri" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "Apri Inkscape" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "Apri wxGlade" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "Opzione" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "Opzioni" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "Organizzazione (opzionale):" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "Altro profilo" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "Output" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "PLC :\n" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "PLC Log" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "Generazione del codice PLC fallita !\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "PLC vuoto o già avviato." + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "PLC non avviato." + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "PLCOpenEditor" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "PORTA" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "Nome POU" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "Nome POU:" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "Tipo POU" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "Tipo POU:" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "Impostazioni pagina" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "Dimensione pagina ()opzionale:" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "Pagina: %d" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "Istanza padre" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "Incolla" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "Incolla POU" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "Pattern da cercare:" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "Numero di pin:" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "Per favore indicare un target" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "Stampa" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "Priorità" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "Priorità:" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "Problema durante l'avvio del PLC : errore %d" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "Nome prodotto" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "Nome prodotto (necessario):" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "Programma" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "Programmi" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "Progetto" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "" + +#: ../features.py:33 +msgid "Python file" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "La transizione SFC nel POU \"%s\" deve essere connessa." + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "ST" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "ST files (*.st)|*.st|All files|*.*" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "SVG files (*.svg)|*.svg|All files|*.*" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "SVGUI" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "Salva" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "Salva come" + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "Salva come" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "Il percorso scelto per il salvataggio è lo stesso di un progetto! \n" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "Cerca in" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "Cerca" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "Cerca nel Progetto" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "Secondi:" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "Seleziona tutto" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "Seleziona una classe per la variabile:" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "Seleziona un editor:" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "Seleziona un'istanza" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "Seleziona un oggetto" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" +"La cartella selezionata contiene già un altro progetto. Sovrascrivere? \n" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "Selezione" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "Selezione Convergenza" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "Selezione Divergenza" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "Scoperta Servizi" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "Servici disponibili:" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "Imposta" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "Trasla a sinistra" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "Trasla a destra" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "Mostra il codice IEC generato da PLCGenerator" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "Mostra il Master" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "Mostra il Master generato da config_utils" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "Mostra il codice" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "Seno" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "Singolo" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "I file sorgente non sono cambiati, compilazione non necessaria.\n" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "Radice quadrata (base 2)" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "Blocchi funzione standard" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "Avvia il PLC" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "Avvia la compilazione in %s\n" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "Avviato" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "Avviando il PLC\n" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "Barra di Stato" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "Passo" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "Arresto" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "Arresto PLC" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "Arresto del PLC in esecuzione" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "Arrestato" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "Arrestando il debugger...\n" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "Struttura" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "Sottrazione" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "Compilato con successo.\n" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "Cambia layout grafico" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "Errore di sintassi nella 'regular expression' usata per la ricerca!" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "TIPO" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "Tangente" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "Task" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "Tasks:" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "Temp" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" +"Il file '%s' esiste già.\n" +"Vuoi sovrascriverlo?" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "Il gruppo di blocchi deve essere coerente!" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "Ci sono dei cambiamenti, vuoi salvare?" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "Questa opzione non è ancora disponibile" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "Tick: %d" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "Tempo" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "Addizione del tempo" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "Concatenazion del tempo" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "Divisione del tempo" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "Moltiplicazione del tempo" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "Sottrazione del tempo" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "Trasferimento" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "Trasferimento PLC" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "Trasferimento completato con successo.\n" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "Trasferimento fallito\n" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "Transizione" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "Nome della transizione" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "Nome della transizione:" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "La transizione con il nome %s non esiste!" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "Transizioni" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "Tradotto da" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "Yipo" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "Tipo e derivati" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "Tipo:" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "Impossibile definire il PDO mapping per il nodo %02x" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "Tipo di POU \"%s\" non definito" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "Annulla" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "Sconosciuto" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "Variabile \"%s\" sconosciuta per questo POU!" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "Senza_nome" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "Senza_nome%d" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "Dimensione del dato non riconosciuta: \"%s\"" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "Tipi definiti dall'utente" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "Tipo Utente" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "POU definiti dall'Utente" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "Valore" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "Valori:" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "Variabile" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "Proprietà della variabile" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "Classe della variabile" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "La variabile non appartiene a questo POU!" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "Variabile:" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "Variabili" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "Verticale:" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "WAMP connessione timeout" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "WAMP connessione a '1%s' fallita.\n" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "WAMP importazione fallita:" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "WXGLADE GUI" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "Warning" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "Warnings nel ST/IL/SFC generatore di codice:\n" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "Intero Progetto" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "Largezza:" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "Scritto da" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "WxGlade GUI" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" +"Non il permesso di scrittura.\n" +"Aprire Inkscape comunque?" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" +"Non hai il permesso di scrittura.\n" +"Aprire wxGlade comunque?" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" +"Devi avere il permesso di lavorare al progetto\n" +"Vuoi lavorare con una copia del progetto ?" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" +"Devi selezionare il blocco o il gruppo di blocchi attorno al quale il ramo " +"deve essere aggiunto." + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "Devi selezionare un filo dove un contatto puo' essere aggiunto!" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "Devi digitare un nome!" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "Devi digitare un valore!" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "Zoom" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "giorni" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "errore: %s\n" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "uscito con stato {a1} (pid {a2})\n" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "file : " + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "funzione" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "funzione : " + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "blocco funzione" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "ore" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "linea: " + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "millisecondi" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "minuti" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "programma" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "secondi" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "stringa dal centro" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "stringa a sinistra di" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "stringa a destra di" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "aggiornamento info non disponibile." + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "warning: %s\n" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "{a1} \"{a2}\" non può essere copiato come {a3}." + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" +"Il file XML {a1} non rispetta lo schema XSD alla linea %{a2}:\n" +"{a3}" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "CanFestivalSlaveNode" + +msgid "CAN_Device" +msgstr "CAN_Device" + +msgid "CAN_Baudrate" +msgstr "CAN_Baudrate" + +msgid "NodeId" +msgstr "NodeId" + +msgid "Sync_Align" +msgstr "Sync_Align" + +msgid "Sync_Align_Ratio" +msgstr "Sync_Align_Ratio" + +msgid "CanFestivalNode" +msgstr "CanFestivalNode" + +msgid "Sync_TPDOs" +msgstr "Sync_TPDOs" + +msgid "CanFestivalInstance" +msgstr "CanFestivalInstance" + +msgid "CAN_Driver" +msgstr "CAN_Driver" + +msgid "Generic" +msgstr "Generico" + +msgid "Command" +msgstr "Comando" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "XenoConfig" + +msgid "Compiler" +msgstr "Compilatore" + +msgid "CFLAGS" +msgstr "CFLAGS" + +msgid "Linker" +msgstr "Linker" + +msgid "LDFLAGS" +msgstr "LDFLAGS" + +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + +msgid "BaseParams" +msgstr "ParametriBase" + +msgid "IEC_Channel" +msgstr "IEC_Channel" + +msgid "Enabled" +msgstr "Abilitato" + +msgid "BeremizRoot" +msgstr "BeremizRoot" + +msgid "TargetType" +msgstr "TargetType" + +msgid "Libraries" +msgstr "Librerie" + +msgid "URI_location" +msgstr "URI_location" + +msgid "Disable_Extensions" +msgstr "Disabilita_Estensioni" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)s" + +msgid "variables" +msgstr "variabili" + +msgid "variable" +msgstr "variabile" + +msgid "name" +msgstr "nome" + +msgid "type" +msgstr "tipo" + +msgid "class" +msgstr "classe" + +msgid "initial" +msgstr "iniziale" + +msgid "desc" +msgstr "descrizione" + +msgid "onchange" +msgstr "su_fronte" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" +"Il function block RAMP e' modellato sul esempio fornito nello standard." + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "" + +msgid "second input parameter" +msgstr "" + +msgid "first output parameter" +msgstr "" + +msgid "second output parameter" +msgstr "" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_ko_KR.po --- a/i18n/Beremiz_ko_KR.po Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/Beremiz_ko_KR.po Tue Jan 30 16:06:58 2018 +0100 @@ -1,49 +1,25 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: Beremiz_Korean_Version\n" +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Andrey Skvortsov , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-09-07 01:17+0200\n" -"PO-Revision-Date: 2012-09-07 01:38+0100\n" -"Last-Translator: Laurent BESSARD \n" -"Language-Team: LinuxIT \n" -"Language: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Andrey Skvortsov , 2017\n" +"Language-Team: Korean (Korea) (https://www.transifex.com/beremiz/teams/75746/ko_KR/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Poedit-Language: Korean\n" -"X-Poedit-Country: KOREA, REPUBLIC OF\n" -"X-Poedit-SourceCharset: utf-8\n" -"X-Poedit-Language: Korean\n" -"X-Poedit-Country: KOREA, REPUBLIC OF\n" -"X-Poedit-SourceCharset: utf-8\n" - -#: ../PLCOpenEditor.py:520 -msgid "" -"\n" -"An error has occurred.\n" -"\n" -"Click OK to save an error report.\n" -"\n" -"Please be kind enough to send this file to:\n" -"edouard.tisserant@gmail.com\n" -"\n" -"Error:\n" -msgstr "" -"\n" -"알 수 없는 에러가 발생했습니다.\n" -"\n" -"OK 버튼을 눌러 에러 리포트를 저장하세요.\n" -"\n" -"edouard.tisserant@gmail.com 주소로 에러 리포트를 첨부하여 보내주세요\n" -"\n" -"에러:\n" - -#: ../Beremiz.py:1071 +"Language: ko_KR\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 #, python-format msgid "" "\n" @@ -53,223 +29,156 @@ "Please be kind enough to send this file to:\n" "beremiz-devel@lists.sourceforge.net\n" "\n" -"You should now restart Beremiz.\n" +"You should now restart program.\n" "\n" "Traceback:\n" msgstr "" -"\n" -"원인을 알 수 없는 에러가 발생하였습니다. 버그 리포트 저장 위치 :\n" -"(%s)\n" -"\n" -"버그 리포트 파일을 beremiz-devel@lists.sourceforge.net으로\n" -"전송해주시면 감사하겠습니다.\n" -"\n" -"베레미즈를 다시 시작해주세요.\n" -"\n" -"에러 위치:\n" - -#: ../controls/VariablePanel.py:77 + +#: ../controls/VariablePanel.py:72 msgid " External" msgstr " 외부" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " InOut" msgstr " 입출력" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " Input" msgstr " 입력" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid " Local" msgstr " 로컬" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " Output" msgstr " 출력" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid " Temp" msgstr " 임시" -#: ../PLCOpenEditor.py:530 -msgid " : " -msgstr " :" - -#: ../dialogs/PouTransitionDialog.py:94 -#: ../dialogs/PouActionDialog.py:91 -#: ../dialogs/PouDialog.py:111 -#: ../dialogs/SFCTransitionDialog.py:144 +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 #, python-format msgid " and %s" msgstr " , %s" -#: ../ProjectController.py:890 +#: ../ProjectController.py:1151 msgid " generation failed !\n" msgstr "생성 실패!\n" -#: ../plcopen/plcopen.py:1051 +#: ../plcopen/plcopen.py:886 #, python-format msgid "\"%s\" Data Type doesn't exist !!!" msgstr "\"%s\" 존재하지 않는 데이터 타입!!!" -#: ../plcopen/plcopen.py:1069 +#: ../plcopen/plcopen.py:904 #, python-format msgid "\"%s\" POU already exists !!!" msgstr "\"%s\" POU가 이미 존재합니다!!!" -#: ../plcopen/plcopen.py:1090 +#: ../plcopen/plcopen.py:925 #, python-format msgid "\"%s\" POU doesn't exist !!!" msgstr "\"%s\" POU가 존재하지 않습니다!!!" -#: ../editors/Viewer.py:234 +#: ../editors/Viewer.py:247 #, python-format msgid "\"%s\" can't use itself!" msgstr "\"%s\": 자신을 사용 할 수 없습니다!" -#: ../IDEFrame.py:1706 -#: ../IDEFrame.py:1725 +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 #, python-format msgid "\"%s\" config already exists!" msgstr "\"%s\" 설정(config)이 이미 존재합니다!" -#: ../plcopen/plcopen.py:315 +#: ../plcopen/plcopen.py:472 #, python-format msgid "\"%s\" configuration already exists !!!" msgstr "\"%s\" 설정(configuration)이 이미 존재합니다!" -#: ../IDEFrame.py:1660 +#: ../IDEFrame.py:1605 #, python-format msgid "\"%s\" data type already exists!" msgstr "\"%s\" 데이터 타입이 이미존재합니다!" -#: ../PLCControler.py:2040 -#: ../PLCControler.py:2044 -#, python-format -msgid "\"%s\" element can't be pasted here!!!" -msgstr "\"%s\" 항목을 이곳에 붙여넣기 할 수 없습니다!!!" - -#: ../editors/TextViewer.py:305 -#: ../editors/TextViewer.py:325 -#: ../editors/Viewer.py:252 -#: ../dialogs/PouTransitionDialog.py:105 -#: ../dialogs/ConnectionDialog.py:150 -#: ../dialogs/PouActionDialog.py:102 -#: ../dialogs/FBDBlockDialog.py:162 +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 #, python-format msgid "\"%s\" element for this pou already exists!" msgstr "이 POU에 \"%s\" 항목이 이미 존재합니다!" -#: ../Beremiz.py:894 +#: ../BeremizIDE.py:897 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "\"%s\" 베레미즈 프로젝트 폴더가 아닙니다\n" -#: ../plcopen/structures.py:106 -#, python-format -msgid "\"%s\" function cancelled in \"%s\" POU: No input connected" -msgstr "\"%s\" 펑션이 취소 되었습니다 \"%s\" POU: 연결된 입력 없음" - -#: ../controls/VariablePanel.py:656 -#: ../IDEFrame.py:1651 -#: ../editors/DataTypeEditor.py:548 -#: ../editors/DataTypeEditor.py:577 -#: ../dialogs/PouNameDialog.py:49 -#: ../dialogs/PouTransitionDialog.py:101 -#: ../dialogs/SFCStepNameDialog.py:51 -#: ../dialogs/ConnectionDialog.py:146 -#: ../dialogs/FBDVariableDialog.py:199 -#: ../dialogs/PouActionDialog.py:98 -#: ../dialogs/PouDialog.py:118 -#: ../dialogs/SFCStepDialog.py:122 -#: ../dialogs/FBDBlockDialog.py:158 +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 #, python-format msgid "\"%s\" is a keyword. It can't be used!" msgstr "\"%s\" 는 키워드 입니다. 사용 할 수 없습니다!" -#: ../editors/Viewer.py:240 -#, python-format -msgid "\"%s\" is already used by \"%s\"!" -msgstr "\"%s\" 는 \"%s\" 에서 이미 사용중입니다!" - -#: ../plcopen/plcopen.py:2786 +#: ../plcopen/plcopen.py:2417 #, python-format msgid "\"%s\" is an invalid value!" msgstr "\"%s\" 유효하지 않은 값입니다!" -#: ../PLCOpenEditor.py:362 -#: ../PLCOpenEditor.py:399 +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 #, python-format msgid "\"%s\" is not a valid folder!" msgstr "\"%s\" 유효하지 않은 폴더입니다!" -#: ../controls/VariablePanel.py:654 -#: ../IDEFrame.py:1649 -#: ../editors/DataTypeEditor.py:572 -#: ../dialogs/PouNameDialog.py:47 -#: ../dialogs/PouTransitionDialog.py:99 -#: ../dialogs/SFCStepNameDialog.py:49 -#: ../dialogs/ConnectionDialog.py:144 -#: ../dialogs/PouActionDialog.py:96 -#: ../dialogs/PouDialog.py:116 -#: ../dialogs/SFCStepDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:156 +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 #, python-format msgid "\"%s\" is not a valid identifier!" msgstr "\"%s\"는 유효하지 않은 식별자입니다!" -#: ../IDEFrame.py:214 -#: ../IDEFrame.py:2445 -#: ../IDEFrame.py:2464 -#, python-format -msgid "\"%s\" is used by one or more POUs. It can't be removed!" -msgstr "\"%s\" 는 현재 하나 이상의 POU에서 사용중입니다. 제거할 수 없습니다!" - -#: ../controls/VariablePanel.py:311 -#: ../IDEFrame.py:1669 -#: ../editors/TextViewer.py:303 -#: ../editors/TextViewer.py:323 -#: ../editors/TextViewer.py:360 -#: ../editors/Viewer.py:250 -#: ../editors/Viewer.py:295 -#: ../editors/Viewer.py:312 -#: ../dialogs/ConnectionDialog.py:148 -#: ../dialogs/PouDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:160 +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 #, python-format msgid "\"%s\" pou already exists!" msgstr "\"%s\" POU는 이미 존재합니다!" -#: ../plcopen/plcopen.py:346 -#, python-format -msgid "\"%s\" resource already exists in \"%s\" configuration !!!" -msgstr "\"%s\" 리소스는 \"%s\" 설정(configuration)에 이미 존재합니다 !!!" - -#: ../plcopen/plcopen.py:362 -#, python-format -msgid "\"%s\" resource doesn't exist in \"%s\" configuration !!!" -msgstr "\"%s\" 리소스를 \"%s\" 설정(configuration)에서 찾을 수 없습니다 !!!" - -#: ../dialogs/SFCStepNameDialog.py:57 -#: ../dialogs/SFCStepDialog.py:128 +#: ../dialogs/SFCStepNameDialog.py:58 #, python-format msgid "\"%s\" step already exists!" msgstr "\"%s\" 스텝이 이미 생성되었습니다!" -#: ../editors/DataTypeEditor.py:543 +#: ../editors/DataTypeEditor.py:550 #, python-format msgid "\"%s\" value already defined!" msgstr "\"%s\" 이미 정의된 값이 있습니다!" -#: ../editors/DataTypeEditor.py:719 -#: ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 #, python-format msgid "\"%s\" value isn't a valid array dimension!" msgstr "\"%s\" 값은 올바른 배열 차원이 아닙니다!" -#: ../editors/DataTypeEditor.py:726 -#: ../dialogs/ArrayTypeDialog.py:103 +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 #, python-format msgid "" "\"%s\" value isn't a valid array dimension!\n" @@ -278,218 +187,254 @@ "\"%s\" 올바른 데이터를 입력하세요!\n" "우측의 데이터는 좌측보다 커야 합니다." -#: ../PLCControler.py:793 -#, python-format -msgid "%s \"%s\" can't be pasted as a %s." -msgstr "%s \"%s\" 붙여넣기 할 수 없는 형식 : %s." - -#: ../PLCControler.py:1422 -#, fuzzy, python-format +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "" + +#: ../PLCControler.py:1533 +#, python-format msgid "%s Data Types" -msgstr "데이터 타입(Data Types)" - -#: ../editors/GraphicViewer.py:278 -#, python-format -msgid "%s Graphics" -msgstr "%s 그래픽" - -#: ../PLCControler.py:1417 -#, fuzzy, python-format +msgstr "" + +#: ../PLCControler.py:1516 +#, python-format msgid "%s POUs" -msgstr "POU 붙여넣기" - -#: ../canfestival/SlaveEditor.py:42 -#: ../canfestival/NetworkEditor.py:72 +msgstr "" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 #, python-format msgid "%s Profile" msgstr "%s 프로필" -#: ../plcopen/plcopen.py:1780 -#: ../plcopen/plcopen.py:1790 -#: ../plcopen/plcopen.py:1800 -#: ../plcopen/plcopen.py:1810 -#: ../plcopen/plcopen.py:1819 +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 #, python-format msgid "%s body don't have instances!" msgstr "%s 인스턴스를 찾을 수 없습니다!" -#: ../plcopen/plcopen.py:1842 -#: ../plcopen/plcopen.py:1849 +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 #, python-format msgid "%s body don't have text!" msgstr "%s 텍스트를 찾을 수 없습니다!" -#: ../IDEFrame.py:364 +#: ../IDEFrame.py:386 msgid "&Add Element" msgstr "&구성원 추가" -#: ../IDEFrame.py:334 +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "" + +#: ../IDEFrame.py:356 msgid "&Configuration" msgstr "&설정(Configuration)" -#: ../IDEFrame.py:325 +#: ../IDEFrame.py:345 msgid "&Data Type" msgstr "&데이터 타입" -#: ../IDEFrame.py:368 +#: ../IDEFrame.py:390 msgid "&Delete" msgstr "&삭제" -#: ../IDEFrame.py:317 +#: ../IDEFrame.py:337 msgid "&Display" msgstr "&보기" -#: ../IDEFrame.py:316 -#, fuzzy +#: ../IDEFrame.py:336 msgid "&Edit" -msgstr "수정" - -#: ../IDEFrame.py:315 +msgstr "" + +#: ../IDEFrame.py:335 msgid "&File" msgstr "&파일" -#: ../IDEFrame.py:327 +#: ../IDEFrame.py:347 msgid "&Function" msgstr "&함수" -#: ../IDEFrame.py:318 +#: ../IDEFrame.py:338 msgid "&Help" msgstr "&도움말" -#: ../IDEFrame.py:331 +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "" + +#: ../IDEFrame.py:351 msgid "&Program" msgstr "&프로그램" -#: ../PLCOpenEditor.py:148 -#, fuzzy +#: ../PLCOpenEditor.py:127 msgid "&Properties" msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"&속성\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"&프로젝트 속성" - -#: ../Beremiz.py:310 + +#: ../BeremizIDE.py:219 msgid "&Recent Projects" msgstr "&최근 프로젝트" -#: ../Beremiz.py:352 +#: ../IDEFrame.py:353 msgid "&Resource" msgstr "" -#: ../controls/SearchResultPanel.py:237 -#, python-format -msgid "'%s' - %d match in project" -msgstr "'%s' - %d 개의 검색된 결과" - #: ../controls/SearchResultPanel.py:239 -#, python-format -msgid "'%s' - %d matches in project" -msgstr "'%s' - %d 개의 검색된 결과들" - -#: ../connectors/PYRO/__init__.py:51 -#, python-format -msgid "'%s' is located at %s\n" -msgstr "" - -#: ../controls/SearchResultPanel.py:289 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "" + +#: ../controls/SearchResultPanel.py:291 #, python-format msgid "(%d matches)" msgstr "" -#: ../PLCOpenEditor.py:508 -#: ../PLCOpenEditor.py:510 -#: ../PLCOpenEditor.py:511 +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 msgid ", " msgstr ", " -#: ../dialogs/PouTransitionDialog.py:96 -#: ../dialogs/PouActionDialog.py:93 -#: ../dialogs/PouDialog.py:113 -#: ../dialogs/SFCTransitionDialog.py:146 +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 #, python-format msgid ", %s" msgstr ", %s" -#: ../PLCOpenEditor.py:506 +#: ../PLCOpenEditor.py:404 msgid ". " msgstr ". " -#: ../ProjectController.py:1268 -msgid "... debugger recovered\n" -msgstr "... 디버거 복구\n" - -#: ../IDEFrame.py:1672 -#: ../IDEFrame.py:1714 -#: ../IDEFrame.py:1733 -#: ../dialogs/PouDialog.py:122 -#, python-format -msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" msgstr "POU 구성원의 이름 \"%s\"은 오류를 발생시킬 수 있습니다. 계속 하시겠습니까?" -#: ../controls/VariablePanel.py:658 -#: ../IDEFrame.py:1684 -#: ../IDEFrame.py:1695 -#: ../dialogs/PouNameDialog.py:51 -#: ../dialogs/PouTransitionDialog.py:103 -#: ../dialogs/SFCStepNameDialog.py:53 -#: ../dialogs/PouActionDialog.py:100 -#: ../dialogs/SFCStepDialog.py:124 +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 #, python-format msgid "A POU named \"%s\" already exists!" msgstr "이미 생성된 POU 이름입니다 : \"%s\"" -#: ../ConfigTreeNode.py:371 -#, fuzzy, python-format -msgid "A child named \"%s\" already exist -> \"%s\"\n" -msgstr "입력 하신 \"%s\" 이 중복 되는 이름입니다. -> \"%s\"\n" - -#: ../dialogs/BrowseLocationsDialog.py:175 +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:218 msgid "A location must be selected!" msgstr "위치를 지정해야 합니다!" -#: ../controls/VariablePanel.py:660 -#: ../IDEFrame.py:1686 -#: ../IDEFrame.py:1697 -#: ../dialogs/SFCStepNameDialog.py:55 -#: ../dialogs/SFCStepDialog.py:126 +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 #, python-format msgid "A variable with \"%s\" as name already exists in this pou!" msgstr "변수 \"%s\"는 이미 POU에 정의 되어 있습니다!" -#: ../Beremiz.py:362 -#: ../PLCOpenEditor.py:181 -#, fuzzy +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 msgid "About" msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"베레미즈는...\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"베레미즈란" - -#: ../Beremiz.py:931 -msgid "About Beremiz" -msgstr "베레미즈" - -#: ../PLCOpenEditor.py:376 -msgid "About PLCOpenEditor" -msgstr "PLC 오픈에디터..." #: ../plcopen/iec_std.csv:22 msgid "Absolute number" msgstr "절대값 연산" -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCStepDialog.py:69 +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 msgid "Action" msgstr "액션(Action)" -#: ../editors/Viewer.py:495 -#, fuzzy +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 msgid "Action Block" -msgstr "함수 블럭(Function Block)" - -#: ../dialogs/PouActionDialog.py:81 +msgstr "" + +#: ../dialogs/PouActionDialog.py:82 msgid "Action Name" msgstr "액션 명" @@ -497,150 +442,157 @@ msgid "Action Name:" msgstr "액션 명:" -#: ../plcopen/plcopen.py:1480 +#: ../plcopen/plcopen.py:1364 #, python-format msgid "Action with name %s doesn't exist!" msgstr "액션 명 %s는 이미 존재합니다!" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Actions" msgstr "액션(Actions)" -#: ../dialogs/ActionBlockDialog.py:134 +#: ../dialogs/ActionBlockDialog.py:133 msgid "Actions:" msgstr "액션(Actions):" -#: ../canfestival/SlaveEditor.py:54 -#: ../canfestival/NetworkEditor.py:84 -#: ../editors/Viewer.py:527 +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 msgid "Add" msgstr "추가" -#: ../IDEFrame.py:1925 -#: ../IDEFrame.py:1956 +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 msgid "Add Action" msgstr "액션 추가" -#: ../features.py:7 +#: ../features.py:32 msgid "Add C code accessing located variables synchronously" msgstr "동기적으로 위치한 변수를 액세스하는 C 코드를 추가합니다" -#: ../IDEFrame.py:1908 +#: ../IDEFrame.py:1876 msgid "Add Configuration" msgstr "설정(Configuration) 추가" -#: ../IDEFrame.py:1888 +#: ../IDEFrame.py:1856 msgid "Add DataType" msgstr "데이터 타입 추가" -#: ../editors/Viewer.py:453 +#: ../editors/Viewer.py:572 msgid "Add Divergence Branch" msgstr "Branch 추가" -#: ../dialogs/DiscoveryDialog.py:115 +#: ../dialogs/DiscoveryDialog.py:117 msgid "Add IP" msgstr "" -#: ../IDEFrame.py:1896 +#: ../IDEFrame.py:1864 msgid "Add POU" msgstr "POU 추가" -#: ../features.py:8 +#: ../features.py:33 msgid "Add Python code executed asynchronously" msgstr "비동기적으로 실행되는 파이썬 코드를 추가합니다" -#: ../IDEFrame.py:1936 -#: ../IDEFrame.py:1982 +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 msgid "Add Resource" msgstr "리소스 추가" -#: ../IDEFrame.py:1914 -#: ../IDEFrame.py:1953 +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 msgid "Add Transition" msgstr "트랜지션(Transition) 추가" -#: ../editors/Viewer.py:442 +#: ../editors/Viewer.py:559 msgid "Add Wire Segment" msgstr "와이어 세그먼트(Wire Segment) 추가" -#: ../editors/SFCViewer.py:359 +#: ../editors/SFCViewer.py:433 msgid "Add a new initial step" msgstr "새로운 초기 스텝 추가" -#: ../editors/Viewer.py:2289 -#: ../editors/SFCViewer.py:696 +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 msgid "Add a new jump" msgstr "새로운 점프 추가" -#: ../editors/SFCViewer.py:381 +#: ../editors/SFCViewer.py:455 msgid "Add a new step" msgstr "새로운 스텝 추가" -#: ../features.py:9 +#: ../features.py:34 msgid "Add a simple WxGlade based GUI." msgstr "간단 WxGlade GUI를 추가합니다" -#: ../dialogs/ActionBlockDialog.py:138 -#, fuzzy +#: ../dialogs/ActionBlockDialog.py:137 msgid "Add action" -msgstr "액션 추가" - -#: ../editors/DataTypeEditor.py:345 -#, fuzzy +msgstr "" + +#: ../editors/DataTypeEditor.py:352 msgid "Add element" -msgstr "구성원(Element) 추가" - -#: ../editors/ResourceEditor.py:251 -#, fuzzy +msgstr "" + +#: ../editors/ResourceEditor.py:268 msgid "Add instance" -msgstr "인스턴스 추가" - -#: ../canfestival/NetworkEditor.py:86 +msgstr "" + +#: ../canfestival/NetworkEditor.py:103 msgid "Add slave" msgstr "슬레이브 추가" -#: ../editors/ResourceEditor.py:222 -#, fuzzy +#: ../editors/ResourceEditor.py:239 msgid "Add task" -msgstr "태스크 추가" - -#: ../controls/VariablePanel.py:378 -#, fuzzy +msgstr "" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 msgid "Add variable" -msgstr "변수" +msgstr "" #: ../plcopen/iec_std.csv:33 msgid "Addition" msgstr "추가" -#: ../plcopen/structures.py:250 +#: ../plcopen/definitions.py:49 msgid "Additional function blocks" msgstr "추가적 함수 블록" -#: ../editors/Viewer.py:1395 +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "" + +#: ../editors/Viewer.py:1686 msgid "Alignment" msgstr "정렬" -#: ../controls/VariablePanel.py:75 -#: ../dialogs/BrowseLocationsDialog.py:35 -#: ../dialogs/BrowseLocationsDialog.py:116 +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 msgid "All" msgstr "모두" #: ../editors/FileManagementPanel.py:35 -#, fuzzy msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" -msgstr "SVG 파일 (*.svg)|*svg|모든 파일|*.*" - -#: ../ProjectController.py:1335 +msgstr "" + +#: ../ProjectController.py:1685 msgid "Already connected. Please disconnect\n" msgstr "이미 접속중입니다. 연결을 해제 하세요\n" -#: ../editors/DataTypeEditor.py:587 +#: ../editors/DataTypeEditor.py:591 #, python-format msgid "An element named \"%s\" already exists in this structure!" msgstr "항목 \"%s\" 는 이미 구조체 안에 존재 합니다!" +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "" + #: ../plcopen/iec_std.csv:31 msgid "Arc cosine" msgstr "Arc cosine" @@ -657,8 +609,8 @@ msgid "Arithmetic" msgstr "산술 연산" -#: ../controls/VariablePanel.py:729 -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 msgid "Array" msgstr "배열" @@ -666,19 +618,19 @@ msgid "Assignment" msgstr "할당" -#: ../dialogs/FBDVariableDialog.py:197 +#: ../dialogs/FBDVariableDialog.py:222 msgid "At least a variable or an expression must be selected!" msgstr "변수 또는 표현식이 필요합니다!" -#: ../controls/ProjectPropertiesPanel.py:99 +#: ../controls/ProjectPropertiesPanel.py:100 msgid "Author" msgstr "작성자" -#: ../controls/ProjectPropertiesPanel.py:96 +#: ../controls/ProjectPropertiesPanel.py:97 msgid "Author Name (optional):" msgstr "작성자 이름(옵션):" -#: ../dialogs/FindInPouDialog.py:72 +#: ../dialogs/FindInPouDialog.py:77 msgid "Backward" msgstr "" @@ -690,25 +642,21 @@ msgid "Bad domain name at " msgstr "도메인 이름이 올바르지 않습니다:" -#: ../canfestival/config_utils.py:341 -#: ../canfestival/config_utils.py:623 +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 #, python-format msgid "Bad location size : %s" msgstr "잘못된 로케이션 사이즈: %s" -#: ../editors/DataTypeEditor.py:168 -#: ../editors/DataTypeEditor.py:198 -#: ../editors/DataTypeEditor.py:290 -#: ../dialogs/ArrayTypeDialog.py:55 +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 msgid "Base Type:" msgstr "기본 타입:" -#: ../controls/VariablePanel.py:699 -#: ../editors/DataTypeEditor.py:617 +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 msgid "Base Types" msgstr "기본 타입" -#: ../Beremiz.py:486 +#: ../BeremizIDE.py:455 msgid "Beremiz" msgstr "베레미즈" @@ -740,144 +688,169 @@ msgid "Bitwise inverting" msgstr "비트 연산 반전 (Invert)" -#: ../editors/Viewer.py:465 -#, fuzzy +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 msgid "Block" -msgstr "블럭 수정" - -#: ../dialogs/FBDBlockDialog.py:38 +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:60 msgid "Block Properties" msgstr "블럭 속성" -#: ../editors/Viewer.py:434 +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "" + +#: ../editors/Viewer.py:550 msgid "Bottom" msgstr "하단" -#: ../dialogs/BrowseValuesLibraryDialog.py:37 -#, fuzzy, python-format +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format msgid "Browse %s values library" -msgstr "%s 라이브러리 탐색" - -#: ../dialogs/BrowseLocationsDialog.py:55 +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:65 msgid "Browse Locations" msgstr "위치 탐색" -#: ../ProjectController.py:1484 +#: ../ProjectController.py:1832 msgid "Build" msgstr "빌드" -#: ../ProjectController.py:1051 +#: ../ProjectController.py:1297 msgid "Build directory already clean\n" msgstr "빌드 디렉토리가 이미 비어 있습니다\n" -#: ../ProjectController.py:1485 +#: ../ProjectController.py:1833 msgid "Build project into build folder" msgstr "현재 작성된 프로젝트를 빌드 폴더에 생성합니다" -#: ../ProjectController.py:910 +#: ../ProjectController.py:1080 msgid "C Build crashed !\n" msgstr "C 파일 빌드 과정에 문제가 있습니다!\n" -#: ../ProjectController.py:907 +#: ../ProjectController.py:1077 msgid "C Build failed.\n" msgstr "C 파일 빌드에 실패 했습니다\n" -#: ../ProjectController.py:895 +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "" + +#: ../ProjectController.py:1155 msgid "C code generated successfully.\n" msgstr "C 코드가 성공적으로 생성되었습니다\n" -#: ../targets/toolchain_gcc.py:132 +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "" + +#: ../targets/toolchain_gcc.py:192 #, python-format msgid "C compilation of %s failed.\n" msgstr "%s 의 C 컴파일 작업이 실패 했습니다\n" -#: ../features.py:7 +#: ../features.py:32 msgid "C extension" msgstr "C 확장" -#: ../features.py:6 +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "" + +#: ../features.py:31 msgid "CANopen support" msgstr "CANopen 지원" -#: ../plcopen/plcopen.py:1722 -#: ../plcopen/plcopen.py:1736 -#: ../plcopen/plcopen.py:1757 -#: ../plcopen/plcopen.py:1773 +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 msgid "Can only generate execution order on FBD networks!" msgstr "FBD 네트워크 상태에서만 실행 순서를 생성할 수 있습니다!" -#: ../controls/VariablePanel.py:256 +#: ../controls/VariablePanel.py:267 msgid "Can only give a location to local or global variables" msgstr "로컬 또는 전역 변수만 위치 지정 가능합니다" -#: ../PLCOpenEditor.py:357 +#: ../PLCOpenEditor.py:344 #, python-format msgid "Can't generate program to file %s!" msgstr "프로그램을 %s 파일로 생성할 수 없습니다!" -#: ../controls/VariablePanel.py:254 +#: ../controls/VariablePanel.py:265 msgid "Can't give a location to a function block instance" msgstr "함수 블럭 인스턴스에는 위치를 지정할 수 없습니다" -#: ../PLCOpenEditor.py:397 +#: ../PLCOpenEditor.py:389 #, python-format msgid "Can't save project to file %s!" msgstr "프로젝트를 %s로 저장할 수 없습니다!" -#: ../controls/VariablePanel.py:298 -#, fuzzy +#: ../controls/VariablePanel.py:313 msgid "Can't set an initial value to a function block instance" -msgstr "함수 블럭 인스턴스에는 위치를 지정할 수 없습니다" - -#: ../ConfigTreeNode.py:470 -#, python-format -msgid "Cannot create child %s of type %s " -msgstr "%s로 (%s 타입) 차일드를 생성 할 수 없습니다" - -#: ../ConfigTreeNode.py:400 +msgstr "" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "" + +#: ../ConfigTreeNode.py:454 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "%d보다 낮은 번호의 가용 IEC 채널을 검색하는데 실패했습니다\n" -#: ../connectors/PYRO/__init__.py:92 +#: ../connectors/PYRO/__init__.py:131 msgid "Cannot get PLC status - connection failed.\n" msgstr "현재 PLC 상태를 알 수 없습니다 - 접속 실패.\n" -#: ../ProjectController.py:715 +#: ../ProjectController.py:943 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "VARIABLES.csv 파일을 열거나 파싱할 수 없습니다!\n" -#: ../canfestival/config_utils.py:371 -#, python-format -msgid "Cannot set bit offset for non bool '%s' variable (ID:%d,Idx:%x,sIdx:%x))" -msgstr "'%s' 변수의 비트 옵셋 설정 실패 (ID:%d,ldx:%x,sldx:%x))" - -#: ../dialogs/FindInPouDialog.py:81 -#: ../dialogs/SearchInProjectDialog.py:67 +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 msgid "Case sensitive" msgstr "대소문자 구별" -#: ../editors/Viewer.py:429 +#: ../editors/Viewer.py:545 msgid "Center" msgstr "중앙" -#: ../Beremiz_service.py:322 +#: ../Beremiz_service.py:268 msgid "Change IP of interface to bind" msgstr "바인드 인터페이스 IP 변경" -#: ../Beremiz_service.py:321 +#: ../Beremiz_service.py:267 msgid "Change Name" msgstr "이름 변경" -#: ../IDEFrame.py:1974 +#: ../IDEFrame.py:1946 msgid "Change POU Type To" msgstr "POU 타입 변경" -#: ../Beremiz_service.py:325 +#: ../Beremiz_service.py:269 msgid "Change Port Number" msgstr "포트 번호 변경" -#: ../Beremiz_service.py:327 +#: ../Beremiz_service.py:270 msgid "Change working directory" msgstr "작업 디렉토리 변경" @@ -885,113 +858,115 @@ msgid "Character string" msgstr "문자열" -#: ../svgui/svgui.py:92 +#: ../svgui/svgui.py:128 msgid "Choose a SVG file" msgstr "SVG 파일 선택" -#: ../ProjectController.py:353 +#: ../ProjectController.py:542 msgid "Choose a directory to save project" msgstr "프로젝트 저장 디렉토리 선택" -#: ../canfestival/canfestival.py:118 -#: ../PLCOpenEditor.py:313 -#: ../PLCOpenEditor.py:347 -#: ../PLCOpenEditor.py:391 +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 msgid "Choose a file" msgstr "파일 선택" -#: ../Beremiz.py:831 -#: ../Beremiz.py:866 +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 msgid "Choose a project" msgstr "프로젝트 선택" -#: ../dialogs/BrowseValuesLibraryDialog.py:42 -#, fuzzy, python-format +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format msgid "Choose a value for %s:" -msgstr "%s 선택:" - -#: ../Beremiz_service.py:373 +msgstr "" + +#: ../Beremiz_service.py:325 msgid "Choose a working directory " msgstr "작업 디렉토리 선택" -#: ../ProjectController.py:281 +#: ../ProjectController.py:449 msgid "Chosen folder doesn't contain a program. It's not a valid project!" msgstr "베레미즈 프로젝트 폴더를 선택해 주세요. 선택하신 폴더는 프로젝트 폴더가 아닙니다!" -#: ../ProjectController.py:247 +#: ../ProjectController.py:416 msgid "Chosen folder isn't empty. You can't use it for a new project!" msgstr "선택된 폴더는 비어있지 않습니다. 새 프로젝트 폴더로 사용 할 수 없습니다!" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Class" msgstr "클래스" -#: ../controls/VariablePanel.py:369 +#: ../controls/VariablePanel.py:441 msgid "Class Filter:" msgstr "클래스 필터:" -#: ../dialogs/FBDVariableDialog.py:62 +#: ../dialogs/FBDVariableDialog.py:70 msgid "Class:" msgstr "클래스:" -#: ../ProjectController.py:1488 +#: ../ProjectController.py:1836 msgid "Clean" msgstr "클린" -#: ../ProjectController.py:1490 +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "" + +#: ../ProjectController.py:1838 msgid "Clean project build folder" msgstr "프로젝트 빌드 폴더를 비웁니다" -#: ../ProjectController.py:1048 +#: ../ProjectController.py:1294 msgid "Cleaning the build directory\n" msgstr "빌드 디렉토리를 비우는 중\n" -#: ../IDEFrame.py:411 -#, fuzzy +#: ../IDEFrame.py:435 msgid "Clear Errors" -msgstr "에러 화면 클리어\tCTRL+K" - -#: ../editors/Viewer.py:520 +msgstr "" + +#: ../editors/Viewer.py:641 msgid "Clear Execution Order" msgstr "실행 순서 클리어" -#: ../editors/GraphicViewer.py:125 -msgid "Clear the graph values" -msgstr "" - -#: ../Beremiz.py:598 -#: ../PLCOpenEditor.py:221 +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 msgid "Close Application" msgstr "어플리케이션 닫기" -#: ../IDEFrame.py:1089 -#: ../Beremiz.py:319 -#: ../Beremiz.py:552 -#: ../PLCOpenEditor.py:131 +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 msgid "Close Project" msgstr "프로젝트 닫기" -#: ../Beremiz.py:317 -#: ../PLCOpenEditor.py:129 -#, fuzzy +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 msgid "Close Tab" -msgstr "탭 닫기\tCTRL+W" - -#: ../editors/Viewer.py:481 +msgstr "" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 msgid "Coil" msgstr "" -#: ../editors/Viewer.py:501 -#: ../editors/LDViewer.py:503 +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 msgid "Comment" msgstr "코멘트" -#: ../controls/ProjectPropertiesPanel.py:94 +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:95 msgid "Company Name (required):" msgstr "회사명(필수):" -#: ../controls/ProjectPropertiesPanel.py:95 +#: ../controls/ProjectPropertiesPanel.py:96 msgid "Company URL (optional):" msgstr "회사 URL(옵션):" @@ -999,7 +974,7 @@ msgid "Comparison" msgstr "비교연산" -#: ../ProjectController.py:538 +#: ../ProjectController.py:734 msgid "Compiling IEC Program into C code...\n" msgstr "IEC 프로그램을 C코드로 컴파일링 중...\n" @@ -1007,72 +982,93 @@ msgid "Concatenation" msgstr "문자열 연결(concatenation)" -#: ../dialogs/SearchInProjectDialog.py:47 +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:40 msgid "Configuration" msgstr "설정(Configuration)" -#: ../PLCControler.py:96 +#: ../PLCControler.py:99 msgid "Configurations" msgstr "구성(Configurations)" -#: ../ProjectController.py:1503 +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "" + +#: ../ProjectController.py:1851 msgid "Connect" msgstr "연결하기" -#: ../ProjectController.py:1504 +#: ../ProjectController.py:1852 msgid "Connect to the target PLC" msgstr "타겟 PLC와 연결" -#: ../connectors/PYRO/__init__.py:40 -#, python-format -msgid "Connecting to URI : %s\n" -msgstr "URI 주소로 연결중 : %s\n" - -#: ../editors/Viewer.py:467 -#: ../dialogs/SFCTransitionDialog.py:76 +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 msgid "Connection" msgstr "연결" -#: ../dialogs/ConnectionDialog.py:37 +#: ../dialogs/ConnectionDialog.py:53 msgid "Connection Properties" msgstr "연결 속성" -#: ../ProjectController.py:1359 +#: ../ProjectController.py:1709 msgid "Connection canceled!\n" msgstr "연결 취소!\n" -#: ../ProjectController.py:1384 +#: ../ProjectController.py:1734 #, python-format msgid "Connection failed to %s!\n" msgstr "%s 연결에 실패 하였습니다!\n" -#: ../connectors/PYRO/__init__.py:63 -#, fuzzy, python-format +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format msgid "Connection to '%s' failed.\n" -msgstr "%s 의 C 컴파일 작업이 실패 했습니다\n" - -#: ../dialogs/ConnectionDialog.py:56 +msgstr "" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 msgid "Connector" msgstr "연결자(Connector)" -#: ../dialogs/SFCStepDialog.py:58 +#: ../dialogs/SFCStepDialog.py:66 msgid "Connectors:" msgstr "연결자(Connectors):" -#: ../controls/VariablePanel.py:65 +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "" + +#: ../controls/VariablePanel.py:60 msgid "Constant" msgstr "상수" -#: ../editors/Viewer.py:477 -#, fuzzy +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 msgid "Contact" -msgstr "지속 커넥터" - -#: ../controls/ProjectPropertiesPanel.py:197 +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:198 msgid "Content Description (optional):" msgstr "컨텐츠 설명(옵션):" -#: ../dialogs/ConnectionDialog.py:61 +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 msgid "Continuation" msgstr "지속 커넥터" @@ -1092,21 +1088,20 @@ msgid "Conversion to time-of-day" msgstr "시간으로 변환" -#: ../IDEFrame.py:348 -#: ../IDEFrame.py:401 -#: ../editors/Viewer.py:536 +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 msgid "Copy" msgstr "복사하기" -#: ../IDEFrame.py:1961 +#: ../IDEFrame.py:1933 msgid "Copy POU" msgstr "POU 복사" -#: ../editors/FileManagementPanel.py:283 +#: ../editors/FileManagementPanel.py:65 msgid "Copy file from left folder to right" msgstr "" -#: ../editors/FileManagementPanel.py:282 +#: ../editors/FileManagementPanel.py:64 msgid "Copy file from right folder to left" msgstr "" @@ -1114,55 +1109,53 @@ msgid "Cosine" msgstr "Cosine" -#: ../ConfigTreeNode.py:582 -#, python-format -msgid "" -"Could not add child \"%s\", type %s :\n" -"%s\n" -msgstr "" -"\"%s\" child 추가 할 수 없습니다, type %s:\n" -"%s\n" - -#: ../ConfigTreeNode.py:559 -#, python-format -msgid "" -"Couldn't load confnode base parameters %s :\n" -" %s" -msgstr "" -"플러그인 베이스 파라메터를 불러 올 수 없습니다 %s:\n" -"%s" - -#: ../ConfigTreeNode.py:570 -#, python-format -msgid "" -"Couldn't load confnode parameters %s :\n" -" %s" -msgstr "" -"플러그인 파라메터를 불러 올 수 없습니다 %s:\n" -"%s" - -#: ../PLCControler.py:765 -#: ../PLCControler.py:802 +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "" + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../PLCControler.py:948 msgid "Couldn't paste non-POU object." msgstr "POU 오브젝트만 붙여넣기 가능합니다" -#: ../ProjectController.py:1317 +#: ../ProjectController.py:1651 msgid "Couldn't start PLC !\n" msgstr "PLC 를 시작 할 수 없습니다!\n" -#: ../ProjectController.py:1325 +#: ../ProjectController.py:1659 msgid "Couldn't stop PLC !\n" msgstr "PLC 를 정지 할 수 없습니다!\n" -#: ../ProjectController.py:1295 +#: ../ProjectController.py:1623 msgid "Couldn't stop debugger.\n" msgstr "디버거를 정지 할 수 없습니다.\n" -#: ../svgui/svgui.py:22 +#: ../svgui/svgui.py:49 msgid "Create HMI" msgstr "HMI 생성" -#: ../dialogs/PouDialog.py:43 +#: ../dialogs/PouDialog.py:46 msgid "Create a new POU" msgstr "새로운 POU 생성" @@ -1170,118 +1163,107 @@ msgid "Create a new action" msgstr "새로운 액션 생성" -#: ../IDEFrame.py:135 +#: ../IDEFrame.py:159 msgid "Create a new action block" msgstr "새로운 액션 블럭 생성" -#: ../IDEFrame.py:84 -#: ../IDEFrame.py:114 -#: ../IDEFrame.py:147 +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 msgid "Create a new block" msgstr "새로운 블럭 생성" -#: ../IDEFrame.py:108 +#: ../IDEFrame.py:132 msgid "Create a new branch" msgstr "새로운 분기(Branch) 생성" -#: ../IDEFrame.py:102 +#: ../IDEFrame.py:126 msgid "Create a new coil" msgstr "새로운 코일 생성" -#: ../IDEFrame.py:78 -#: ../IDEFrame.py:93 -#: ../IDEFrame.py:123 +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 msgid "Create a new comment" msgstr "새로운 코멘트 생성" -#: ../IDEFrame.py:87 -#: ../IDEFrame.py:117 -#: ../IDEFrame.py:150 +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 msgid "Create a new connection" msgstr "새로운 연결 생성" -#: ../IDEFrame.py:105 -#: ../IDEFrame.py:156 +#: ../IDEFrame.py:129 ../IDEFrame.py:180 msgid "Create a new contact" msgstr "새로운 접점 생성" -#: ../IDEFrame.py:138 +#: ../IDEFrame.py:162 msgid "Create a new divergence" msgstr "새로운 분기(divergence) 생성" -#: ../dialogs/SFCDivergenceDialog.py:36 +#: ../dialogs/SFCDivergenceDialog.py:53 msgid "Create a new divergence or convergence" msgstr "새로운 분기(divergence) 또는 합류(convergence) 생성" -#: ../IDEFrame.py:126 +#: ../IDEFrame.py:150 msgid "Create a new initial step" msgstr "새로운 이니셜 스텝 생성" -#: ../IDEFrame.py:141 +#: ../IDEFrame.py:165 msgid "Create a new jump" msgstr "새로운 점프 생성" -#: ../IDEFrame.py:96 -#: ../IDEFrame.py:153 +#: ../IDEFrame.py:120 ../IDEFrame.py:177 msgid "Create a new power rail" msgstr "새로운 전원 레일 생성" -#: ../IDEFrame.py:99 +#: ../IDEFrame.py:123 msgid "Create a new rung" msgstr "새로운 Rung 생성" -#: ../IDEFrame.py:129 +#: ../IDEFrame.py:153 msgid "Create a new step" msgstr "새로운 스텝 생성" -#: ../IDEFrame.py:132 -#: ../dialogs/PouTransitionDialog.py:42 +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 msgid "Create a new transition" msgstr "새로운 트랜지션 생성" -#: ../IDEFrame.py:81 -#: ../IDEFrame.py:111 -#: ../IDEFrame.py:144 +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 msgid "Create a new variable" msgstr "새로운 변수 생성" -#: ../IDEFrame.py:346 -#: ../IDEFrame.py:400 -#: ../editors/Viewer.py:535 +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 msgid "Cut" msgstr "잘라내기" -#: ../editors/ResourceEditor.py:71 +#: ../editors/ResourceEditor.py:72 msgid "Cyclic" msgstr "주기적" -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 #: ../plcopen/iec_std.csv:60 msgid "DEPRECATED" msgstr "사용중지됨" -#: ../canfestival/SlaveEditor.py:50 -#: ../canfestival/NetworkEditor.py:80 +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 msgid "DS-301 Profile" msgstr "DS-301 프로필" -#: ../canfestival/SlaveEditor.py:51 -#: ../canfestival/NetworkEditor.py:81 +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 msgid "DS-302 Profile" msgstr "DS-302 프로필" -#: ../dialogs/SearchInProjectDialog.py:43 +#: ../dialogs/SearchInProjectDialog.py:36 msgid "Data Type" msgstr "데이터 타입(Data Type)" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Data Types" msgstr "데이터 타입(Data Types)" @@ -1289,82 +1271,75 @@ msgid "Data type conversion" msgstr "데이터 타입 변환" -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:45 +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 msgid "Date addition" msgstr "날짜 추가" -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:57 -#: ../plcopen/iec_std.csv:58 -#: ../plcopen/iec_std.csv:59 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 msgid "Date and time subtraction" msgstr "날짜, 시간 뺄셈" -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:51 +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 msgid "Date subtraction" msgstr "날짜 뺄셈" -#: ../dialogs/DurationEditorDialog.py:43 +#: ../dialogs/DurationEditorDialog.py:44 msgid "Days:" msgstr "" -#: ../ProjectController.py:1405 -msgid "Debug connect matching running PLC\n" -msgstr "디버그 작동중인 PLC와 매칭 되었습니다\n" - -#: ../ProjectController.py:1408 -msgid "Debug do not match PLC - stop/transfert/start to re-enable\n" -msgstr "PLC와 디버거가 연결되지 않았습니다 - 정지/전송/시작을 다시 시도하세요\n" - -#: ../controls/PouInstanceVariablesPanel.py:52 -#, fuzzy +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:134 msgid "Debug instance" -msgstr "인스턴스 삭제" - -#: ../editors/Viewer.py:3222 +msgstr "" + +#: ../editors/Viewer.py:448 #, python-format msgid "Debug: %s" msgstr "디버그: %s" -#: ../ProjectController.py:1122 -#, fuzzy, python-format +#: ../ProjectController.py:1412 +#, python-format msgid "Debug: Unknown variable '%s'\n" -msgstr "디버그 : 알 수 없는 변수 %s\n" - -#: ../ProjectController.py:1120 -#, fuzzy, python-format +msgstr "" + +#: ../ProjectController.py:1410 +#, python-format msgid "Debug: Unsupported type to debug '%s'\n" -msgstr "디버그 : 지원하지 않는 타입 %s\n" - -#: ../IDEFrame.py:608 +msgstr "" + +#: ../IDEFrame.py:639 msgid "Debugger" msgstr "디버거" -#: ../ProjectController.py:1285 +#: ../ProjectController.py:1592 msgid "Debugger disabled\n" msgstr "디버거 사용 불가\n" -#: ../ProjectController.py:1297 +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "" + +#: ../ProjectController.py:1625 msgid "Debugger stopped.\n" msgstr "디버거 정지.\n" -#: ../IDEFrame.py:1990 -#: ../Beremiz.py:958 -#: ../editors/Viewer.py:511 +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 msgid "Delete" msgstr "삭제" -#: ../editors/Viewer.py:454 +#: ../editors/Viewer.py:573 msgid "Delete Divergence Branch" msgstr "파생된 Branch 삭제 (Divergence Branch)" -#: ../editors/FileManagementPanel.py:371 +#: ../editors/FileManagementPanel.py:153 msgid "Delete File" msgstr "" -#: ../editors/Viewer.py:443 +#: ../editors/Viewer.py:560 msgid "Delete Wire Segment" msgstr "와이어 세그먼트 삭제" @@ -1376,184 +1351,168 @@ msgid "Deletion (within)" msgstr "내부 삭제" -#: ../editors/DataTypeEditor.py:146 +#: ../editors/DataTypeEditor.py:153 msgid "Derivation Type:" msgstr "미분 타입" -#: ../plcopen/structures.py:264 -msgid "" -"Derivative\n" -"The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." -msgstr "" -"미분\n" -"미분 함수 블럭은 입력 XIN의 변화의 속도에 비례하여 출력 XOUT을 생성합니다" - -#: ../controls/VariablePanel.py:360 -#, fuzzy +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "" + +#: ../controls/VariablePanel.py:432 msgid "Description:" -msgstr "방향:" - -#: ../editors/DataTypeEditor.py:314 -#: ../dialogs/ArrayTypeDialog.py:61 +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 msgid "Dimensions:" msgstr "넓이:" -#: ../dialogs/FindInPouDialog.py:61 -#, fuzzy +#: ../dialogs/FindInPouDialog.py:66 msgid "Direction" -msgstr "방향:" - -#: ../dialogs/BrowseLocationsDialog.py:78 +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:91 msgid "Direction:" msgstr "방향:" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Directly" msgstr "직접" -#: ../ProjectController.py:1512 +#: ../ProjectController.py:1860 msgid "Disconnect" msgstr "연결 해제" -#: ../ProjectController.py:1514 +#: ../ProjectController.py:1862 msgid "Disconnect from PLC" msgstr "PLC 연결 해제" -#: ../editors/Viewer.py:496 -#, fuzzy +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 msgid "Divergence" -msgstr "벡터 발산(Divergence) 선택" +msgstr "" #: ../plcopen/iec_std.csv:36 msgid "Division" msgstr "분할" -#: ../editors/FileManagementPanel.py:370 +#: ../editors/FileManagementPanel.py:152 #, python-format msgid "Do you really want to delete the file '%s'?" msgstr "" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Documentation" msgstr "도움문서" -#: ../PLCOpenEditor.py:351 +#: ../PLCOpenEditor.py:338 msgid "Done" msgstr "완료" -#: ../plcopen/structures.py:227 -msgid "" -"Down-counter\n" -"The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." -msgstr "" -"감산 카운터\n" -"감산 카운터는 사용자가 설정한 값으로부터 감소하여 0이 될 때 신호를 발생시킵니다." - -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Duration" msgstr "지속시간" -#: ../canfestival/canfestival.py:118 -#, fuzzy +#: ../canfestival/canfestival.py:165 msgid "EDS files (*.eds)|*.eds|All files|*.*" -msgstr "SVG 파일 (*.svg)|*svg|모든 파일|*.*" - -#: ../editors/Viewer.py:510 +msgstr "" + +#: ../editors/Viewer.py:629 msgid "Edit Block" msgstr "블럭 수정" -#: ../dialogs/LDElementDialog.py:41 +#: ../dialogs/LDElementDialog.py:56 msgid "Edit Coil Values" msgstr "코일 데이터 수정" -#: ../dialogs/LDElementDialog.py:38 +#: ../dialogs/LDElementDialog.py:54 msgid "Edit Contact Values" msgstr "접접 데이터 수정" #: ../dialogs/DurationEditorDialog.py:59 -#, fuzzy msgid "Edit Duration" -msgstr "트랜지션 수정" - -#: ../dialogs/SFCStepDialog.py:35 +msgstr "" + +#: ../dialogs/SFCStepDialog.py:51 msgid "Edit Step" msgstr "스텝 수정" -#: ../wxglade_hmi/wxglade_hmi.py:12 +#: ../wxglade_hmi/wxglade_hmi.py:38 msgid "Edit a WxWidgets GUI with WXGlade" msgstr "WXGlade를 이용하여 GUI 수정" -#: ../dialogs/ActionBlockDialog.py:122 +#: ../dialogs/ActionBlockDialog.py:121 msgid "Edit action block properties" msgstr "액션 블럭 속성 수정" -#: ../dialogs/ArrayTypeDialog.py:45 -#, fuzzy +#: ../dialogs/ArrayTypeDialog.py:44 msgid "Edit array type properties" -msgstr "액션 블럭 속성 수정" - -#: ../editors/Viewer.py:2112 -#: ../editors/Viewer.py:2114 -#: ../editors/Viewer.py:2630 -#: ../editors/Viewer.py:2632 +msgstr "" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 msgid "Edit comment" msgstr "코멘트 수정" -#: ../editors/FileManagementPanel.py:284 -#, fuzzy +#: ../editors/FileManagementPanel.py:66 msgid "Edit file" -msgstr "C 파일 수정" +msgstr "" #: ../controls/CustomEditableListBox.py:39 msgid "Edit item" msgstr "항목 수정" -#: ../editors/Viewer.py:2594 +#: ../editors/Viewer.py:3014 msgid "Edit jump target" msgstr "점프 타겟 수정" -#: ../ProjectController.py:1526 +#: ../ProjectController.py:1874 msgid "Edit raw IEC code added to code generated by PLCGenerator" msgstr "PLCGenerator로 생성된 IEC 코드 수정" -#: ../editors/SFCViewer.py:725 +#: ../editors/SFCViewer.py:799 msgid "Edit step name" msgstr "스텝 이름 수정" -#: ../dialogs/SFCTransitionDialog.py:38 +#: ../dialogs/SFCTransitionDialog.py:52 msgid "Edit transition" msgstr "트랜지션 수정" -#: ../IDEFrame.py:580 +#: ../IDEFrame.py:611 msgid "Editor ToolBar" msgstr "" -#: ../ProjectController.py:1013 +#: ../ProjectController.py:1257 msgid "Editor selection" msgstr "" -#: ../editors/DataTypeEditor.py:341 +#: ../editors/DataTypeEditor.py:348 msgid "Elements :" msgstr "구성원:" -#: ../IDEFrame.py:343 +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "" + +#: ../IDEFrame.py:365 msgid "Enable Undo/Redo" msgstr "되돌리기/되돌리기 취소 활성화" -#: ../Beremiz_service.py:380 +#: ../Beremiz_service.py:333 msgid "Enter a name " msgstr "이름 입력" -#: ../Beremiz_service.py:365 +#: ../Beremiz_service.py:318 msgid "Enter a port number " msgstr "포트 번호 입력" -#: ../Beremiz_service.py:355 +#: ../Beremiz_service.py:309 msgid "Enter the IP of the interface to bind" msgstr "바인드 인터페이스 IP 입력" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Enumerated" msgstr "열거형 데이터" @@ -1561,69 +1520,48 @@ msgid "Equal to" msgstr "같은 값 일때" -#: ../Beremiz_service.py:270 -#: ../Beremiz_service.py:394 -#: ../controls/VariablePanel.py:330 -#: ../controls/VariablePanel.py:678 -#: ../controls/DebugVariablePanel.py:164 -#: ../IDEFrame.py:1083 -#: ../IDEFrame.py:1672 -#: ../IDEFrame.py:1709 -#: ../IDEFrame.py:1714 -#: ../IDEFrame.py:1728 -#: ../IDEFrame.py:1733 -#: ../IDEFrame.py:2422 -#: ../Beremiz.py:1083 -#: ../PLCOpenEditor.py:358 -#: ../PLCOpenEditor.py:363 -#: ../PLCOpenEditor.py:531 -#: ../PLCOpenEditor.py:541 -#: ../editors/TextViewer.py:376 -#: ../editors/DataTypeEditor.py:543 -#: ../editors/DataTypeEditor.py:548 -#: ../editors/DataTypeEditor.py:572 -#: ../editors/DataTypeEditor.py:577 -#: ../editors/DataTypeEditor.py:587 -#: ../editors/DataTypeEditor.py:719 -#: ../editors/DataTypeEditor.py:726 -#: ../editors/Viewer.py:366 -#: ../editors/LDViewer.py:663 -#: ../editors/LDViewer.py:879 -#: ../editors/LDViewer.py:883 -#: ../editors/FileManagementPanel.py:210 -#: ../ProjectController.py:221 -#: ../dialogs/PouNameDialog.py:53 -#: ../dialogs/PouTransitionDialog.py:107 -#: ../dialogs/BrowseLocationsDialog.py:175 -#: ../dialogs/ProjectDialog.py:71 -#: ../dialogs/SFCStepNameDialog.py:59 -#: ../dialogs/ConnectionDialog.py:152 -#: ../dialogs/FBDVariableDialog.py:201 -#: ../dialogs/PouActionDialog.py:104 +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 #: ../dialogs/BrowseValuesLibraryDialog.py:83 -#: ../dialogs/PouDialog.py:132 -#: ../dialogs/SFCTransitionDialog.py:147 -#: ../dialogs/DurationEditorDialog.py:121 -#: ../dialogs/DurationEditorDialog.py:163 -#: ../dialogs/SearchInProjectDialog.py:157 -#: ../dialogs/SFCStepDialog.py:130 -#: ../dialogs/ArrayTypeDialog.py:97 -#: ../dialogs/ArrayTypeDialog.py:103 -#: ../dialogs/FBDBlockDialog.py:164 -#: ../dialogs/ForceVariableDialog.py:169 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 msgid "Error" msgstr "에러" -#: ../ProjectController.py:587 -msgid "Error : At least one configuration and one resource must be declared in PLC !\n" +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" msgstr "에러 : PLC 프로그램은 하나 이상의 설정과 리소스가 반드시 선언되어야 합니다!\n" -#: ../ProjectController.py:579 +#: ../ProjectController.py:781 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "에러 : IEC -> C 컴파일러 %d\n" -#: ../ProjectController.py:520 +#: ../ProjectController.py:712 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" @@ -1632,38 +1570,37 @@ "ST/IL/SFC 코드 생성기 에러 : \n" "%s\n" -#: ../ConfigTreeNode.py:182 +#: ../ConfigTreeNode.py:216 #, python-format msgid "Error while saving \"%s\"\n" msgstr "저장 중 에러 발생 \"%s\"\n" -#: ../canfestival/canfestival.py:122 +#: ../canfestival/canfestival.py:170 msgid "Error: Export slave failed\n" msgstr "" -#: ../canfestival/canfestival.py:270 +#: ../canfestival/canfestival.py:371 msgid "Error: No Master generated\n" msgstr "에러 : 마스터 미생성 \"%s\"\n" -#: ../canfestival/canfestival.py:265 +#: ../canfestival/canfestival.py:366 msgid "Error: No PLC built\n" msgstr "에러 : PLC 빌드 미생성\n" -#: ../ProjectController.py:1378 +#: ../ProjectController.py:1728 #, python-format msgid "Exception while connecting %s!\n" msgstr "%s접속중 예외 상황이 발생했습니다!\n" -#: ../dialogs/FBDBlockDialog.py:95 +#: ../dialogs/FBDBlockDialog.py:120 msgid "Execution Control:" msgstr "실행 제어:" -#: ../dialogs/FBDVariableDialog.py:76 -#: ../dialogs/FBDBlockDialog.py:87 +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 msgid "Execution Order:" msgstr "실행 순서:" -#: ../features.py:10 +#: ../features.py:35 msgid "Experimental web based HMI" msgstr "웹 기반의 HMI(실험중)" @@ -1675,193 +1612,171 @@ msgid "Exponentiation" msgstr "지수화" -#: ../canfestival/canfestival.py:128 +#: ../canfestival/canfestival.py:176 msgid "Export CanOpen slave to EDS file" msgstr "" -#: ../editors/GraphicViewer.py:144 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 msgid "Export graph values to clipboard" msgstr "" -#: ../canfestival/canfestival.py:127 +#: ../canfestival/canfestival.py:175 msgid "Export slave" msgstr "" -#: ../dialogs/FBDVariableDialog.py:69 +#: ../dialogs/FBDVariableDialog.py:90 msgid "Expression:" msgstr "표현식:" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid "External" msgstr "외부" -#: ../ProjectController.py:591 +#: ../ProjectController.py:802 msgid "Extracting Located Variables...\n" msgstr "위치 변수(located variables) 추출 중...\n" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "FBD" msgstr "FBD" -#: ../ProjectController.py:1445 +#: ../ProjectController.py:1791 msgid "Failed : Must build before transfer.\n" msgstr "실패 : 빌드 후에 전송 하세요.\n" -#: ../editors/Viewer.py:405 -#: ../dialogs/LDElementDialog.py:84 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 msgid "Falling Edge" msgstr "폴링 엣지" -#: ../plcopen/structures.py:217 -msgid "" -"Falling edge detector\n" -"The output produces a single pulse when a falling edge is detected." -msgstr "" -"폴링 엣지 검출\n" -"출력부의 폴링 엣지를 검출합니다" - -#: ../ProjectController.py:900 +#: ../ProjectController.py:1070 msgid "Fatal : cannot get builder.\n" msgstr "치명적 오류 : 빌드 파일 생성 프로그램을 찾을 수 없습니다.\n" -#: ../dialogs/DurationEditorDialog.py:160 -#, fuzzy, python-format +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format msgid "Field %s hasn't a valid value!" -msgstr "\"%s\" 유효하지 않은 값입니다!" - -#: ../dialogs/DurationEditorDialog.py:162 -#, fuzzy, python-format +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format msgid "Fields %s haven't a valid value!" -msgstr "\"%s\" 유효하지 않은 값입니다!" - -#: ../editors/FileManagementPanel.py:209 -#, fuzzy, python-format +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format msgid "File '%s' already exists!" -msgstr "입력 하신 \"%s\" 이 중복 되는 이름입니다. -> \"%s\"\n" - -#: ../IDEFrame.py:353 -#: ../dialogs/FindInPouDialog.py:30 -#: ../dialogs/FindInPouDialog.py:99 +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 msgid "Find" msgstr "" -#: ../IDEFrame.py:355 -#, fuzzy +#: ../IDEFrame.py:377 msgid "Find Next" -msgstr "인쇄\tCTRL+P" - -#: ../IDEFrame.py:357 -#, fuzzy +msgstr "" + +#: ../IDEFrame.py:379 msgid "Find Previous" -msgstr "인쇄 페이지 미리보기\tCTRL+SHIFT+P" +msgstr "" #: ../plcopen/iec_std.csv:90 msgid "Find position" msgstr "위치 찾기" -#: ../dialogs/FindInPouDialog.py:51 +#: ../dialogs/FindInPouDialog.py:55 msgid "Find:" msgstr "" -#: ../connectors/PYRO/__init__.py:125 +#: ../connectors/PYRO/__init__.py:163 msgid "Force runtime reload\n" msgstr "실행환경 강제 리로딩\n" -#: ../controls/DebugVariablePanel.py:295 -#: ../editors/Viewer.py:1353 +#: ../editors/Viewer.py:1600 msgid "Force value" msgstr "강제 데이터 입력" -#: ../dialogs/ForceVariableDialog.py:152 +#: ../dialogs/ForceVariableDialog.py:162 msgid "Forcing Variable Value" msgstr "강제 변수 데이터" -#: ../dialogs/PouTransitionDialog.py:97 -#: ../dialogs/ProjectDialog.py:70 -#: ../dialogs/PouActionDialog.py:94 -#: ../dialogs/PouDialog.py:114 -#: ../dialogs/SFCTransitionDialog.py:147 +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 #, python-format msgid "Form isn't complete. %s must be filled!" msgstr "형식이 완성되지 않았습니다. %s 를 입력하세요!" -#: ../dialogs/ConnectionDialog.py:142 -#: ../dialogs/FBDBlockDialog.py:154 +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 msgid "Form isn't complete. Name must be filled!" msgstr "형식이 완성되지 않았습니다. 이름을 입력하세요" -#: ../dialogs/SearchInProjectDialog.py:145 -msgid "Form isn't complete. Pattern to search must be filled!" -msgstr "형식이 완성되지 않았습니다. 검색어를 입력하세요!" - -#: ../dialogs/FBDBlockDialog.py:152 +#: ../dialogs/FBDBlockDialog.py:232 msgid "Form isn't complete. Valid block type must be selected!" msgstr "형식이 완성되지 않았습니다. 블럭 타입을 선택하세요!" -#: ../dialogs/FindInPouDialog.py:67 +#: ../dialogs/FindInPouDialog.py:72 msgid "Forward" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:44 +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 msgid "Function" msgstr "함수" -#: ../IDEFrame.py:329 +#: ../IDEFrame.py:349 msgid "Function &Block" msgstr "함수 &블록" -#: ../IDEFrame.py:1969 -#: ../dialogs/SearchInProjectDialog.py:45 +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 msgid "Function Block" msgstr "함수 블럭(Function Block)" -#: ../controls/VariablePanel.py:741 +#: ../controls/VariablePanel.py:854 msgid "Function Block Types" msgstr "함수 블럭 타입" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "Function Blocks" msgstr "함수 블럭(Function Blocks)" -#: ../editors/Viewer.py:236 +#: ../editors/Viewer.py:249 msgid "Function Blocks can't be used in Functions!" msgstr "함수 블럭을 함수에서 사용할 수 없습니다!" -#: ../editors/Viewer.py:238 -msgid "Function Blocks can't be used in Transitions!" -msgstr "함수 블럭을 트랜지션에서 사용할 수 없습니다!" - -#: ../PLCControler.py:2055 +#: ../PLCControler.py:2343 #, python-format msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" msgstr "함수 블럭 \"%s\" 을 함수에 붙여 넣기 할 수 없습니다!!!" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "Functions" msgstr "함수 (Functions)" -#: ../PLCOpenEditor.py:138 -#, fuzzy +#: ../PLCOpenEditor.py:117 msgid "Generate Program" -msgstr "프로그램 생성\tCTRL+G" - -#: ../ProjectController.py:510 +msgstr "" + +#: ../ProjectController.py:703 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "IEC-61131 기반의 ST/IL/SFC 코드 생성중...\n" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid "Global" msgstr "글로벌" -#: ../editors/GraphicViewer.py:131 -#, fuzzy +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 msgid "Go to current value" -msgstr "강제 데이터 입력" - -#: ../controls/ProjectPropertiesPanel.py:173 +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:174 msgid "Graphics" msgstr "그래픽" @@ -1873,114 +1788,110 @@ msgid "Greater than or equal to" msgstr "크거나 같은 값 일때" -#: ../controls/ProjectPropertiesPanel.py:134 +#: ../controls/ProjectPropertiesPanel.py:135 msgid "Grid Resolution:" msgstr "격자 해상도:" -#: ../controls/ProjectPropertiesPanel.py:120 +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:121 msgid "Height:" msgstr "높이:" -#: ../editors/FileManagementPanel.py:303 +#: ../editors/FileManagementPanel.py:85 msgid "Home Directory:" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:150 +#: ../controls/ProjectPropertiesPanel.py:151 msgid "Horizontal:" msgstr "가로:" -#: ../dialogs/DurationEditorDialog.py:44 +#: ../dialogs/DurationEditorDialog.py:45 msgid "Hours:" msgstr "" -#: ../plcopen/structures.py:279 -msgid "" -"Hysteresis\n" -"The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." -msgstr "" -"이력(Hysteresis)\n" -"이력 펑션 블럭은 두 부동 소수점(REAL) 입력 XIN1, XIN2의 차이에 의한 이력(Hysteresis) 참,거짓(불린) 출력을 제공합니다" - -#: ../ProjectController.py:827 -msgid "IEC-61131-3 code generation failed !\n" -msgstr "IEC-61131-3 코드 생성 실패!\n" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "IL" msgstr "IL" -#: ../Beremiz_service.py:356 -#: ../Beremiz_service.py:357 +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 msgid "IP is not valid!" msgstr "IP를 정확히 입력하세요!" -#: ../svgui/svgui.py:17 -#: ../svgui/svgui.py:18 +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 msgid "Import SVG" msgstr "SVG 가져오기" -#: ../controls/VariablePanel.py:76 -#: ../dialogs/FBDVariableDialog.py:34 +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 msgid "InOut" msgstr "입출력" -#: ../controls/VariablePanel.py:263 -#, python-format -msgid "Incompatible data types between \"%s\" and \"%s\"" -msgstr "\"%s\" 와 \"%s\"간의 데이터 타입이 호환되지 않습니다" - -#: ../controls/VariablePanel.py:274 -#, python-format -msgid "Incompatible size of data between \"%s\" and \"%s\"" -msgstr "\"%s\"와 \"%s\"간의 데이터 크기가 호환되지 않습니다" - -#: ../controls/VariablePanel.py:270 +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../controls/VariablePanel.py:282 #, python-format msgid "Incompatible size of data between \"%s\" and \"BOOL\"" msgstr "\"%s\"와 \"BOOL\"간의 데이터 크기가 호환되지 않습니다" -#: ../dialogs/ActionBlockDialog.py:37 +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Indicator" msgstr "지시기(Indicator)" -#: ../editors/Viewer.py:492 -#, fuzzy +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:611 msgid "Initial Step" -msgstr "초기 값" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 +msgstr "" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 msgid "Initial Value" msgstr "초기 값" -#: ../editors/DataTypeEditor.py:178 -#: ../editors/DataTypeEditor.py:209 -#: ../editors/DataTypeEditor.py:265 -#: ../editors/DataTypeEditor.py:303 +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 msgid "Initial Value:" msgstr "초기 값:" -#: ../svgui/svgui.py:21 +#: ../svgui/svgui.py:48 msgid "Inkscape" msgstr "Inkscape" -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCTransitionDialog.py:66 -#: ../dialogs/SFCTransitionDialog.py:137 +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 msgid "Inline" msgstr "인라인" -#: ../controls/VariablePanel.py:76 -#: ../dialogs/BrowseLocationsDialog.py:36 -#: ../dialogs/FBDVariableDialog.py:33 -#: ../dialogs/SFCStepDialog.py:61 +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Input" msgstr "입력" -#: ../dialogs/FBDBlockDialog.py:78 +#: ../dialogs/FBDBlockDialog.py:96 msgid "Inputs:" msgstr "입력(Inputs):" @@ -1988,125 +1899,114 @@ msgid "Insertion (into)" msgstr "대상에 삽입" -#: ../plcopen/plcopen.py:1833 +#: ../plcopen/plcopen.py:1696 #, python-format msgid "Instance with id %d doesn't exist!" msgstr "ID %d의 인스턴스가 존재하지 않습니다!" -#: ../editors/ResourceEditor.py:247 +#: ../editors/ResourceEditor.py:264 msgid "Instances:" msgstr "인스턴스:" -#: ../plcopen/structures.py:259 -msgid "" -"Integral\n" -"The integral function block integrates the value of input XIN over time." -msgstr "" -"적분\n" -"적분 함수 블럭은 시간에 따른 입력 XIN의 데이터를 적분합니다" - -#: ../controls/VariablePanel.py:75 +#: ../controls/VariablePanel.py:70 msgid "Interface" msgstr "인터페이스" -#: ../editors/ResourceEditor.py:71 +#: ../editors/ResourceEditor.py:72 msgid "Interrupt" msgstr "인터럽트" -#: ../editors/ResourceEditor.py:67 +#: ../editors/ResourceEditor.py:68 msgid "Interval" msgstr "간격 (Interval)" -#: ../PLCControler.py:2032 -#: ../PLCControler.py:2070 +#: ../PLCControler.py:2331 msgid "Invalid plcopen element(s)!!!" msgstr "알 수 없는 plcopen 항목입니다!!!" -#: ../canfestival/config_utils.py:376 -#: ../canfestival/config_utils.py:637 -#, python-format -msgid "Invalid type \"%s\"-> %d != %d for location\"%s\"" -msgstr "타입 에러 \"%s\" -> %d != %d 위치 \"%s\"" - -#: ../dialogs/ForceVariableDialog.py:167 -#, python-format -msgid "Invalid value \"%s\" for \"%s\" variable!" -msgstr "\"%s\"값은 \"%s\" 변수에 적합하지 않습니다!" - -#: ../controls/DebugVariablePanel.py:153 -#: ../controls/DebugVariablePanel.py:156 +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 #, python-format msgid "Invalid value \"%s\" for debug variable" msgstr "\"%s\"값은 디버그 변수에 적합하지 않습니다!" -#: ../controls/VariablePanel.py:244 -#: ../controls/VariablePanel.py:247 -#, fuzzy, python-format +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format msgid "Invalid value \"%s\" for variable grid element" -msgstr "\"%s\"값은 \"%s\" 변수에 적합하지 않습니다!" - -#: ../editors/Viewer.py:221 -#: ../editors/Viewer.py:224 +msgstr "" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 #, python-format msgid "Invalid value \"%s\" for viewer block" msgstr "\"%s\" 값은 뷰어 블록에 적합하지 않습니다!" +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + #: ../dialogs/DurationEditorDialog.py:121 msgid "" "Invalid value!\n" "You must fill a numeric value." msgstr "" -#: ../editors/Viewer.py:497 +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 msgid "Jump" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "LD" msgstr "LD" -#: ../editors/LDViewer.py:215 -#: ../editors/LDViewer.py:231 +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 #, python-format msgid "Ladder element with id %d is on more than one rung." msgstr "래더 항목 ID %d이 하나 이상의 Rung에 존재합니다" -#: ../dialogs/PouTransitionDialog.py:86 -#: ../dialogs/PouActionDialog.py:83 -#: ../dialogs/PouDialog.py:102 +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 msgid "Language" msgstr "언어" -#: ../controls/ProjectPropertiesPanel.py:186 +#: ../controls/ProjectPropertiesPanel.py:187 msgid "Language (optional):" msgstr "언어(옵션):" -#: ../dialogs/PouTransitionDialog.py:60 -#: ../dialogs/PouActionDialog.py:56 -#: ../dialogs/PouDialog.py:71 +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 msgid "Language:" msgstr "언어:" -#: ../ProjectController.py:1451 +#: ../ProjectController.py:1797 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "최근 빌드가 이미 타겟과 일치합니다. 전송합니다...\n" -#: ../Beremiz_service.py:324 +#: ../Beremiz_service.py:273 msgid "Launch WX GUI inspector" msgstr "WX GUI Inspector 실행" -#: ../Beremiz_service.py:323 +#: ../Beremiz_service.py:272 msgid "Launch a live Python shell" msgstr "Live Python Shell 실행" -#: ../editors/Viewer.py:428 +#: ../editors/Viewer.py:544 msgid "Left" msgstr "좌측" -#: ../dialogs/LDPowerRailDialog.py:55 +#: ../dialogs/LDPowerRailDialog.py:63 msgid "Left PowerRail" msgstr "좌측 전원 레일" @@ -2122,92 +2022,95 @@ msgid "Less than or equal to" msgstr "작거나 같은 값 일때" -#: ../IDEFrame.py:600 +#: ../IDEFrame.py:631 msgid "Library" msgstr "라이브러리" +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "" + #: ../plcopen/iec_std.csv:73 msgid "Limitation" msgstr "한도" -#: ../targets/toolchain_gcc.py:142 +#: ../targets/toolchain_gcc.py:202 msgid "Linking :\n" msgstr "링크 중 : \n" -#: ../controls/VariablePanel.py:77 -#: ../dialogs/DiscoveryDialog.py:110 +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 msgid "Local" msgstr "로컬" -#: ../ProjectController.py:1353 +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "" + +#: ../ProjectController.py:1703 msgid "Local service discovery failed!\n" msgstr "" -#: ../controls/VariablePanel.py:58 +#: ../controls/VariablePanel.py:53 msgid "Location" msgstr "위치" -#: ../dialogs/BrowseLocationsDialog.py:61 +#: ../dialogs/BrowseLocationsDialog.py:72 msgid "Locations available:" msgstr "가능한 위치:" -#: ../Beremiz.py:393 -msgid "Log Console" -msgstr "로그 콘솔" - #: ../plcopen/iec_std.csv:25 msgid "Logarithm to base 10" msgstr "상용로그(상용대수)" -#: ../connectors/PYRO/__init__.py:55 +#: ../connectors/PYRO/__init__.py:94 #, python-format msgid "MDNS resolution failure for '%s'\n" msgstr "" -#: ../canfestival/SlaveEditor.py:37 -#: ../canfestival/NetworkEditor.py:67 +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 msgid "Map Variable" msgstr "변수 맵" -#: ../features.py:6 +#: ../features.py:31 msgid "Map located variables over CANopen" msgstr "CANopen 변수 맵" -#: ../canfestival/NetworkEditor.py:89 +#: ../canfestival/NetworkEditor.py:106 msgid "Master" msgstr "CAN 마스터" -#: ../ConfigTreeNode.py:480 -#, python-format -msgid "Max count (%d) reached for this confnode of type %s " -msgstr "플러그인 최대 카운트 (%d)에 도달했습니다. %s" +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" #: ../plcopen/iec_std.csv:71 msgid "Maximum" msgstr "최대값" -#: ../editors/DataTypeEditor.py:232 +#: ../editors/DataTypeEditor.py:239 msgid "Maximum:" msgstr "최대값:" -#: ../dialogs/BrowseLocationsDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Memory" msgstr "메모리" -#: ../IDEFrame.py:568 -#, fuzzy +#: ../IDEFrame.py:599 msgid "Menu ToolBar" -msgstr "툴바" - -#: ../dialogs/DurationEditorDialog.py:48 +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:49 msgid "Microseconds:" msgstr "" -#: ../editors/Viewer.py:433 +#: ../editors/Viewer.py:549 msgid "Middle" msgstr "중간" -#: ../dialogs/DurationEditorDialog.py:47 +#: ../dialogs/DurationEditorDialog.py:48 msgid "Milliseconds:" msgstr "" @@ -2215,86 +2118,66 @@ msgid "Minimum" msgstr "최소값" -#: ../editors/DataTypeEditor.py:219 +#: ../editors/DataTypeEditor.py:226 msgid "Minimum:" msgstr "최소값:" -#: ../dialogs/DurationEditorDialog.py:45 +#: ../dialogs/DurationEditorDialog.py:46 msgid "Minutes:" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:210 +#: ../controls/ProjectPropertiesPanel.py:211 msgid "Miscellaneous" msgstr "기타" -#: ../dialogs/LDElementDialog.py:59 +#: ../dialogs/LDElementDialog.py:63 msgid "Modifier:" msgstr "수정자:" -#: ../PLCGenerator.py:703 -#: ../PLCGenerator.py:936 -#, python-format -msgid "More than one connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "\"%s\"에 대응하는 하나 이상의 연결이 \"%s\" POU에서 발견 되었습니다" - -#: ../dialogs/ActionBlockDialog.py:141 -#, fuzzy +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 msgid "Move action down" -msgstr "하단 이동" - -#: ../dialogs/ActionBlockDialog.py:140 -#, fuzzy +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:139 msgid "Move action up" -msgstr "상단 이동" - -#: ../controls/DebugVariablePanel.py:185 -#, fuzzy -msgid "Move debug variable down" -msgstr "출력 변수를 찾을 수 없습니다" - -#: ../controls/DebugVariablePanel.py:184 -#, fuzzy -msgid "Move debug variable up" -msgstr "출력 변수를 찾을 수 없습니다" +msgstr "" #: ../controls/CustomEditableListBox.py:43 msgid "Move down" msgstr "하단 이동" -#: ../editors/DataTypeEditor.py:348 -#, fuzzy +#: ../editors/DataTypeEditor.py:355 msgid "Move element down" -msgstr "하단 이동" - -#: ../editors/DataTypeEditor.py:347 -#, fuzzy +msgstr "" + +#: ../editors/DataTypeEditor.py:354 msgid "Move element up" -msgstr "상단 이동" - -#: ../editors/ResourceEditor.py:254 -#, fuzzy +msgstr "" + +#: ../editors/ResourceEditor.py:271 msgid "Move instance down" -msgstr "하단 이동" - -#: ../editors/ResourceEditor.py:253 -#, fuzzy +msgstr "" + +#: ../editors/ResourceEditor.py:270 msgid "Move instance up" -msgstr "상단 이동" - -#: ../editors/ResourceEditor.py:225 -#, fuzzy +msgstr "" + +#: ../editors/ResourceEditor.py:242 msgid "Move task down" -msgstr "하단 이동" - -#: ../editors/ResourceEditor.py:224 -#, fuzzy +msgstr "" + +#: ../editors/ResourceEditor.py:241 msgid "Move task up" -msgstr "상단 이동" - -#: ../IDEFrame.py:75 -#: ../IDEFrame.py:90 -#: ../IDEFrame.py:120 -#: ../IDEFrame.py:161 +msgstr "" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 msgid "Move the view" msgstr "화면을 드래그하여 이동" @@ -2302,15 +2185,13 @@ msgid "Move up" msgstr "상단 이동" -#: ../controls/VariablePanel.py:381 -#, fuzzy +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 msgid "Move variable down" -msgstr "하단 이동" - -#: ../controls/VariablePanel.py:380 -#, fuzzy +msgstr "" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 msgid "Move variable up" -msgstr "상단 이동" +msgstr "" #: ../plcopen/iec_std.csv:74 msgid "Multiplexer (select 1 of N)" @@ -2320,28 +2201,26 @@ msgid "Multiplication" msgstr "곱셈" -#: ../editors/FileManagementPanel.py:301 -#, fuzzy +#: ../editors/FileManagementPanel.py:83 msgid "My Computer:" -msgstr "컴파일러" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 -#: ../editors/ResourceEditor.py:67 -#: ../editors/ResourceEditor.py:76 +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Name" msgstr "이름" -#: ../Beremiz_service.py:381 +#: ../Beremiz_service.py:334 msgid "Name must not be null!" msgstr "이름은 널(null)이 되어서는 안됩니다!" -#: ../dialogs/ConnectionDialog.py:65 -#: ../dialogs/FBDVariableDialog.py:89 -#: ../dialogs/LDElementDialog.py:88 -#: ../dialogs/SFCStepDialog.py:51 -#: ../dialogs/FBDBlockDialog.py:70 +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 msgid "Name:" msgstr "이름:" @@ -2349,15 +2228,20 @@ msgid "Natural logarithm" msgstr "자연 로그" -#: ../editors/Viewer.py:403 -#: ../dialogs/LDElementDialog.py:67 +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 msgid "Negated" msgstr "역 방향(Negate)" -#: ../Beremiz.py:307 -#: ../Beremiz.py:342 -#: ../PLCOpenEditor.py:125 -#: ../PLCOpenEditor.py:167 +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 msgid "New" msgstr "새로 만들기" @@ -2365,30 +2249,25 @@ msgid "New item" msgstr "새로운 아이템" -#: ../editors/Viewer.py:402 +#: ../editors/Viewer.py:518 msgid "No Modifier" msgstr "수정자 없음" -#: ../PLCControler.py:2929 -msgid "No PLC project found" -msgstr "PLC 프로젝트를 찾을 수 없습니다" - -#: ../ProjectController.py:1478 +#: ../ProjectController.py:1826 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "전송할 PLC 파일이 없습니다 (빌드 성공 여부를 확인하세요)\n" -#: ../PLCGenerator.py:1321 +#: ../PLCGenerator.py:1631 #, python-format msgid "No body defined in \"%s\" POU" msgstr "\"%s\" POU에 바디(body)가 정의되어 있지 않습니다" -#: ../PLCGenerator.py:722 -#: ../PLCGenerator.py:945 -#, python-format -msgid "No connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "\"%s\"에 대응하는 연결을 \"%s\" POU에서 찾을 수 없습니다" - -#: ../PLCOpenEditor.py:370 +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:357 msgid "" "No documentation available.\n" "Coming soon." @@ -2396,75 +2275,69 @@ "현재 지원되는 도움 문서가 없습니다.\n" "지원 예정" -#: ../PLCGenerator.py:744 +#: ../PLCGenerator.py:829 #, python-format msgid "No informations found for \"%s\" block" msgstr "\"%s\"블럭에 대한 정보를 찾을 수 없습니다" -#: ../plcopen/structures.py:167 -msgid "No output variable found" -msgstr "출력 변수를 찾을 수 없습니다" - -#: ../Beremiz_service.py:394 -msgid "No running PLC" -msgstr "실행중인 PLC가 없습니다" +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" #: ../controls/SearchResultPanel.py:169 msgid "No search results available." msgstr "검색된 결과가 없습니다" -#: ../svgui/svgui.py:98 +#: ../svgui/svgui.py:134 #, python-format msgid "No such SVG file: %s\n" msgstr "선택하신 %s파일은 없습니다\n" -#: ../canfestival/config_utils.py:632 -#, python-format -msgid "No such index/subindex (%x,%x) (variable %s)" -msgstr "인덱스/서브 인덱스 (%x,%x)(변수 %s) 에러" - -#: ../canfestival/config_utils.py:361 -#, python-format -msgid "No such index/subindex (%x,%x) in ID : %d (variable %s)" -msgstr "인덱스/서브 인덱스 (%x,%x) ID 위치: %d (변수 %s)" +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" #: ../dialogs/BrowseValuesLibraryDialog.py:83 msgid "No valid value selected!" msgstr "유효한 데이터 값을 선택하세요!" -#: ../PLCGenerator.py:1319 +#: ../PLCGenerator.py:1629 #, python-format msgid "No variable defined in \"%s\" POU" msgstr "\"%s\" POU에 정의된 변수가 없습니다" -#: ../canfestival/SlaveEditor.py:49 -#: ../canfestival/NetworkEditor.py:79 -msgid "Node infos" -msgstr "노드 정보" - -#: ../canfestival/config_utils.py:354 -#, python-format -msgid "Non existing node ID : %d (variable %s)" -msgstr "Node ID : %d(변수%s)를 찾을 수 없습니다" - -#: ../controls/VariablePanel.py:69 +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 msgid "Non-Retain" msgstr "유지 안함(No Retain)" -#: ../dialogs/LDElementDialog.py:62 +#: ../dialogs/LDElementDialog.py:75 msgid "Normal" msgstr "기본(Normal)" -#: ../canfestival/config_utils.py:383 -#, python-format -msgid "Not PDO mappable variable : '%s' (ID:%d,Idx:%x,sIdx:%x))" -msgstr "PDO 맵핑 변수가 아닙니다 : '%s'(ID:%d,ldx:%x,sldx:%x))" +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" #: ../plcopen/iec_std.csv:80 msgid "Not equal to" msgstr "같지 않을 때" -#: ../dialogs/SFCDivergenceDialog.py:80 +#: ../dialogs/SFCDivergenceDialog.py:89 msgid "Number of sequences:" msgstr "시퀀스 넘버:" @@ -2472,299 +2345,310 @@ msgid "Numerical" msgstr "수치(Numeric)" -#: ../plcopen/structures.py:247 -msgid "" -"Off-delay timer\n" -"The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." -msgstr "" -"오프 딜레이 타이머\n" -"오프 딜레이 타이머는 입력이 FALSE로 진행되는 주기를 수정하여 출력을 FALSE로 셋팅합니다" - -#: ../plcopen/structures.py:242 -msgid "" -"On-delay timer\n" -"The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." -msgstr "" -"온 딜레이 타이머\n" -"온 딜레이 타이머는 입력이 TRUE로 진행되는 주기를 수정하여 출력을 TRUE로 셋팅합니다" - -#: ../dialogs/SearchInProjectDialog.py:93 +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:84 msgid "Only Elements" msgstr "구성원 내부 검색" -#: ../Beremiz.py:309 -#: ../Beremiz.py:343 -#: ../PLCOpenEditor.py:127 -#: ../PLCOpenEditor.py:168 -#, fuzzy +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 msgid "Open" msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"프로젝트 열기\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"파일 열기" - -#: ../svgui/svgui.py:107 + +#: ../svgui/svgui.py:143 msgid "Open Inkscape" msgstr "잉크스케이프 열기" -#: ../ProjectController.py:1530 +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" + +#: ../ProjectController.py:1878 msgid "Open a file explorer to manage project files" msgstr "" -#: ../wxglade_hmi/wxglade_hmi.py:109 +#: ../wxglade_hmi/wxglade_hmi.py:155 msgid "Open wxGlade" msgstr "WxGlade 열기" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Option" msgstr "옵션" -#: ../dialogs/FindInPouDialog.py:76 -#, fuzzy +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 msgid "Options" -msgstr "옵션" - -#: ../controls/ProjectPropertiesPanel.py:97 +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:98 msgid "Organization (optional):" msgstr "구성단체(옵션):" -#: ../canfestival/SlaveEditor.py:47 -#: ../canfestival/NetworkEditor.py:77 +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 msgid "Other Profile" msgstr "다른 프로필" -#: ../controls/VariablePanel.py:76 -#: ../dialogs/BrowseLocationsDialog.py:37 -#: ../dialogs/FBDVariableDialog.py:35 -#: ../dialogs/SFCStepDialog.py:65 +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Output" msgstr "출력" -#: ../canfestival/SlaveEditor.py:36 -#: ../canfestival/NetworkEditor.py:66 +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 msgid "PDO Receive" msgstr "수신 PDO" -#: ../canfestival/SlaveEditor.py:35 -#: ../canfestival/NetworkEditor.py:65 +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 msgid "PDO Transmit" msgstr "전송 PDO" -#: ../plcopen/structures.py:269 -msgid "" -"PID\n" -"The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." -msgstr "" -"PID\n" -"PID 펑션 블럭은 폐쇄 루프 제어를 위한 다음과 같은 비례상수, 미분, 적분등의 기본 제어 규칙을 제공합니다" - -#: ../targets/toolchain_gcc.py:107 +#: ../targets/toolchain_gcc.py:167 msgid "PLC :\n" msgstr "PLC :\n" -#: ../ProjectController.py:1096 -#: ../ProjectController.py:1398 -#, python-format -msgid "PLC is %s\n" -msgstr "현재 PLC %s\n" - -#: ../PLCOpenEditor.py:313 -#: ../PLCOpenEditor.py:391 +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "" + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "" + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" msgstr "PLCOpen files (*.xml)|*.xml|모든 파일|*.*" -#: ../PLCOpenEditor.py:175 -#: ../PLCOpenEditor.py:231 +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 msgid "PLCOpenEditor" msgstr "PLCOpenEditor" -#: ../dialogs/PouDialog.py:98 +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "" + +#: ../dialogs/PouDialog.py:101 msgid "POU Name" msgstr "POU 이름" -#: ../dialogs/PouDialog.py:56 +#: ../dialogs/PouDialog.py:58 msgid "POU Name:" msgstr "POU 이름:" -#: ../dialogs/PouDialog.py:100 +#: ../dialogs/PouDialog.py:103 msgid "POU Type" msgstr "POU 타입" -#: ../dialogs/PouDialog.py:63 +#: ../dialogs/PouDialog.py:65 msgid "POU Type:" msgstr "POU 타입:" -#: ../Beremiz.py:322 -#: ../PLCOpenEditor.py:141 +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 msgid "Page Setup" msgstr "인쇄 페이지 설정" -#: ../controls/ProjectPropertiesPanel.py:110 +#: ../controls/ProjectPropertiesPanel.py:111 msgid "Page Size (optional):" msgstr "페이지 크기(옵션):" -#: ../PLCOpenEditor.py:476 +#: ../IDEFrame.py:2613 #, python-format msgid "Page: %d" msgstr "인쇄 페이지: %d" -#: ../controls/PouInstanceVariablesPanel.py:41 -#, fuzzy +#: ../controls/PouInstanceVariablesPanel.py:124 msgid "Parent instance" -msgstr "인스턴스 삭제" - -#: ../IDEFrame.py:350 -#: ../IDEFrame.py:402 -#: ../editors/Viewer.py:537 +msgstr "" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 msgid "Paste" msgstr "붙여넣기" -#: ../IDEFrame.py:1900 +#: ../IDEFrame.py:1868 msgid "Paste POU" msgstr "POU 붙여넣기" -#: ../dialogs/SearchInProjectDialog.py:64 +#: ../dialogs/SearchInProjectDialog.py:56 msgid "Pattern to search:" msgstr "검색할 문자열 패턴:" -#: ../dialogs/LDPowerRailDialog.py:64 +#: ../dialogs/LDPowerRailDialog.py:74 msgid "Pin number:" msgstr "핀 넘버:" -#: ../editors/Viewer.py:2289 -#: ../editors/Viewer.py:2594 -#: ../editors/SFCViewer.py:696 +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 msgid "Please choose a target" msgstr "타겟을 선택하세요" -#: ../editors/Viewer.py:2112 -#: ../editors/Viewer.py:2114 -#: ../editors/Viewer.py:2630 -#: ../editors/Viewer.py:2632 +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 msgid "Please enter comment text" msgstr "코멘트를 입력하세요" -#: ../editors/SFCViewer.py:359 -#: ../editors/SFCViewer.py:381 -#: ../editors/SFCViewer.py:725 +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 msgid "Please enter step name" msgstr "스텝 이름을 입력하세요" -#: ../dialogs/ForceVariableDialog.py:153 +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "텍스트를 입력하세요" + +#: ../dialogs/ForceVariableDialog.py:163 #, python-format msgid "Please enter value for a \"%s\" variable:" msgstr "\"%s\" 변수의 데이터를 입력하세요" -#: ../Beremiz_service.py:366 +#: ../Beremiz_service.py:319 msgid "Port number must be 0 <= port <= 65535!" msgstr "포트 번호는 0~65535까지 유효합니다!" -#: ../Beremiz_service.py:366 +#: ../Beremiz_service.py:319 msgid "Port number must be an integer!" msgstr "포트 번호는 숫자로만 입력하세요!" -#: ../editors/GraphicViewer.py:105 -msgid "Position:" -msgstr "위치:" - -#: ../editors/Viewer.py:476 -#, fuzzy +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 msgid "Power Rail" -msgstr "좌측 전원 레일" - -#: ../dialogs/LDPowerRailDialog.py:36 +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:51 msgid "Power Rail Properties" msgstr "전원 레일 속성" -#: ../Beremiz.py:324 -#: ../PLCOpenEditor.py:143 +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 msgid "Preview" msgstr "인쇄 페이지 미리보기" -#: ../dialogs/SFCDivergenceDialog.py:93 -#: ../dialogs/LDPowerRailDialog.py:78 -#: ../dialogs/ConnectionDialog.py:78 -#: ../dialogs/FBDVariableDialog.py:97 -#: ../dialogs/SFCTransitionDialog.py:96 -#: ../dialogs/LDElementDialog.py:101 -#: ../dialogs/SFCStepDialog.py:79 -#: ../dialogs/FBDBlockDialog.py:103 +#: ../dialogs/BlockPreviewDialog.py:57 msgid "Preview:" msgstr "인쇄 페이지 미리보기:" -#: ../Beremiz.py:326 -#: ../Beremiz.py:346 -#: ../PLCOpenEditor.py:145 -#: ../PLCOpenEditor.py:171 +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 msgid "Print" msgstr "인쇄" -#: ../IDEFrame.py:1155 +#: ../IDEFrame.py:1079 msgid "Print preview" msgstr "인쇄 미리보기" -#: ../editors/ResourceEditor.py:67 +#: ../editors/ResourceEditor.py:68 msgid "Priority" msgstr "우선권" -#: ../dialogs/SFCTransitionDialog.py:83 +#: ../dialogs/SFCTransitionDialog.py:90 msgid "Priority:" msgstr "우선권:" -#: ../controls/ProjectPropertiesPanel.py:80 +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:81 msgid "Product Name (required):" msgstr "제품 이름(필수):" -#: ../controls/ProjectPropertiesPanel.py:82 +#: ../controls/ProjectPropertiesPanel.py:83 msgid "Product Release (optional):" msgstr "제품 출시 번호(옵션):" -#: ../controls/ProjectPropertiesPanel.py:81 +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:82 msgid "Product Version (required):" msgstr "제품 버젼(필수):" -#: ../IDEFrame.py:1972 -#: ../dialogs/SearchInProjectDialog.py:46 +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 msgid "Program" msgstr "프로그램" -#: ../PLCOpenEditor.py:360 +#: ../PLCOpenEditor.py:347 msgid "Program was successfully generated!" msgstr "프로그램이 성공적으로 생성되었습니다!" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Programs" msgstr "프로그램(Programs)" -#: ../editors/Viewer.py:230 +#: ../editors/Viewer.py:243 msgid "Programs can't be used by other POUs!" msgstr "다른 POU에서 사용할 수 없는 프로그램입니다!" -#: ../controls/ProjectPropertiesPanel.py:84 -#: ../IDEFrame.py:553 +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 msgid "Project" msgstr "프로젝트" #: ../controls/SearchResultPanel.py:173 -#, fuzzy, python-format +#, python-format msgid "Project '%s':" -msgstr "프로젝트" - -#: ../ProjectController.py:1529 +msgstr "" + +#: ../ProjectController.py:1877 msgid "Project Files" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:78 +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:79 msgid "Project Name (required):" msgstr "프로젝트 명(필수):" -#: ../controls/ProjectPropertiesPanel.py:79 +#: ../controls/ProjectPropertiesPanel.py:80 msgid "Project Version (optional):" msgstr "프로젝트 버젼(옵션):" -#: ../PLCControler.py:2916 +#: ../PLCControler.py:3164 msgid "" "Project file syntax error:\n" "\n" @@ -2772,103 +2656,90 @@ "프로젝트 파일 구문 오류:\n" "\n" -#: ../dialogs/ProjectDialog.py:32 +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 msgid "Project properties" msgstr "프로젝트 속성" -#: ../ConfigTreeNode.py:506 -#, python-format -msgid "Project tree layout do not match confnode.xml %s!=%s " -msgstr "프로젝트 레이아웃이 포함된 %s!=%s confnode.xml파일이 손상되거나 일치 하지 않습니다" - -#: ../PLCControler.py:96 +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "" + +#: ../PLCControler.py:99 msgid "Properties" msgstr "속성" -#: ../plcopen/structures.py:237 -msgid "" -"Pulse timer\n" -"The pulse timer can be used to generate output pulses of a given time duration." -msgstr "" -"펄스 타이머\n" -"펄스 타이머 펑션 블럭은 사용자가 설정한 시간동안 출력 신호를 생성하는데 사용합니다" - -#: ../features.py:8 +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "" + +#: ../features.py:33 msgid "Python file" msgstr "파이썬 파일" -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Qualifier" msgstr "한정자" -#: ../Beremiz_service.py:328 -#: ../Beremiz.py:329 -#: ../PLCOpenEditor.py:151 +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 msgid "Quit" msgstr "프로그램 종료" -#: ../plcopen/structures.py:202 -msgid "" -"RS bistable\n" -"The RS bistable is a latch where the Reset dominates." -msgstr "" -"RS 쌍안정 회로\n" -"RS 쌍안정(bistable) 회로 함수은 리셋 동작시 래치가 발생됩니다" - -#: ../plcopen/structures.py:274 -#, fuzzy -msgid "" -"Ramp\n" -"The RAMP function block is modelled on example given in the standard." -msgstr "" -"램프(Ramp)\n" -"램프(Ramp) 함수 블럭은 정지(Holdback) 기능이 추가된 기본 예제 모델입니다" - -#: ../editors/GraphicViewer.py:89 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 msgid "Range:" msgstr "범위:" -#: ../ProjectController.py:1525 +#: ../ProjectController.py:1873 msgid "Raw IEC code" msgstr "IEC 코드" -#: ../plcopen/structures.py:254 -msgid "" -"Real time clock\n" -"The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." -msgstr "" - -#: ../Beremiz.py:1039 -#, fuzzy, python-format +#: ../BeremizIDE.py:1047 +#, python-format msgid "Really delete node '%s'?" -msgstr "플러그인을 삭제하시겠습니까?" - -#: ../IDEFrame.py:340 -#: ../IDEFrame.py:398 +msgstr "" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 msgid "Redo" msgstr "되돌리기 취소" -#: ../dialogs/SFCTransitionDialog.py:57 -#: ../dialogs/SFCTransitionDialog.py:135 +#: ../dialogs/SFCTransitionDialog.py:75 msgid "Reference" msgstr "레퍼런스" -#: ../IDEFrame.py:408 -#: ../dialogs/DiscoveryDialog.py:105 +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 msgid "Refresh" msgstr "새로고침" -#: ../dialogs/SearchInProjectDialog.py:73 +#: ../dialogs/SearchInProjectDialog.py:66 msgid "Regular expression" msgstr "정규 표현식" -#: ../dialogs/FindInPouDialog.py:91 -#, fuzzy +#: ../dialogs/FindInPouDialog.py:96 msgid "Regular expressions" -msgstr "정규 표현식" - -#: ../controls/DebugVariablePanel.py:299 -#: ../editors/Viewer.py:1356 +msgstr "" + +#: ../editors/Viewer.py:1603 msgid "Release value" msgstr "강제 데이터 입력 해제" @@ -2876,55 +2747,59 @@ msgid "Remainder (modulo)" msgstr "잔여 (모듈)" -#: ../Beremiz.py:1040 -#, fuzzy, python-format +#: ../BeremizIDE.py:1048 +#, python-format msgid "Remove %s node" -msgstr "플러그인 제거" - -#: ../dialogs/ActionBlockDialog.py:139 -#, fuzzy +msgstr "" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:138 msgid "Remove action" -msgstr "선택" - -#: ../controls/DebugVariablePanel.py:183 -#, fuzzy -msgid "Remove debug variable" -msgstr "새로운 변수 생성" - -#: ../editors/DataTypeEditor.py:346 +msgstr "" + +#: ../editors/DataTypeEditor.py:353 msgid "Remove element" msgstr "" -#: ../editors/FileManagementPanel.py:281 +#: ../editors/FileManagementPanel.py:63 msgid "Remove file from left folder" msgstr "" -#: ../editors/ResourceEditor.py:252 -#, fuzzy +#: ../editors/ResourceEditor.py:269 msgid "Remove instance" -msgstr "인스턴스 삭제" - -#: ../canfestival/NetworkEditor.py:87 +msgstr "" + +#: ../canfestival/NetworkEditor.py:104 msgid "Remove slave" msgstr "슬레이브 제거" -#: ../editors/ResourceEditor.py:223 +#: ../editors/ResourceEditor.py:240 msgid "Remove task" msgstr "" -#: ../controls/VariablePanel.py:379 -#, fuzzy +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 msgid "Remove variable" -msgstr "새로운 변수 생성" - -#: ../IDEFrame.py:1976 +msgstr "" + +#: ../IDEFrame.py:1948 msgid "Rename" msgstr "이름 변경" -#: ../editors/FileManagementPanel.py:399 +#: ../editors/FileManagementPanel.py:181 msgid "Replace File" msgstr "" +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "" + #: ../plcopen/iec_std.csv:89 msgid "Replacement (within)" msgstr "내부 교체" @@ -2933,11 +2808,11 @@ msgid "Reset" msgstr "리셋 코일(Unlatch)" -#: ../editors/Viewer.py:521 +#: ../editors/Viewer.py:642 msgid "Reset Execution Order" msgstr "실행 순서 초기화" -#: ../IDEFrame.py:423 +#: ../IDEFrame.py:451 msgid "Reset Perspective" msgstr "" @@ -2945,43 +2820,30 @@ msgid "Reset search result" msgstr "" -#: ../editors/GraphicViewer.py:137 -msgid "Reset zoom and offset" -msgstr "" - -#: ../PLCControler.py:96 +#: ../BeremizIDE.py:979 ../PLCControler.py:99 msgid "Resources" msgstr "리소스" -#: ../controls/VariablePanel.py:67 +#: ../controls/VariablePanel.py:62 msgid "Retain" msgstr "유지 변수" -#: ../controls/VariablePanel.py:352 +#: ../controls/VariablePanel.py:424 msgid "Return Type:" msgstr "반환(Return) 타입:" -#: ../editors/Viewer.py:430 +#: ../editors/Viewer.py:546 msgid "Right" msgstr "우측" -#: ../dialogs/LDPowerRailDialog.py:60 +#: ../dialogs/LDPowerRailDialog.py:64 msgid "Right PowerRail" msgstr "우측 전원 레일" -#: ../editors/Viewer.py:404 -#: ../dialogs/LDElementDialog.py:80 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 msgid "Rising Edge" msgstr "라이징 엣지" -#: ../plcopen/structures.py:212 -msgid "" -"Rising edge detector\n" -"The output produces a single pulse when a rising edge is detected." -msgstr "" -"라이징 엣지 검출\n" -"출력부의 라이징 엣지를 검출합니다" - #: ../plcopen/iec_std.csv:65 msgid "Rotate left" msgstr "좌측으로 회전 (rotate)" @@ -2994,156 +2856,138 @@ msgid "Rounding up/down" msgstr "라운딩 업/다운" -#: ../ProjectController.py:1493 +#: ../ProjectController.py:1841 msgid "Run" msgstr "실행" -#: ../ProjectController.py:841 -#: ../ProjectController.py:850 -msgid "Runtime extensions C code generation failed !\n" -msgstr "확장 C 코드의 생성에 실패하였습니다 ! \n" - -#: ../canfestival/SlaveEditor.py:34 -#: ../canfestival/NetworkEditor.py:64 +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 msgid "SDO Client" msgstr "SDO 클라이언트" -#: ../canfestival/SlaveEditor.py:33 -#: ../canfestival/NetworkEditor.py:63 +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 msgid "SDO Server" msgstr "SDO 서버" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "SFC" msgstr "SFC" -#: ../plcopen/structures.py:197 -msgid "" -"SR bistable\n" -"The SR bistable is a latch where the Set dominates." -msgstr "" -"SR 쌍안정\n" -"SR 쌍안정 셋팅시 래치 스위치가 동작합니다" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "ST" msgstr "ST" -#: ../PLCOpenEditor.py:347 +#: ../PLCOpenEditor.py:334 msgid "ST files (*.st)|*.st|All files|*.*" msgstr "ST 파일 (*.st)|*.st|모든 파일|*.*" -#: ../svgui/svgui.py:92 +#: ../svgui/svgui.py:128 msgid "SVG files (*.svg)|*.svg|All files|*.*" msgstr "SVG 파일 (*.svg)|*svg|모든 파일|*.*" -#: ../features.py:10 +#: ../features.py:35 msgid "SVGUI" msgstr "SVGUI" -#: ../Beremiz.py:313 -#: ../Beremiz.py:344 -#: ../PLCOpenEditor.py:134 -#: ../PLCOpenEditor.py:169 -#, fuzzy +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 msgid "Save" msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"프로젝트 저장\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"파일 저장" - -#: ../Beremiz.py:345 -#: ../PLCOpenEditor.py:136 -#: ../PLCOpenEditor.py:170 -#, fuzzy + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 msgid "Save As..." msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"다른 이름으로 저장\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"다른 이름으로 저장..." - -#: ../Beremiz.py:315 -#, fuzzy + +#: ../BeremizIDE.py:224 msgid "Save as" msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"프로젝트 저장\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"파일 저장" - -#: ../dialogs/SearchInProjectDialog.py:76 + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:69 msgid "Scope" msgstr "검색 범위" -#: ../IDEFrame.py:592 -#: ../dialogs/SearchInProjectDialog.py:105 +#: ../IDEFrame.py:623 msgid "Search" msgstr "검색" -#: ../IDEFrame.py:360 -#: ../IDEFrame.py:404 -#: ../dialogs/SearchInProjectDialog.py:52 +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 msgid "Search in Project" msgstr "프로젝트 내부 검색" -#: ../dialogs/DurationEditorDialog.py:46 +#: ../dialogs/DurationEditorDialog.py:47 msgid "Seconds:" msgstr "" -#: ../IDEFrame.py:366 -#, fuzzy +#: ../IDEFrame.py:388 msgid "Select All" -msgstr "모두 선택\tCTRL+A" - -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:330 -#: ../editors/Viewer.py:277 +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Select a variable class:" msgstr "변수 클래스 선택:" -#: ../ProjectController.py:1013 +#: ../ProjectController.py:1257 msgid "Select an editor:" msgstr "" -#: ../controls/PouInstanceVariablesPanel.py:197 -#, fuzzy +#: ../controls/PouInstanceVariablesPanel.py:281 msgid "Select an instance" -msgstr "인스턴스 삭제" - -#: ../IDEFrame.py:576 +msgstr "" + +#: ../IDEFrame.py:607 msgid "Select an object" msgstr "오브젝트 선택" +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" + #: ../plcopen/iec_std.csv:70 msgid "Selection" msgstr "선택" -#: ../dialogs/SFCDivergenceDialog.py:62 +#: ../dialogs/SFCDivergenceDialog.py:65 msgid "Selection Convergence" msgstr "수렴(Convergence) 선택" -#: ../dialogs/SFCDivergenceDialog.py:55 +#: ../dialogs/SFCDivergenceDialog.py:64 msgid "Selection Divergence" msgstr "벡터 발산(Divergence) 선택" -#: ../plcopen/structures.py:207 -msgid "" -"Semaphore\n" -"The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." -msgstr "" -"세마포어(semaphore)\n" -"세마포어 함수 블럭은 소프트웨어적으로 동시에 두 개 이상의 프로그램 요소가 실행될 수 있는 다중 프로그래밍 환경에서 한순간에 반드시 하나의 작업에 의해 접근되어야 하는 임계 영역 및 상호 배제 원리를 지키기 위해 사용됩니다" - -#: ../dialogs/DiscoveryDialog.py:84 +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:85 msgid "Services available:" msgstr "서비스 이용 가능:" -#: ../dialogs/LDElementDialog.py:72 +#: ../dialogs/LDElementDialog.py:76 msgid "Set" msgstr "셋팅 코일(Latch)" @@ -3155,27 +2999,27 @@ msgid "Shift right" msgstr "우측이로 이동(Shift)" -#: ../ProjectController.py:1519 +#: ../ProjectController.py:1867 msgid "Show IEC code generated by PLCGenerator" msgstr "PLCGenerator가 생성한 IEC 코드 보기" -#: ../canfestival/canfestival.py:288 +#: ../canfestival/canfestival.py:389 msgid "Show Master" msgstr "마스터 보기" -#: ../canfestival/canfestival.py:289 +#: ../canfestival/canfestival.py:390 msgid "Show Master generated by config_utils" msgstr "config_util(설정 유틸리티)에 의해 생성된 마스터 보기" -#: ../ProjectController.py:1517 +#: ../ProjectController.py:1865 msgid "Show code" msgstr "코드 보기" -#: ../dialogs/SFCDivergenceDialog.py:74 +#: ../dialogs/SFCDivergenceDialog.py:67 msgid "Simultaneous Convergence" msgstr "동시 수렴" -#: ../dialogs/SFCDivergenceDialog.py:68 +#: ../dialogs/SFCDivergenceDialog.py:66 msgid "Simultaneous Divergence" msgstr "동시 발산" @@ -3183,62 +3027,79 @@ msgid "Sine" msgstr "Sine" -#: ../editors/ResourceEditor.py:67 +#: ../editors/ResourceEditor.py:68 msgid "Single" msgstr "싱글" +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + #: ../plcopen/iec_std.csv:23 msgid "Square root (base 2)" msgstr "제곱근 (SQRT: base 2)" -#: ../plcopen/structures.py:193 +#: ../plcopen/definitions.py:48 msgid "Standard function blocks" msgstr "기본 함수 블럭" -#: ../Beremiz_service.py:319 -#: ../ProjectController.py:1495 +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 msgid "Start PLC" msgstr "PLC 시작" -#: ../ProjectController.py:819 +#: ../ProjectController.py:1046 #, python-format msgid "Start build in %s\n" msgstr "%s에서 빌드를 시작합니다\n" -#: ../ProjectController.py:1314 +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "" + +#: ../ProjectController.py:1648 msgid "Starting PLC\n" msgstr "PLC 시작중\n" -#: ../Beremiz.py:403 +#: ../BeremizIDE.py:365 msgid "Status ToolBar" msgstr "" -#: ../editors/Viewer.py:493 -#, fuzzy +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 msgid "Step" -msgstr "스텝 수정" - -#: ../ProjectController.py:1498 +msgstr "" + +#: ../ProjectController.py:1846 msgid "Stop" msgstr "정지" -#: ../Beremiz_service.py:320 +#: ../Beremiz_service.py:264 msgid "Stop PLC" msgstr "PLC 정지" -#: ../ProjectController.py:1500 +#: ../ProjectController.py:1848 msgid "Stop Running PLC" msgstr "작동중인 PLC 정지" -#: ../ProjectController.py:1292 +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "" + +#: ../ProjectController.py:1620 msgid "Stopping debugger...\n" msgstr "디버거 정지중...\n" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Structure" msgstr "구조" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Subrange" msgstr "서브레인지" @@ -3246,58 +3107,73 @@ msgid "Subtraction" msgstr "뺄셈 연산" -#: ../ProjectController.py:915 +#: ../ProjectController.py:1085 msgid "Successfully built.\n" msgstr "성공적으로 빌드 완료 되었습니다\n" -#: ../dialogs/SearchInProjectDialog.py:154 +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 msgid "Syntax error in regular expression of pattern to search!" msgstr "정규 표현식에 적합하지 않은 검색어 입니다!" +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "" + #: ../plcopen/iec_std.csv:29 msgid "Tangent" msgstr "Tangent" -#: ../editors/ResourceEditor.py:76 +#: ../editors/ResourceEditor.py:83 msgid "Task" msgstr "태스크" -#: ../editors/ResourceEditor.py:218 +#: ../editors/ResourceEditor.py:235 msgid "Tasks:" msgstr "태스크:" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid "Temp" msgstr "임시" -#: ../editors/FileManagementPanel.py:398 +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + +#: ../editors/FileManagementPanel.py:180 #, python-format msgid "" "The file '%s' already exist.\n" "Do you want to replace it?" msgstr "" -#: ../editors/LDViewer.py:879 +#: ../editors/LDViewer.py:882 msgid "The group of block must be coherent!" msgstr "블럭 그룹은 일관성을 가져야 합니다!" -#: ../IDEFrame.py:1091 -#: ../Beremiz.py:555 -#, fuzzy +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 msgid "There are changes, do you want to save?" msgstr "" -"#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"프로젝트가 수정되었습니다. 저장 하시겠습니까?\n" -"#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -"프로젝트 파일이 변경되었습니다. 저장 하시겠습니까?" - -#: ../IDEFrame.py:1709 -#: ../IDEFrame.py:1728 -#, python-format -msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" msgstr "\"%s\" POU 에 문제가 있을 수 있습니다. 계속 하시겠습니까?" -#: ../IDEFrame.py:1178 +#: ../IDEFrame.py:1102 msgid "" "There was a problem printing.\n" "Perhaps your current printer is not set correctly?" @@ -3305,20 +3181,20 @@ "인쇄 작업에 문제가 있습니다.\n" "프린터 설정을 확인하세요" -#: ../editors/LDViewer.py:888 +#: ../editors/LDViewer.py:891 msgid "This option isn't available yet!" msgstr "선택하신 옵션은 지원되지 않습니다!" -#: ../editors/GraphicViewer.py:278 -msgid "Tick" -msgstr "틱(Tick)" +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "" #: ../plcopen/iec_std.csv:40 msgid "Time" msgstr "시간 연산" -#: ../plcopen/iec_std.csv:40 -#: ../plcopen/iec_std.csv:41 +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 msgid "Time addition" msgstr "시간 더하기 연산" @@ -3326,61 +3202,61 @@ msgid "Time concatenation" msgstr "시간 연결 연산" -#: ../plcopen/iec_std.csv:60 -#: ../plcopen/iec_std.csv:61 +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 msgid "Time division" msgstr "시간 나누기 연산" -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:47 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 msgid "Time multiplication" msgstr "시간 곱하기 연산" -#: ../plcopen/iec_std.csv:48 -#: ../plcopen/iec_std.csv:49 +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 msgid "Time subtraction" msgstr "시간 뺄셈 연산" -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:43 +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 msgid "Time-of-day addition" msgstr "하루의 시간 더하기" -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:53 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:55 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 msgid "Time-of-day subtraction" msgstr "하루의 시간 빼기" -#: ../editors/Viewer.py:432 +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:548 msgid "Top" msgstr "상단" -#: ../ProjectController.py:1507 +#: ../ProjectController.py:1855 msgid "Transfer" msgstr "전송" -#: ../ProjectController.py:1509 +#: ../ProjectController.py:1857 msgid "Transfer PLC" msgstr "PLC에 전송" -#: ../ProjectController.py:1474 +#: ../ProjectController.py:1820 msgid "Transfer completed successfully.\n" msgstr "전송이 성공적으로 완료되었습니다\n" -#: ../ProjectController.py:1476 +#: ../ProjectController.py:1823 msgid "Transfer failed\n" msgstr "전송 실패\n" -#: ../editors/Viewer.py:494 -#, fuzzy +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 msgid "Transition" -msgstr "트랜지션" - -#: ../PLCGenerator.py:1212 -#, python-format -msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" +msgstr "" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" msgstr "" #: ../dialogs/PouTransitionDialog.py:84 @@ -3391,39 +3267,50 @@ msgid "Transition Name:" msgstr "트랜지션 이름:" -#: ../PLCGenerator.py:1301 -#, python-format -msgid "Transition with content \"%s\" not connected to a next step in \"%s\" POU" -msgstr "\"%s\" 트랜지션의 내용이 POU 다음 스텝 \"%s\"와 연결되지 않았습니다" - -#: ../PLCGenerator.py:1292 -#, python-format -msgid "Transition with content \"%s\" not connected to a previous step in \"%s\" POU" -msgstr "\"%s\" 트랜지션의 내용이 POU 이전 스텝 \"%s\"와 연결되지 않았습니다" - -#: ../plcopen/plcopen.py:1442 +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 #, python-format msgid "Transition with name %s doesn't exist!" msgstr "%s 트랜지션 이름이 존재 하지 않습니다!" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Transitions" msgstr "트랜지션" -#: ../editors/ResourceEditor.py:67 +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + +#: ../editors/ResourceEditor.py:68 msgid "Triggering" msgstr "트리거링 스위치" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 -#: ../editors/ResourceEditor.py:76 -#: ../dialogs/ActionBlockDialog.py:37 +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Type" msgstr "타입" -#: ../canfestival/config_utils.py:335 -#: ../canfestival/config_utils.py:617 +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 #, python-format msgid "Type conflict for location \"%s\"" msgstr "\"%s\" 타입 에러" @@ -3432,175 +3319,179 @@ msgid "Type conversion" msgstr "변환 타입" -#: ../editors/DataTypeEditor.py:155 +#: ../editors/DataTypeEditor.py:162 msgid "Type infos:" msgstr "타입 정보:" -#: ../dialogs/SFCDivergenceDialog.py:51 -#: ../dialogs/LDPowerRailDialog.py:51 -#: ../dialogs/ConnectionDialog.py:52 -#: ../dialogs/SFCTransitionDialog.py:53 -#: ../dialogs/FBDBlockDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 msgid "Type:" msgstr "타입:" -#: ../canfestival/config_utils.py:455 -#: ../canfestival/config_utils.py:469 +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 #, python-format msgid "Unable to define PDO mapping for node %02x" msgstr "PDO 매핑 정의 에러:%02x" -#: ../targets/Xenomai/__init__.py:14 +#: ../targets/Xenomai/__init__.py:39 #, python-format msgid "Unable to get Xenomai's %s \n" msgstr "Xenomai의 %s 를 가져 올 수 없습니다\n" -#: ../PLCGenerator.py:865 -#: ../PLCGenerator.py:924 -#, fuzzy, python-format -msgid "Undefined block type \"%s\" in \"%s\" POU" -msgstr "\"%s\" 의 POU 타입이 정의되지 않았습니다" - -#: ../PLCGenerator.py:240 +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 #, python-format msgid "Undefined pou type \"%s\"" msgstr "\"%s\" 의 POU 타입이 정의되지 않았습니다" -#: ../IDEFrame.py:338 -#: ../IDEFrame.py:397 +#: ../IDEFrame.py:360 ../IDEFrame.py:421 msgid "Undo" msgstr "되돌리기" -#: ../ProjectController.py:254 +#: ../ProjectController.py:423 msgid "Unknown" msgstr "" -#: ../editors/Viewer.py:336 +#: ../editors/Viewer.py:394 #, python-format msgid "Unknown variable \"%s\" for this POU!" msgstr "\"%s\" 변수를 현재 POU에서 알 수 없습니다!" -#: ../ProjectController.py:251 -#: ../ProjectController.py:252 +#: ../ProjectController.py:420 ../ProjectController.py:421 msgid "Unnamed" msgstr "" -#: ../PLCControler.py:305 +#: ../PLCControler.py:638 #, python-format msgid "Unnamed%d" msgstr "이름 없음%d" -#: ../controls/VariablePanel.py:272 +#: ../controls/VariablePanel.py:284 #, python-format msgid "Unrecognized data size \"%s\"" msgstr "\"%s\"의 데이터 크기를 알 수 없습니다" -#: ../plcopen/structures.py:222 -msgid "" -"Up-counter\n" -"The up-counter can be used to signal when a count has reached a maximum value." -msgstr "" -"업 카운터\n" -"업 카운터는 시그널이 사용자가 설정한 최대값에 도달할 때까지 동작합니다" - -#: ../plcopen/structures.py:232 -msgid "" -"Up-down counter\n" -"The 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." -msgstr "" -"업 다운 카운터\n" -"업 다운 카운터는 CU, CD 두 개의 입력부를 가지고 있습니다. 입력부는 동시에 동작하며 하나는 카운터 상승, 다른 하나는 카운터 감소로 동작합니다" - -#: ../controls/VariablePanel.py:709 -#: ../editors/DataTypeEditor.py:623 +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 msgid "User Data Types" msgstr "사용자 데이터 타입" -#: ../canfestival/SlaveEditor.py:38 -#: ../canfestival/NetworkEditor.py:68 +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 msgid "User Type" msgstr "사용자 타입" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "User-defined POUs" msgstr "사용자 정의 POU" -#: ../controls/DebugVariablePanel.py:40 -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Value" msgstr "데이터 값" -#: ../editors/GraphicViewer.py:278 -msgid "Values" -msgstr "데이터 값" - -#: ../editors/DataTypeEditor.py:252 +#: ../editors/DataTypeEditor.py:259 msgid "Values:" msgstr "데이터 값:" -#: ../controls/DebugVariablePanel.py:40 -#: ../editors/Viewer.py:466 -#: ../dialogs/ActionBlockDialog.py:41 +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 msgid "Variable" msgstr "변수" -#: ../dialogs/FBDVariableDialog.py:47 +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 msgid "Variable Properties" msgstr "변수 속성" -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:330 -#: ../editors/Viewer.py:277 +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Variable class" msgstr "변수 클래스" -#: ../editors/TextViewer.py:374 -#: ../editors/Viewer.py:338 +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 msgid "Variable don't belong to this POU!" msgstr "POU에 속하지 않는 변수 입니다!" -#: ../controls/VariablePanel.py:77 +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "" + +#: ../controls/VariablePanel.py:72 msgid "Variables" msgstr "변수" -#: ../controls/ProjectPropertiesPanel.py:151 +#: ../controls/ProjectPropertiesPanel.py:152 msgid "Vertical:" msgstr "수직:" -#: ../wxglade_hmi/wxglade_hmi.py:11 +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:37 msgid "WXGLADE GUI" msgstr "WXGLADE GUI" -#: ../ProjectController.py:1276 -msgid "Waiting debugger to recover...\n" -msgstr "디버거 복구 대기중...\n" - -#: ../editors/LDViewer.py:888 -#: ../dialogs/PouDialog.py:126 +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 msgid "Warning" msgstr "경고" -#: ../ProjectController.py:515 +#: ../ProjectController.py:707 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "ST/IL/SFC 코드 생성기 경고:\n" -#: ../dialogs/SearchInProjectDialog.py:85 +#: ../dialogs/SearchInProjectDialog.py:78 msgid "Whole Project" msgstr "프로젝트 전체 검색" -#: ../controls/ProjectPropertiesPanel.py:119 +#: ../controls/ProjectPropertiesPanel.py:120 msgid "Width:" msgstr "폭:" -#: ../dialogs/FindInPouDialog.py:86 +#: ../dialogs/FindInPouDialog.py:91 msgid "Wrap search" msgstr "" -#: ../features.py:9 +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "" + +#: ../features.py:34 msgid "WxGlade GUI" msgstr "WxGlade GUI" -#: ../svgui/svgui.py:106 +#: ../svgui/svgui.py:142 msgid "" "You don't have write permissions.\n" "Open Inkscape anyway ?" @@ -3608,7 +3499,7 @@ "현재 쓰기 권한이 없습니다\n" "그래도 Inkscape를 열까요?" -#: ../wxglade_hmi/wxglade_hmi.py:108 +#: ../wxglade_hmi/wxglade_hmi.py:154 msgid "" "You don't have write permissions.\n" "Open wxGlade anyway ?" @@ -3616,7 +3507,7 @@ "현재 쓰기 권한이 없습니다\n" "그래도 WxGlade를 열까요?" -#: ../ProjectController.py:220 +#: ../ProjectController.py:371 msgid "" "You must have permission to work on the project\n" "Work on a project copy ?" @@ -3624,68 +3515,82 @@ "프로젝트 작업에는 권한이 필요합니다\n" "사본으로 작업하시겠습니까?" -#: ../editors/LDViewer.py:883 -msgid "You must select the block or group of blocks around which a branch should be added!" +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" msgstr "Branch를 추가할 블럭 또는 블럭 그룹을 선택해야 합니다!" -#: ../editors/LDViewer.py:663 +#: ../editors/LDViewer.py:666 msgid "You must select the wire where a contact should be added!" msgstr "접점에 추가될 와이어를 선택해야 합니다!" -#: ../dialogs/PouNameDialog.py:45 -#: ../dialogs/SFCStepNameDialog.py:47 -#: ../dialogs/SFCStepDialog.py:118 +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 msgid "You must type a name!" msgstr "이름을 입력하세요!" -#: ../dialogs/ForceVariableDialog.py:165 +#: ../dialogs/ForceVariableDialog.py:193 msgid "You must type a value!" msgstr "데이터 값을 입력하세요!" -#: ../IDEFrame.py:414 +#: ../IDEFrame.py:438 msgid "Zoom" msgstr "확대(Zoom)" -#: ../editors/GraphicViewer.py:97 -#, fuzzy -msgid "Zoom:" -msgstr "확대(Zoom)" - -#: ../PLCOpenEditor.py:356 +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "" + +#: ../PLCOpenEditor.py:343 #, python-format msgid "error: %s\n" msgstr "에러: %s\n" -#: ../util/ProcessLogger.py:161 -#, python-format -msgid "exited with status %s (pid %s)\n" -msgstr "종료함: 종료 상태 번호 %s(PID %s)\n" - -#: ../PLCOpenEditor.py:508 -#: ../PLCOpenEditor.py:510 +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 msgid "file : " msgstr "파일 :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "function" msgstr "함수" -#: ../PLCOpenEditor.py:511 +#: ../PLCOpenEditor.py:409 msgid "function : " msgstr "함수 :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "functionBlock" msgstr "함수 블럭" -#: ../PLCOpenEditor.py:511 +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "" + +#: ../PLCOpenEditor.py:409 msgid "line : " msgstr "라인 :" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "" + +#: ../dialogs/PouDialog.py:32 msgid "program" msgstr "프로그램" +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "" + #: ../plcopen/iec_std.csv:84 msgid "string from the middle" msgstr "중간 문자열" @@ -3698,11 +3603,27 @@ msgid "string right of" msgstr "우측 문자열" -#: ../PLCOpenEditor.py:354 +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "" + +#: ../PLCOpenEditor.py:341 #, python-format msgid "warning: %s\n" msgstr "경고: %s\n" +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + #: Extra XSD strings msgid "CanFestivalSlaveNode" msgstr "CANFestival 슬레이브 노드" @@ -3734,18 +3655,36 @@ msgid "CAN_Driver" msgstr "CAN 드라이버" -msgid "Debug_mode" -msgstr "디버그 모드" - -msgid "CExtension" -msgstr "C 확장" +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "Xenomai 설정" + +msgid "Compiler" +msgstr "컴파일러" msgid "CFLAGS" msgstr "CFLAGS" +msgid "Linker" +msgstr "링커" + msgid "LDFLAGS" msgstr "LDFLAGS" +msgid "Linux" +msgstr "리눅스" + +msgid "Win32" +msgstr "Win32" + msgid "BaseParams" msgstr "베이스 파라메터" @@ -3755,24 +3694,6 @@ msgid "Enabled" msgstr "가능" -msgid "Linux" -msgstr "리눅스" - -msgid "Compiler" -msgstr "컴파일러" - -msgid "Linker" -msgstr "링커" - -msgid "Win32" -msgstr "Win32" - -msgid "Xenomai" -msgstr "Xenomai" - -msgid "XenoConfig" -msgstr "Xenomai 설정" - msgid "BeremizRoot" msgstr "베레미즈 루트" @@ -3788,159 +3709,206 @@ msgid "Disable_Extensions" msgstr "확장 모듈 제거 (Extension 제거)" -#~ msgid "Close Project\tCTRL+SHIFT+W" -#~ msgstr "프로젝트 닫기\tCTRL+SHIFT+W" - -#~ msgid "New\tCTRL+N" -#~ msgstr "새로 만들기\tCTRL+N" - -#~ msgid "Open\tCTRL+O" -#~ msgstr "프로젝트 열기\tCTRL+O" - -#~ msgid "Page Setup\tCTRL+ALT+P" -#~ msgstr "인쇄 페이지 설정\tCTRL+ALT+P" - -#, fuzzy -#~ msgid "Preview\tCTRL+SHIFT+P" -#~ msgstr "" -#~ "#-#-#-#-# Beremiz_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -#~ "인쇄 미리보기\tnCTRL+SHIFT+P\n" -#~ "#-#-#-#-# PLCOpenEditor_ko_KR.po (Beremiz_Korean_Version) #-#-#-#-#\n" -#~ "인쇄 페이지 미리보기\tCTRL+SHIFT+P" - -#~ msgid "Print\tCTRL+P" -#~ msgstr "인쇄\tCTRL+P" - -#~ msgid "Quit\tCTRL+Q" -#~ msgstr "프로그램 종료\tCTRL+Q" - -#~ msgid "Save\tCTRL+S" -#~ msgstr "프로젝트 저장\tCTRL+S" - -#~ msgid "Save as\tCTRL+SHIFT+S" -#~ msgstr "다른 이름으로 저장\tCTRL+SHIFT+S" - -#~ msgid "Copy\tCTRL+C" -#~ msgstr "복사\tCTRL+C" - -#~ msgid "Cut\tCTRL+X" -#~ msgstr "잘라내기\tCTRL+X" - -#, fuzzy -#~ msgid "Find\tCTRL+F" -#~ msgstr "되돌리기\tCTRL+Z" - -#~ msgid "PLCOpenEditor\tF1" -#~ msgstr "PLCOpenEditor\tF1" - -#~ msgid "Paste\tCTRL+V" -#~ msgstr "붙여넣기\tCTRL+V" - -#~ msgid "Redo\tCTRL+Y" -#~ msgstr "되돌리기 취소\tCTRL+Y" - -#~ msgid "Refresh\tCTRL+R" -#~ msgstr "새로 고침\tCTRL+R" - -#~ msgid "Save As...\tCTRL+SHIFT+S" -#~ msgstr "다른 이름으로 저장...\tCTRL+SHIFT+S" - -#, fuzzy -#~ msgid "Search in Project\tCTRL+SHIFT+F" -#~ msgstr "프로젝트 내부 검색\tCTRL+F" - -#~ msgid "Undo\tCTRL+Z" -#~ msgstr "되돌리기\tCTRL+Z" - -#~ msgid "&ConfNode" -#~ msgstr "&플러그인" - -#~ msgid "Add ConfNode" -#~ msgstr "플러그인 추가" - -#~ msgid "Add a sub confnode" -#~ msgstr "서브 플러그인 추가" - -#~ msgid "Append " -#~ msgstr "추가" - -#~ msgid "Delete this confnode" -#~ msgstr "해당 플러그인 삭제" - -#~ msgid "Edit CanOpen Network with NetworkEdit" -#~ msgstr "네트워크 에디터를 이용하여 CanOpen 네트워크 수정" - -#~ msgid "Edit Python File" -#~ msgstr "파이썬 파일 수정" - -#~ msgid "Edit network" -#~ msgstr "네트워크 수정" - -#~ msgid "Enable/Disable this confnode" -#~ msgstr "플러그인 사용/정지" - -#~ msgid "Please enter a name for confnode:" -#~ msgstr "플러그인 이름을 입력하세요:" - -#~ msgid "Project not created" -#~ msgstr "프로젝트가 생성되지 않았습니다" - -#~ msgid "Topology" -#~ msgstr "토폴로지" - -#~ msgid "Wrong URI, please check it !\n" -#~ msgstr "URI 타입 주소를 확인하세요!\n" - -#~ msgid "Add a new data type" -#~ msgstr "새로운 데이터 타입 추가" - -#~ msgid "Add new configuration" -#~ msgstr "새로운 설정(configuration) 추가" - -#~ msgid "Add new resource" -#~ msgstr "새로운 리소스 추가" - -#~ msgid "Block Types" -#~ msgstr "블럭 타입" - -#~ msgid "CSV Log" -#~ msgstr "CSV 로그" - -#~ msgid "Delete Task" -#~ msgstr "태스크 삭제" - -#~ msgid "Display" -#~ msgstr "디스플레이" - -#~ msgid "File" -#~ msgstr "파일" - -#~ msgid "Graphic Panel" -#~ msgstr "그래픽 패널" - -#~ msgid "Help" -#~ msgstr "도움말" - -#~ msgid "Instances" -#~ msgstr "인스턴스" - -#~ msgid "Invalid value \"%s\" for location" -#~ msgstr "\"%s\"값은 위치에 적합하지 않습니다!" - -#~ msgid "Please enter configuration name" -#~ msgstr "설정(configuration) 이름을 입력하세요" - -#~ msgid "Please enter data type name" -#~ msgstr "데이터 타입 이름을 입력하세요" - -#~ msgid "Please enter resource name" -#~ msgstr "리소스 이름을 입력하세요" - -#~ msgid "Please enter text" -#~ msgstr "텍스트를 입력하세요" - -#~ msgid "Plugins" -#~ msgstr "플러그인" - -#~ msgid "Types" -#~ msgstr "타입(Types)" +msgid "%(codefile_name)s" +msgstr "" + +msgid "variables" +msgstr "" + +msgid "variable" +msgstr "" + +msgid "name" +msgstr "" + +msgid "type" +msgstr "" + +msgid "class" +msgstr "" + +msgid "initial" +msgstr "" + +msgid "desc" +msgstr "" + +msgid "onchange" +msgstr "" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "" + +msgid "second input parameter" +msgstr "" + +msgid "first output parameter" +msgstr "" + +msgid "second output parameter" +msgstr "" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_pt_BR.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_pt_BR.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3877 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-03-09 15:28+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: Thiago Alves , 2017\n" +"Language-Team: Portuguese (Brazil) (https://www.transifex.com/beremiz/teams/75746/pt_BR/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt_BR\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: ../PLCOpenEditor.py:408 ../Beremiz.py:1191 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" +"\n" +"Ocorreu uma exceção não tratada (bug). Relatório de erro salvo em:\n" +"(%s)\n" +"\n" +"Favor enviar este relatório de erro para:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"Você deve reiniciar este programa.\n" +"\n" +"Traceback:\n" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr "Externo" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr "InOut" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr "Input" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr "Local" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr "Output" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr "Temp" + +#: ../dialogs/PouTransitionDialog.py:99 ../dialogs/ProjectDialog.py:66 +#: ../dialogs/PouActionDialog.py:91 ../dialogs/PouDialog.py:113 +#, python-format +msgid " and %s" +msgstr "e %s" + +#: ../ProjectController.py:1110 +msgid " generation failed !\n" +msgstr "falha na geração !\n" + +#: ../plcopen/plcopen.py:881 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "Data Type \"%s\" não existe !!!" + +#: ../plcopen/plcopen.py:899 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "POU \"%s\" já existe !!!" + +#: ../plcopen/plcopen.py:920 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "POU \"%s\" não existe !!!" + +#: ../editors/Viewer.py:246 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" não pode ser usado para si mesmo!" + +#: ../IDEFrame.py:1651 ../IDEFrame.py:1670 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "configuração \"%s\" já existe" + +#: ../plcopen/plcopen.py:467 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "" + +#: ../IDEFrame.py:1601 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:110 ../dialogs/BlockPreviewDialog.py:219 +#: ../dialogs/PouActionDialog.py:102 ../editors/Viewer.py:262 +#: ../editors/Viewer.py:330 ../editors/Viewer.py:354 ../editors/Viewer.py:374 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "" + +#: ../Beremiz.py:994 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:106 +#: ../dialogs/BlockPreviewDialog.py:207 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:98 ../dialogs/PouDialog.py:120 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:584 +#: ../editors/CodeFileEditor.py:770 ../controls/VariablePanel.py:763 +#: ../IDEFrame.py:1592 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "" + +#: ../plcopen/plcopen.py:2412 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "" + +#: ../PLCOpenEditor.py:339 ../PLCOpenEditor.py:381 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:104 +#: ../dialogs/BlockPreviewDialog.py:203 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:96 ../dialogs/PouDialog.py:118 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:579 ../editors/CodeFileEditor.py:768 +#: ../controls/VariablePanel.py:761 ../IDEFrame.py:1590 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "" + +#: ../IDEFrame.py:2395 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:211 ../dialogs/PouDialog.py:122 +#: ../editors/Viewer.py:260 ../editors/Viewer.py:315 ../editors/Viewer.py:345 +#: ../editors/Viewer.py:367 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1610 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:745 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:752 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "" + +#: ../editors/Viewer.py:250 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "" + +#: ../plcopen/plcopen.py:491 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "" + +#: ../plcopen/plcopen.py:509 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "" + +#: ../PLCControler.py:1531 +#, python-format +msgid "%s Data Types" +msgstr "" + +#: ../PLCControler.py:1514 +#, python-format +msgid "%s POUs" +msgstr "" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "" + +#: ../plcopen/plcopen.py:1645 ../plcopen/plcopen.py:1652 +#: ../plcopen/plcopen.py:1664 ../plcopen/plcopen.py:1672 +#: ../plcopen/plcopen.py:1682 +#, python-format +msgid "%s body don't have instances!" +msgstr "" + +#: ../plcopen/plcopen.py:1700 ../plcopen/plcopen.py:1707 +#: ../plcopen/plcopen.py:1714 +#, python-format +msgid "%s body don't have text!" +msgstr "" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "" + +#: ../PLCOpenEditor.py:125 +msgid "&Properties" +msgstr "" + +#: ../Beremiz.py:324 +msgid "&Recent Projects" +msgstr "" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "" + +#: ../PLCOpenEditor.py:396 ../PLCOpenEditor.py:398 ../PLCOpenEditor.py:399 +msgid ", " +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:101 ../dialogs/PouActionDialog.py:93 +#: ../dialogs/PouDialog.py:115 +#, python-format +msgid ", %s" +msgstr "" + +#: ../PLCOpenEditor.py:394 +msgid ". " +msgstr "" + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "" + +#: ../dialogs/PouDialog.py:124 ../IDEFrame.py:1613 ../IDEFrame.py:1659 +#: ../IDEFrame.py:1678 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:108 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:100 +#: ../controls/VariablePanel.py:765 ../IDEFrame.py:1627 ../IDEFrame.py:1640 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:216 +msgid "A location must be selected!" +msgstr "" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:767 +#: ../IDEFrame.py:1629 ../IDEFrame.py:1642 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "" + +#: ../editors/CodeFileEditor.py:774 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "" + +#: ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:158 ../Beremiz.py:381 +msgid "About" +msgstr "" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/ActionBlockDialog.py:42 +msgid "Action" +msgstr "" + +#: ../editors/Viewer.py:555 ../editors/Viewer.py:2356 +msgid "Action Block" +msgstr "" + +#: ../dialogs/PouActionDialog.py:81 +msgid "Action Name" +msgstr "" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "" + +#: ../plcopen/plcopen.py:1359 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:96 +msgid "Actions" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "" + +#: ../editors/Viewer.py:1110 +msgid "Active" +msgstr "" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../editors/Viewer.py:588 ../Beremiz.py:1060 +msgid "Add" +msgstr "" + +#: ../IDEFrame.py:1889 ../IDEFrame.py:1924 +msgid "Add Action" +msgstr "" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "" + +#: ../IDEFrame.py:1872 +msgid "Add Configuration" +msgstr "" + +#: ../IDEFrame.py:1852 +msgid "Add DataType" +msgstr "" + +#: ../editors/Viewer.py:513 +msgid "Add Divergence Branch" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:116 +msgid "Add IP" +msgstr "" + +#: ../IDEFrame.py:1860 +msgid "Add POU" +msgstr "" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "" + +#: ../IDEFrame.py:1900 ../IDEFrame.py:1950 +msgid "Add Resource" +msgstr "" + +#: ../IDEFrame.py:1878 ../IDEFrame.py:1921 +msgid "Add Transition" +msgstr "" + +#: ../editors/Viewer.py:500 +msgid "Add Wire Segment" +msgstr "" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "" + +#: ../editors/Viewer.py:2717 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "" + +#: ../plcopen/definitions.py:47 +msgid "Additional function blocks" +msgstr "" + +#: ../editors/Viewer.py:571 +msgid "Adjust Block Size" +msgstr "" + +#: ../editors/Viewer.py:1648 +msgid "Alignment" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:39 +#: ../dialogs/BrowseLocationsDialog.py:47 +#: ../dialogs/BrowseLocationsDialog.py:140 +#: ../dialogs/BrowseLocationsDialog.py:143 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "" + +#: ../ProjectController.py:1644 +msgid "Already connected. Please disconnect\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:594 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:96 +msgid "Apply name modification to all continuations with the same name" +msgstr "" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:635 +#: ../controls/VariablePanel.py:841 +msgid "Array" +msgstr "" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:217 +msgid "At least a variable or an expression must be selected!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:99 +msgid "Author" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Author Name (optional):" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:79 +msgid "Backward" +msgstr "" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "" + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:55 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "" + +#: ../editors/DataTypeEditor.py:625 ../controls/VariablePanel.py:799 +msgid "Base Types" +msgstr "" + +#: ../Beremiz.py:553 +msgid "Beremiz" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "" + +#: ../editors/Viewer.py:525 ../editors/Viewer.py:2369 +msgid "Block" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:59 +msgid "Block Properties" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "" + +#: ../editors/Viewer.py:491 +msgid "Bottom" +msgstr "" + +#: ../ProjectController.py:1322 +msgid "Broken" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:37 +#, python-format +msgid "Browse %s values library" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "" + +#: ../ProjectController.py:1790 +msgid "Build" +msgstr "" + +#: ../ProjectController.py:1256 +msgid "Build directory already clean\n" +msgstr "" + +#: ../ProjectController.py:1791 +msgid "Build project into build folder" +msgstr "" + +#: ../ProjectController.py:1039 +msgid "C Build crashed !\n" +msgstr "" + +#: ../ProjectController.py:1036 +msgid "C Build failed.\n" +msgstr "" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "" + +#: ../ProjectController.py:1114 +msgid "C code generated successfully.\n" +msgstr "" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "" + +#: ../targets/toolchain_gcc.py:156 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "" + +#: ../features.py:32 +msgid "C extension" +msgstr "" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "" + +#: ../plcopen/plcopen.py:1584 ../plcopen/plcopen.py:1598 +#: ../plcopen/plcopen.py:1622 ../plcopen/plcopen.py:1638 +msgid "Can only generate execution order on FBD networks!" +msgstr "" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "" + +#: ../PLCOpenEditor.py:334 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "" + +#: ../PLCOpenEditor.py:379 +#, python-format +msgid "Can't save project to file %s!" +msgstr "" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "" + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "" + +#: ../ProjectController.py:902 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:88 +msgid "Case sensitive" +msgstr "" + +#: ../editors/Viewer.py:486 +msgid "Center" +msgstr "" + +#: ../Beremiz_service.py:266 +msgid "Change IP of interface to bind" +msgstr "" + +#: ../Beremiz_service.py:265 +msgid "Change Name" +msgstr "" + +#: ../IDEFrame.py:1942 +msgid "Change POU Type To" +msgstr "" + +#: ../Beremiz_service.py:267 +msgid "Change Port Number" +msgstr "" + +#: ../Beremiz_service.py:268 +msgid "Change working directory" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "" + +#: ../svgui/svgui.py:125 +msgid "Choose a SVG file" +msgstr "" + +#: ../ProjectController.py:501 +msgid "Choose a directory to save project" +msgstr "" + +#: ../canfestival/canfestival.py:160 ../PLCOpenEditor.py:292 +#: ../PLCOpenEditor.py:324 ../PLCOpenEditor.py:373 +msgid "Choose a file" +msgstr "" + +#: ../Beremiz.py:931 ../Beremiz.py:966 +msgid "Choose a project" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:42 +#, python-format +msgid "Choose a value for %s:" +msgstr "" + +#: ../Beremiz_service.py:323 +msgid "Choose a working directory " +msgstr "" + +#: ../ProjectController.py:408 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "" + +#: ../ProjectController.py:375 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:69 +msgid "Class:" +msgstr "" + +#: ../ProjectController.py:1794 +msgid "Clean" +msgstr "" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "" + +#: ../ProjectController.py:1796 +msgid "Clean project build folder" +msgstr "" + +#: ../ProjectController.py:1253 +msgid "Cleaning the build directory\n" +msgstr "" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "" + +#: ../editors/Viewer.py:582 +msgid "Clear Execution Order" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:105 ../dialogs/FindInPouDialog.py:111 +msgid "Close" +msgstr "" + +#: ../PLCOpenEditor.py:199 ../Beremiz.py:693 +msgid "Close Application" +msgstr "" + +#: ../PLCOpenEditor.py:108 ../Beremiz.py:333 ../Beremiz.py:637 +#: ../IDEFrame.py:1009 +msgid "Close Project" +msgstr "" + +#: ../PLCOpenEditor.py:106 ../Beremiz.py:331 +msgid "Close Tab" +msgstr "" + +#: ../editors/Viewer.py:541 ../editors/Viewer.py:2377 +msgid "Coil" +msgstr "" + +#: ../editors/Viewer.py:561 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Company Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:94 +msgid "Company Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company URL (optional):" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "" + +#: ../ProjectController.py:693 +msgid "Compiling IEC Program into C code...\n" +msgstr "" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "" + +#: ../editors/ConfTreeNodeEditor.py:229 +msgid "Config" +msgstr "" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:39 +msgid "Configuration" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Configurations" +msgstr "" + +#: ../editors/Viewer.py:307 ../editors/Viewer.py:337 ../editors/Viewer.py:359 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "" + +#: ../ProjectController.py:1809 +msgid "Connect" +msgstr "" + +#: ../ProjectController.py:1810 +msgid "Connect to the target PLC" +msgstr "" + +#: ../ProjectController.py:1313 +#, python-format +msgid "Connected to URI: %s" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:76 ../editors/Viewer.py:527 +#: ../editors/Viewer.py:2370 +msgid "Connection" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:52 +msgid "Connection Properties" +msgstr "" + +#: ../ProjectController.py:1668 +msgid "Connection canceled!\n" +msgstr "" + +#: ../ProjectController.py:1693 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:64 ../editors/Viewer.py:1605 +msgid "Connector" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:65 +msgid "Connectors:" +msgstr "" + +#: ../Beremiz.py:448 +msgid "Console" +msgstr "" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "" + +#: ../editors/Viewer.py:537 ../editors/Viewer.py:2373 +msgid "Contact" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:197 +msgid "Content Description (optional):" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1606 +msgid "Continuation" +msgstr "" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "" + +#: ../editors/Viewer.py:597 ../controls/LogViewer.py:693 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "" + +#: ../IDEFrame.py:1929 +msgid "Copy POU" +msgstr "" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" + +#: ../py_ext/PythonFileCTNMixin.py:77 +#, python-format +msgid "Couldn't import old %s file." +msgstr "" + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" + +#: ../PLCControler.py:946 +msgid "Couldn't paste non-POU object." +msgstr "" + +#: ../ProjectController.py:1610 +msgid "Couldn't start PLC !\n" +msgstr "" + +#: ../ProjectController.py:1618 +msgid "Couldn't stop PLC !\n" +msgstr "" + +#: ../ProjectController.py:1582 +msgid "Couldn't stop debugger.\n" +msgstr "" + +#: ../svgui/svgui.py:47 +msgid "Create HMI" +msgstr "" + +#: ../dialogs/PouDialog.py:45 +msgid "Create a new POU" +msgstr "" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:47 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "" + +#: ../Beremiz_service.py:432 +msgid "Current working directory :" +msgstr "" + +#: ../editors/Viewer.py:596 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:35 +msgid "Data Type" +msgstr "" + +#: ../PLCControler.py:96 +msgid "Data Types" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:43 +msgid "Days:" +msgstr "" + +#: ../ProjectController.py:1715 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "" + +#: ../editors/Viewer.py:1127 ../editors/Viewer.py:3664 +#, python-format +msgid "Debug: %s" +msgstr "" + +#: ../ProjectController.py:1371 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "" + +#: ../ProjectController.py:1369 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "" + +#: ../ProjectController.py:1551 +msgid "Debugger disabled\n" +msgstr "" + +#: ../ProjectController.py:1712 +msgid "Debugger ready\n" +msgstr "" + +#: ../ProjectController.py:1584 +msgid "Debugger stopped.\n" +msgstr "" + +#: ../editors/Viewer.py:572 ../Beremiz.py:1064 ../IDEFrame.py:1958 +msgid "Delete" +msgstr "" + +#: ../editors/Viewer.py:514 +msgid "Delete Divergence Branch" +msgstr "" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "" + +#: ../editors/Viewer.py:501 +msgid "Delete Wire Segment" +msgstr "" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:61 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:68 +msgid "Direction" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:90 +msgid "Direction:" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "" + +#: ../ProjectController.py:1818 +msgid "Disconnect" +msgstr "" + +#: ../ProjectController.py:1820 +msgid "Disconnect from PLC" +msgstr "" + +#: ../ProjectController.py:1323 +msgid "Disconnected" +msgstr "" + +#: ../editors/Viewer.py:556 ../editors/Viewer.py:2365 +msgid "Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "" + +#: ../PLCOpenEditor.py:328 +msgid "Done" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:38 +msgid "Duration" +msgstr "" + +#: ../canfestival/canfestival.py:163 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "" + +#: ../editors/Viewer.py:570 +msgid "Edit Block" +msgstr "" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:50 +msgid "Edit Step" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:36 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:45 +msgid "Edit array type properties" +msgstr "" + +#: ../editors/Viewer.py:2586 ../editors/Viewer.py:3015 +msgid "Edit comment" +msgstr "" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "" + +#: ../editors/Viewer.py:2974 +msgid "Edit jump target" +msgstr "" + +#: ../ProjectController.py:1832 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:51 +msgid "Edit transition" +msgstr "" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "" + +#: ../ProjectController.py:1216 +msgid "Editor selection" +msgstr "" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "" + +#: ../ProjectController.py:1321 +msgid "Empty" +msgstr "" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "" + +#: ../Beremiz_service.py:331 +msgid "Enter a name " +msgstr "" + +#: ../Beremiz_service.py:316 +msgid "Enter a port number " +msgstr "" + +#: ../Beremiz_service.py:307 +msgid "Enter the IP of the interface to bind" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:179 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:163 +#: ../dialogs/PouTransitionDialog.py:112 ../dialogs/BlockPreviewDialog.py:236 +#: ../dialogs/ProjectDialog.py:71 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:216 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:104 ../dialogs/PouDialog.py:134 +#: ../PLCOpenEditor.py:335 ../PLCOpenEditor.py:340 ../PLCOpenEditor.py:420 +#: ../PLCOpenEditor.py:430 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:423 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:579 ../editors/DataTypeEditor.py:584 +#: ../editors/DataTypeEditor.py:594 ../editors/DataTypeEditor.py:745 +#: ../editors/DataTypeEditor.py:752 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:783 ../ProjectController.py:343 +#: ../ProjectController.py:471 ../ProjectController.py:478 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:784 +#: ../Beremiz.py:1203 ../IDEFrame.py:1003 ../IDEFrame.py:1613 +#: ../IDEFrame.py:1654 ../IDEFrame.py:1659 ../IDEFrame.py:1673 +#: ../IDEFrame.py:1678 ../Beremiz_service.py:211 +msgid "Error" +msgstr "" + +#: ../ProjectController.py:748 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" + +#: ../ProjectController.py:740 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "" + +#: ../ProjectController.py:671 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "" + +#: ../canfestival/canfestival.py:168 +msgid "Error: Export slave failed\n" +msgstr "" + +#: ../canfestival/canfestival.py:369 +msgid "Error: No Master generated\n" +msgstr "" + +#: ../canfestival/canfestival.py:364 +msgid "Error: No PLC built\n" +msgstr "" + +#: ../ProjectController.py:1687 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:117 +msgid "Execution Control:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:79 ../dialogs/FBDBlockDialog.py:105 +msgid "Execution Order:" +msgstr "" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "" + +#: ../canfestival/canfestival.py:174 +msgid "Export CanOpen slave to EDS file" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "" + +#: ../canfestival/canfestival.py:173 +msgid "Export slave" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:89 +msgid "Expression:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "" + +#: ../ProjectController.py:761 +msgid "Extracting Located Variables...\n" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 +#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +msgid "FBD" +msgstr "" + +#: ../ProjectController.py:1750 +msgid "Failed : Must build before transfer.\n" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:462 +msgid "Falling Edge" +msgstr "" + +#: ../ProjectController.py:1029 +msgid "Fatal : cannot get builder.\n" +msgstr "" + +#: ../Beremiz.py:118 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:160 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:162 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:100 ../dialogs/FindInPouDialog.py:36 +#: ../dialogs/FindInPouDialog.py:106 ../IDEFrame.py:375 +msgid "Find" +msgstr "" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:57 +msgid "Find:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "" + +#: ../editors/Viewer.py:1564 +msgid "Force value" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:179 ../dialogs/PouTransitionDialog.py:102 +#: ../dialogs/ProjectDialog.py:70 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:144 ../dialogs/FBDBlockDialog.py:232 +#: ../dialogs/ConnectionDialog.py:160 +msgid "Form isn't complete. Name must be filled!" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:228 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:74 +msgid "Forward" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:36 ../IDEFrame.py:1745 +msgid "Function" +msgstr "" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1744 +#: ../IDEFrame.py:1937 +msgid "Function Block" +msgstr "" + +#: ../controls/VariablePanel.py:837 +msgid "Function Block Types" +msgstr "" + +#: ../PLCControler.py:95 +msgid "Function Blocks" +msgstr "" + +#: ../editors/Viewer.py:248 +msgid "Function Blocks can't be used in Functions!" +msgstr "" + +#: ../PLCControler.py:2337 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "" + +#: ../PLCControler.py:95 +msgid "Functions" +msgstr "" + +#: ../PLCOpenEditor.py:115 +msgid "Generate Program" +msgstr "" + +#: ../ProjectController.py:662 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:173 +msgid "Graphics" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:134 +msgid "Grid Resolution:" +msgstr "" + +#: ../runtime/NevowServer.py:181 +msgid "HTTP interface port :" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Height:" +msgstr "" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:150 +msgid "Horizontal:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Hours:" +msgstr "" + +#: ../dialogs/PouActionDialog.py:31 ../dialogs/PouDialog.py:36 +msgid "IL" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "" + +#: ../Beremiz_service.py:308 ../Beremiz_service.py:309 +msgid "IP is not valid!" +msgstr "" + +#: ../svgui/svgui.py:42 ../svgui/svgui.py:43 +msgid "Import SVG" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:38 ../editors/Viewer.py:1591 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "" + +#: ../editors/Viewer.py:1110 +msgid "Inactive" +msgstr "" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:38 +msgid "Indicator" +msgstr "" + +#: ../editors/Viewer.py:552 +msgid "Initial Step" +msgstr "" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "" + +#: ../svgui/svgui.py:46 +msgid "Inkscape" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:75 ../dialogs/ActionBlockDialog.py:42 +msgid "Inline" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:70 ../dialogs/FBDVariableDialog.py:37 +#: ../dialogs/BrowseLocationsDialog.py:40 ../editors/Viewer.py:289 +#: ../editors/Viewer.py:1589 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:93 +msgid "Inputs:" +msgstr "" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "" + +#: ../plcopen/plcopen.py:1691 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "" + +#: ../PLCControler.py:2325 +msgid "Invalid plcopen element(s)!!!" +msgstr "" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "" + +#: ../editors/Viewer.py:233 ../editors/Viewer.py:236 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:177 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" + +#: ../editors/Viewer.py:557 ../editors/Viewer.py:2354 +msgid "Jump" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 +#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +msgid "LD" +msgstr "" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:91 ../dialogs/PouActionDialog.py:83 +#: ../dialogs/PouDialog.py:104 +msgid "Language" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:186 +msgid "Language (optional):" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:65 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "" + +#: ../ProjectController.py:1756 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" + +#: ../Beremiz_service.py:271 +msgid "Launch WX GUI inspector" +msgstr "" + +#: ../Beremiz_service.py:270 +msgid "Launch a live Python shell" +msgstr "" + +#: ../editors/Viewer.py:485 +msgid "Left" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:62 +msgid "Left PowerRail" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "" + +#: ../targets/toolchain_gcc.py:166 +msgid "Linking :\n" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:111 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "" + +#: ../canfestival/canfestival.py:346 +msgid "Local entries" +msgstr "" + +#: ../ProjectController.py:1662 +msgid "Local service discovery failed!\n" +msgstr "" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:289 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Microseconds:" +msgstr "" + +#: ../editors/Viewer.py:490 +msgid "Middle" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Milliseconds:" +msgstr "" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Minutes:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:210 +msgid "Miscellaneous" +msgstr "" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "" + +#: ../Beremiz_service.py:332 +msgid "Name must not be null!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:56 ../dialogs/FBDBlockDialog.py:83 +#: ../dialogs/ConnectionDialog.py:75 +msgid "Name:" +msgstr "" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:460 +msgid "Negated" +msgstr "" + +#: ../Beremiz_service.py:578 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:554 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../PLCOpenEditor.py:102 ../PLCOpenEditor.py:144 ../Beremiz.py:321 +#: ../Beremiz.py:356 +msgid "New" +msgstr "" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "" + +#: ../editors/Viewer.py:459 +msgid "No Modifier" +msgstr "" + +#: ../ProjectController.py:1784 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:347 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "" + +#: ../svgui/svgui.py:131 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:86 +msgid "Only Elements" +msgstr "" + +#: ../PLCOpenEditor.py:104 ../PLCOpenEditor.py:145 ../Beremiz.py:323 +#: ../Beremiz.py:357 +msgid "Open" +msgstr "" + +#: ../svgui/svgui.py:140 +msgid "Open Inkscape" +msgstr "" + +#: ../version.py:66 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" + +#: ../ProjectController.py:1836 +msgid "Open a file explorer to manage project files" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:138 +msgid "Open wxGlade" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:83 +msgid "Options" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Organization (optional):" +msgstr "" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:39 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:289 +#: ../editors/Viewer.py:1590 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "" + +#: ../targets/toolchain_gcc.py:131 +msgid "PLC :\n" +msgstr "" + +#: ../Beremiz.py:453 +msgid "PLC Log" +msgstr "" + +#: ../ProjectController.py:1013 +msgid "PLC code generation failed !\n" +msgstr "" + +#: ../Beremiz_service.py:295 +msgid "PLC is empty or already started." +msgstr "" + +#: ../Beremiz_service.py:302 +msgid "PLC is not started." +msgstr "" + +#: ../PLCOpenEditor.py:196 ../PLCOpenEditor.py:309 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" + +#: ../PLCOpenEditor.py:292 ../PLCOpenEditor.py:373 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "" + +#: ../PLCOpenEditor.py:152 ../PLCOpenEditor.py:209 +msgid "PLCOpenEditor" +msgstr "" + +#: ../PLCOpenEditor.py:355 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "" + +#: ../dialogs/PouDialog.py:100 +msgid "POU Name" +msgstr "" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "" + +#: ../dialogs/PouDialog.py:102 +msgid "POU Type" +msgstr "" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../PLCOpenEditor.py:118 ../Beremiz.py:336 +msgid "Page Setup" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:110 +msgid "Page Size (optional):" +msgstr "" + +#: ../IDEFrame.py:2598 +#, python-format +msgid "Page: %d" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "" + +#: ../editors/Viewer.py:598 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "" + +#: ../IDEFrame.py:1864 +msgid "Paste POU" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:73 +msgid "Pin number:" +msgstr "" + +#: ../editors/Viewer.py:2717 ../editors/Viewer.py:2974 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "" + +#: ../editors/Viewer.py:2587 ../editors/Viewer.py:3016 +msgid "Please enter comment text" +msgstr "" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "" + +#: ../Beremiz_service.py:194 +msgid "Please enter text" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "" + +#: ../Beremiz_service.py:317 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "" + +#: ../Beremiz_service.py:317 +msgid "Port number must be an integer!" +msgstr "" + +#: ../editors/Viewer.py:536 ../editors/Viewer.py:2378 +msgid "Power Rail" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:50 +msgid "Power Rail Properties" +msgstr "" + +#: ../PLCOpenEditor.py:120 ../Beremiz.py:338 +msgid "Preview" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "" + +#: ../PLCOpenEditor.py:122 ../PLCOpenEditor.py:148 ../Beremiz.py:340 +#: ../Beremiz.py:360 +msgid "Print" +msgstr "" + +#: ../IDEFrame.py:1075 +msgid "Print preview" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:89 +msgid "Priority:" +msgstr "" + +#: ../runtime/PLCObject.py:370 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "" + +#: ../dialogs/ProjectDialog.py:55 +msgid "Product Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Product Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Release (optional):" +msgstr "" + +#: ../dialogs/ProjectDialog.py:56 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Version (required):" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1743 +#: ../IDEFrame.py:1940 +msgid "Program" +msgstr "" + +#: ../PLCOpenEditor.py:337 +msgid "Program was successfully generated!" +msgstr "" + +#: ../PLCControler.py:96 +msgid "Programs" +msgstr "" + +#: ../editors/Viewer.py:242 +msgid "Programs can't be used by other POUs!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:84 ../IDEFrame.py:584 +msgid "Project" +msgstr "" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "" + +#: ../ProjectController.py:1835 +msgid "Project Files" +msgstr "" + +#: ../dialogs/ProjectDialog.py:54 +msgid "Project Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:78 +msgid "Project Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Version (optional):" +msgstr "" + +#: ../PLCControler.py:3158 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" + +#: ../dialogs/ProjectDialog.py:32 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:94 +msgid "Propagate Name" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Properties" +msgstr "" + +#: ../Beremiz_service.py:440 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:427 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:426 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "" + +#: ../features.py:33 +msgid "Python file" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:38 +msgid "Qualifier" +msgstr "" + +#: ../PLCOpenEditor.py:128 ../Beremiz.py:343 ../Beremiz_service.py:273 +msgid "Quit" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "" + +#: ../ProjectController.py:1831 +msgid "Raw IEC code" +msgstr "" + +#: ../Beremiz.py:1143 +#, python-format +msgid "Really delete node '%s'?" +msgstr "" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:74 +msgid "Reference" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:106 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:98 +msgid "Regular expressions" +msgstr "" + +#: ../editors/Viewer.py:1567 +msgid "Release value" +msgstr "" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "" + +#: ../Beremiz.py:1144 +#, python-format +msgid "Remove %s node" +msgstr "" + +#: ../IDEFrame.py:2404 +msgid "Remove Datatype" +msgstr "" + +#: ../IDEFrame.py:2409 +msgid "Remove Pou" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "" + +#: ../IDEFrame.py:1944 +msgid "Rename" +msgstr "" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "" + +#: ../editors/Viewer.py:502 +msgid "Replace Wire by connections" +msgstr "" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "" + +#: ../editors/Viewer.py:583 +msgid "Reset Execution Order" +msgstr "" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "" + +#: ../PLCControler.py:97 ../Beremiz.py:1075 +msgid "Resources" +msgstr "" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "" + +#: ../editors/Viewer.py:487 +msgid "Right" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Right PowerRail" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:461 +msgid "Rising Edge" +msgstr "" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "" + +#: ../ProjectController.py:1799 +msgid "Run" +msgstr "" + +#: ../ProjectController.py:1058 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1067 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "" + +#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +msgid "SFC" +msgstr "" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 +#: ../dialogs/PouDialog.py:36 +msgid "ST" +msgstr "" + +#: ../PLCOpenEditor.py:324 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "" + +#: ../svgui/svgui.py:125 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "" + +#: ../PLCOpenEditor.py:111 ../PLCOpenEditor.py:146 ../Beremiz.py:327 +#: ../Beremiz.py:358 +msgid "Save" +msgstr "" + +#: ../PLCOpenEditor.py:113 ../PLCOpenEditor.py:147 ../Beremiz.py:359 +msgid "Save As..." +msgstr "" + +#: ../Beremiz.py:329 +msgid "Save as" +msgstr "" + +#: ../ProjectController.py:470 +msgid "Save path is the same as path of a project! \n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:44 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Seconds:" +msgstr "" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "" + +#: ../editors/Viewer.py:288 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "" + +#: ../ProjectController.py:1216 +msgid "Select an editor:" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:276 +msgid "Select an instance" +msgstr "" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "" + +#: ../ProjectController.py:477 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "" + +#: ../ProjectController.py:1825 +msgid "Show IEC code generated by PLCGenerator" +msgstr "" + +#: ../canfestival/canfestival.py:387 +msgid "Show Master" +msgstr "" + +#: ../canfestival/canfestival.py:388 +msgid "Show Master generated by config_utils" +msgstr "" + +#: ../ProjectController.py:1823 +msgid "Show code" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "" + +#: ../plcopen/definitions.py:46 +msgid "Standard function blocks" +msgstr "" + +#: ../ProjectController.py:1801 ../Beremiz_service.py:261 +msgid "Start PLC" +msgstr "" + +#: ../ProjectController.py:1005 +#, python-format +msgid "Start build in %s\n" +msgstr "" + +#: ../ProjectController.py:1319 +msgid "Started" +msgstr "" + +#: ../ProjectController.py:1607 +msgid "Starting PLC\n" +msgstr "" + +#: ../Beremiz.py:463 +msgid "Status ToolBar" +msgstr "" + +#: ../editors/Viewer.py:553 ../editors/Viewer.py:2353 +msgid "Step" +msgstr "" + +#: ../ProjectController.py:1804 +msgid "Stop" +msgstr "" + +#: ../Beremiz_service.py:262 +msgid "Stop PLC" +msgstr "" + +#: ../ProjectController.py:1806 +msgid "Stop Running PLC" +msgstr "" + +#: ../ProjectController.py:1320 +msgid "Stopped" +msgstr "" + +#: ../ProjectController.py:1579 +msgid "Stopping debugger...\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "" + +#: ../ProjectController.py:1044 +msgid "Successfully built.\n" +msgstr "" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:172 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "" + +#: ../Beremiz.py:640 ../IDEFrame.py:1011 +msgid "There are changes, do you want to save?" +msgstr "" + +#: ../IDEFrame.py:1654 ../IDEFrame.py:1673 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" + +#: ../IDEFrame.py:1098 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "" + +#: ../editors/Viewer.py:489 +msgid "Top" +msgstr "" + +#: ../ProjectController.py:1813 +msgid "Transfer" +msgstr "" + +#: ../ProjectController.py:1815 +msgid "Transfer PLC" +msgstr "" + +#: ../ProjectController.py:1779 +msgid "Transfer completed successfully.\n" +msgstr "" + +#: ../ProjectController.py:1781 +msgid "Transfer failed\n" +msgstr "" + +#: ../editors/Viewer.py:554 ../editors/Viewer.py:2355 +#: ../editors/Viewer.py:2382 +msgid "Transition" +msgstr "" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:89 +msgid "Transition Name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:58 +msgid "Transition Name:" +msgstr "" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1318 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:96 +msgid "Transitions" +msgstr "" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "" + +#: ../Beremiz_service.py:476 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:38 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:48 +msgid "Type and derivated" +msgstr "" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:57 +#: ../dialogs/LDPowerRailDialog.py:56 ../dialogs/BrowseLocationsDialog.py:99 +#: ../dialogs/FBDBlockDialog.py:65 ../dialogs/ConnectionDialog.py:58 +msgid "Type:" +msgstr "" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "" + +#: ../ProjectController.py:382 +msgid "Unknown" +msgstr "" + +#: ../editors/Viewer.py:393 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "" + +#: ../ProjectController.py:379 ../ProjectController.py:380 +msgid "Unnamed" +msgstr "" + +#: ../PLCControler.py:636 +#, python-format +msgid "Unnamed%d" +msgstr "" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "" + +#: ../editors/DataTypeEditor.py:632 ../controls/VariablePanel.py:810 +msgid "User Data Types" +msgstr "" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "" + +#: ../PLCControler.py:95 +msgid "User-defined POUs" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:38 +msgid "Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:42 ../editors/Viewer.py:526 +#: ../editors/Viewer.py:2385 +msgid "Variable" +msgstr "" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:63 +msgid "Variable Properties" +msgstr "" + +#: ../editors/Viewer.py:288 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "" + +#: ../editors/Viewer.py:395 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Vertical:" +msgstr "" + +#: ../Beremiz_service.py:586 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:562 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:35 +msgid "WXGLADE GUI" +msgstr "" + +#: ../dialogs/PouDialog.py:128 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "" + +#: ../ProjectController.py:666 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:119 +msgid "Width:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:93 +msgid "Wrap search" +msgstr "" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "" + +#: ../svgui/svgui.py:139 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:137 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" + +#: ../ProjectController.py:342 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:175 +msgid "You must type a value!" +msgstr "" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:151 +msgid "days" +msgstr "" + +#: ../PLCOpenEditor.py:333 +#, python-format +msgid "error: %s\n" +msgstr "" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:396 ../PLCOpenEditor.py:398 +msgid "file : " +msgstr "" + +#: ../dialogs/PouDialog.py:31 +msgid "function" +msgstr "" + +#: ../PLCOpenEditor.py:399 +msgid "function : " +msgstr "" + +#: ../dialogs/PouDialog.py:31 +msgid "functionBlock" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:151 +msgid "hours" +msgstr "" + +#: ../PLCOpenEditor.py:399 +msgid "line : " +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:153 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:152 +msgid "minutes" +msgstr "" + +#: ../dialogs/PouDialog.py:31 +msgid "program" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:152 +msgid "seconds" +msgstr "" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "" + +#: ../Beremiz.py:126 +msgid "update info unavailable." +msgstr "" + +#: ../PLCOpenEditor.py:331 +#, python-format +msgid "warning: %s\n" +msgstr "" + +#: ../PLCControler.py:970 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "" + +msgid "CAN_Device" +msgstr "" + +msgid "CAN_Baudrate" +msgstr "" + +msgid "NodeId" +msgstr "" + +msgid "Sync_Align" +msgstr "" + +msgid "Sync_Align_Ratio" +msgstr "" + +msgid "CanFestivalNode" +msgstr "" + +msgid "Sync_TPDOs" +msgstr "" + +msgid "CanFestivalInstance" +msgstr "" + +msgid "CAN_Driver" +msgstr "" + +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "" + +msgid "XenoConfig" +msgstr "" + +msgid "Compiler" +msgstr "" + +msgid "CFLAGS" +msgstr "" + +msgid "Linker" +msgstr "" + +msgid "LDFLAGS" +msgstr "" + +msgid "PLC" +msgstr "" + +msgid "Linux" +msgstr "" + +msgid "Win32" +msgstr "" + +msgid "BaseParams" +msgstr "" + +msgid "IEC_Channel" +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "BeremizRoot" +msgstr "" + +msgid "TargetType" +msgstr "" + +msgid "Libraries" +msgstr "" + +msgid "URI_location" +msgstr "" + +msgid "Disable_Extensions" +msgstr "" + +msgid "%(codefile_name)s" +msgstr "" + +msgid "variables" +msgstr "" + +msgid "variable" +msgstr "" + +msgid "name" +msgstr "" + +msgid "type" +msgstr "" + +msgid "class" +msgstr "" + +msgid "initial" +msgstr "" + +msgid "desc" +msgstr "" + +msgid "onchange" +msgstr "" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "" + +msgid "second input parameter" +msgstr "" + +msgid "first output parameter" +msgstr "" + +msgid "second output parameter" +msgstr "" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_pt_PT.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_pt_PT.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3916 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Pedro Coimbra , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Pedro Coimbra , 2017\n" +"Language-Team: Portuguese (https://www.transifex.com/beremiz/teams/75746/pt/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: pt\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr "Externo" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr "EntradaSaída" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr "Entrada" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr "Local" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr "Saída" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr "Temp" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr "e %s" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr "geração falhada !\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "\" %s \" O tipo de dados não existe !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "\" %s \" POU já existe !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "\" %s \" POU não existe !!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" não pode usar-se!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "\"%s \" config já existe!" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "\"%s\" configuração já existe !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "\"%s\" o tipo de dados já existe!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "\"%s\" o elemento para esta pou já existe!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "\"%s\" o ficheiro não é um projecto Beremiz válido\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "\"%s\" é uma palavra-chave. Não pode ser usada!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "\"%s\" é um valor inválido!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "\"%s\" não é um ficheiro válido!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "\"%s\" não é um identificador válido!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "\"%s\" é usáda por uma ou mais POUs. Deseja continuar?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "\"%s\" pou já existe!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "\"%s\" o passo já existe!" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "\"%s\" valor já defenido|" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "\"%s\" o valor não é uma dimensão de matriz válida!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"\"%s\" o valor não é uma dimensão de matriz válida!\n" +"O valor direito tem de ser maior do que o valor esquerdo." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "\"{a1}\" função cancelada na POU \"{a2}\" : Sem entrada conectada" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" já está a ser usada por \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "o recurso\"{a1}\" já existe na configuração\"{a2}\" !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "o recurso\"{a1}\" não existe na configuração\"{a2}\" !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dd" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "%s Tipo de dados" + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%s POUs" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "%s Perfil" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "%s o corpo não tem instâncias!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "%s o corpo não tem texto!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "&Adicione elemento" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Fechar" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "&Configuração" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "&Tipo de dados" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "&Apagar" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "&Exibir" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "&Editar" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "&Ficheiro" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "&Função" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "&Ajuda" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&Licensa" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "&Programa" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "&Propriedades" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "&Projectos recentes" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "&Recurso" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} é idêntico no projecto" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} são idênticos no projecto" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' está localizado em {a2}\n" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "(%d corresponde)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr "," + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr "." + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1d" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"Uma POU contém um elemento chamado \"%s\" Isto pode causar um conflito. " +"Deseja continuar?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "Uma POU chmada \"%s\" já existe!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "Um filho chamado \"{a1}\" ajá existe -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "Uma localização tem de ser seleccionada!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Uma tarefa com o mesmo nome já existe!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "Uma variavel com o nome\"%s\" já existe nesta pou!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "Uma variavel com o nome\"%s\" já existe!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "Sobre" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "Numero absoluto" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "Acção" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "Bloco de acção" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "Nome da acção" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "Nome da acção:" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "A acção com o nome \"%s\" não existe!" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "Acções" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "Acções:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Activo" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "Adicionar" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "Adicionar acção" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "Adicionar codigo C acessando variaveis localizadas sincronamente" + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "Adicionar Configuração" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "Adicionar um Tipo de Dados" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "Adicionar um Ramo de divergência" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "Adicionar IP" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "Adicionao POU" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "Adicionar codigo Python executado assincronamente" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "Adicionar Recurso" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "Adicionar Transição" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "Adicionar seguemento de ligação" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "Adicionar um novo passo de inicio" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "Adicionar um novo salto" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "Adicionar um novo passo" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "Adicionar um simples GUI baseado em WxGlade" + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "Adicionar acção" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "Adicionar elemento" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "Adicionar instância" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "Adicionar escravo" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "Adicionar tarefa" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "Adicionar variável" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "Adição" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "Blocos de função adicionais" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "Ajustar tamanho de bloco" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "Alinhamento" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "Tudo" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "Todos os ficheiros (*.*)|*.*|CSV files (*.csv)|*.csv" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "Já conectado. Por favor desconecte-se\n" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "Um elemento chamado \"%s\" já existe nesta estrutura!" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Uma instância com o mesmo nome já existe!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "" +"Aplicar modificação de nome para todas as continuações com o mesmo nome" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "Arco coseno" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "Arco seno" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "Arco tangente" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "Aritmético" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "Array" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "Atribuição" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "Pelo menos uma variável ou expressão tem de ser seleccionada!" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "Autor" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "Nome de Autor (opcional)" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "Para trás" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "Mau nome de domínio (circular) em" + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "Mau nome de domínio em" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "Mau tamanho de posição %s" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "Tipo base" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "Tipos base" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "Beremiz" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "Selecção binária (1 de 2)" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "Bit-shift" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "Bitwise" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "Bitwise AND" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "Bitwise OR" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "Bitwise XOR" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "Bitwise inverting" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "Bloco" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "Propriedades do bloco" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Nome do bloco " + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "Fundo" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "Avariado" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "Explore a biblioteca de valores %s" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "Navegar nos endereços" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "Compilar" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "Directório de compilação já está limpo\n" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "Compilar projecto no ficheiro de compilação" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "A compilação C quebrou !\n" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "A compilação C falhou.\n" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "Codigo C" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "Codigo C gerado com sucesso.\n" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "A compilação C falhou.\n" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "A compilação C de %s falhou.\n" + +#: ../features.py:32 +msgid "C extension" +msgstr "Extensão C" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "C&redits" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "Rede CANOpen" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "Escravo CANOpen" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "Suporte CANOpen" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "Só pode gerar ordens de execução em redes FBD!" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "Só pode gerar um endereço para variáveis locais ou globais" + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "Não é possivel gerar um programa no ficheiro %s !" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "" +"Não é possivel gerar um endereço para uma instância de bloco de função" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "Não é possivel salvar o projecto no ficheiro %s !" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "" +"Não é possivel atribuir um valor inicial a uma instância de bloco de função" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "Não é possivel criar um elemento filho {a1} do tipo {a2} " + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "Nâo é possivel encontrar um canal IEC livre inferior a %d\n" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "Não é possivel obter o estado do PLC - falha de conexão.\n" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "Não é possível abrir / analisar VARIÁVEIS.csv!\n" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Não é possível defenir o bit de offset para uma variavel não bool '{a1}' " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "Maiúsculas e minúsculas" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "Centrar" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "Alterar IP da interface a ligar" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "Alterar nome" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "Alterar tipo de Pou para" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "Alterar numero de porto" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "Alterar directoria de trabalho" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "Cadeia de caracteres" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "Escolha um ficheiro SVG" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "Escolha um directório para salvar projecto" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "Escolha um ficheiro" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "Escolha um projecto" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "Escalha um valor para %s :" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "Escolha um directório de trabalho" + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "A pasta escolhida não contém um programa. Não é um projecto válido!" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "" +"A pasta escolhida não está vazia. Não pode ser usada para um novo projecto!" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "Classe" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "Filtro de Classe:" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "Classe:" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "Limpar" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Limpar menssagens log" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "Limpar a pasta de compilação do projeto" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "Limpar o directório de compilação\n" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "limpar erros" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "limpar ordem de execução" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Fechar" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "Fechar aplicação" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "Fechar projecto" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "Fechar aba" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "Bobine" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "Comentário" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Suporte da comunidade" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Nome da Empresa" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "Nome da Empresa (obrigatório):" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "URL da Empresa (opcional):" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "Comparação" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "A compilar programa ICE para codigo C...\n" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "Concatenação" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "Config" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "Config variáveis" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "Configuração" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "Configurações" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Confirme ou altere o nome da variável" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "Conectar" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "Conectar ao PLC alvo" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "Conectar ao URI: %s" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "Conexão" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "Propriedades da conexão" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "Conexão cancelada!\n" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "Falha na conexão a %s !\n" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Conexão perdida!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "A conexão com %s falhou.\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "Conector" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "Conectores:" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "Consola" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "Constante" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "Contacto" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "descrição de conteúdo (opcional):" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "Continuação" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "Conversão de BCD" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "Conversão para BCD" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "Conversão para data" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "Conversão para hora-do-dia" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "Copiar" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "Copiar POU" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "Copiar o arquivo da pasta esquerda para a direita" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "Copiar o arquivo da pasta direita para a esquerda" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "Coseno" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" +"Não foi possível adicionar elemento filho \"{a1}\", tipo {a2}:\n" +" {a3} \n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Não foi possivél importar o ficheiro antigo %s." + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" +"Não foi possível carregar parâmetros base de confnode {a1} : \n" +" {a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" +"Não foi possível carregar parâmetros de confnode {a1} : \n" +" {a2}" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "Não foi possível colar o objeto não-POU." + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "Não foi possível iniciar o PLC!\n" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "Não foi possível parar o PLC!\n" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "Não foi possível parar o depurador.\n" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "Criar HMI" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "Criar uma nova POU" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "Criar uma nova acção" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "Criar um novo bloco de ação" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "Criar um novo bloco" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "Criar uma nova ramificação" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "Criar uma nova bobine" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "Criar um novo comentário" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "Criar uma nova conexão" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "Criar um novo contacto" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "Criar uma nova divergência" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "Criar uma nova divergência ou convergência" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "Criar um novo passo inicial" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "Criar um novo salto" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "Criar um novo trilho de energia" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "Criar uma nova rung" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "Criar um novo passo" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "Criar uma nova transição" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "Criar uma nova variável" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Creditos" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Directoria de trabalho actual" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "Cortar" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "Cíclico" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "Descontinuada" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "Perfile DS-301" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "Perfile DS-302" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "Tipo de dado" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "Tipo de dados" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "Converção de tipo de dados" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "Adição de data" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "Subtração de data e hora" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "Subtração de data" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "Dias:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" +"Depuração não corresponde ao PLC - parar / transferir / iniciar para " +"reativar\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "Depurar a instância" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "depuração %s" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "Depuração: Variável desconhecida '%s '\n" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "Depuração: Tipo não suportado para depurar ' %s '\n" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "Depurador" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "Depurador desabilitado\n" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "Depurador pronto\n" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "Depurador parado\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "Eliminar" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "Eliminar ramificação de divergência" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "Eliminar ficheiro" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "Eliminar seguemento de fio" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "Eliminar item" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "Eliminação (dentro)" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "Tipo de Derivação:" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Descrição" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "Descrição:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "Dimensões:" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "Direcção" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "Direcção:" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "Diretamente" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "" + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "" + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "" + +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "" + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "" + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "" + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "" + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "" + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "" + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "" + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "" + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "" + +#: ../features.py:33 +msgid "Python file" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "" + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "" + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "" + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "" + +msgid "CAN_Device" +msgstr "" + +msgid "CAN_Baudrate" +msgstr "" + +msgid "NodeId" +msgstr "" + +msgid "Sync_Align" +msgstr "" + +msgid "Sync_Align_Ratio" +msgstr "" + +msgid "CanFestivalNode" +msgstr "" + +msgid "Sync_TPDOs" +msgstr "" + +msgid "CanFestivalInstance" +msgstr "" + +msgid "CAN_Driver" +msgstr "" + +msgid "Generic" +msgstr "" + +msgid "Command" +msgstr "" + +msgid "Xenomai" +msgstr "" + +msgid "XenoConfig" +msgstr "" + +msgid "Compiler" +msgstr "" + +msgid "CFLAGS" +msgstr "" + +msgid "Linker" +msgstr "" + +msgid "LDFLAGS" +msgstr "" + +msgid "Linux" +msgstr "" + +msgid "Win32" +msgstr "" + +msgid "BaseParams" +msgstr "" + +msgid "IEC_Channel" +msgstr "" + +msgid "Enabled" +msgstr "" + +msgid "BeremizRoot" +msgstr "" + +msgid "TargetType" +msgstr "" + +msgid "Libraries" +msgstr "" + +msgid "URI_location" +msgstr "" + +msgid "Disable_Extensions" +msgstr "" + +msgid "%(codefile_name)s" +msgstr "" + +msgid "variables" +msgstr "" + +msgid "variable" +msgstr "" + +msgid "name" +msgstr "" + +msgid "type" +msgstr "" + +msgid "class" +msgstr "" + +msgid "initial" +msgstr "" + +msgid "desc" +msgstr "" + +msgid "onchange" +msgstr "" + +msgid "opts" +msgstr "" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "" + +msgid "Preset datetime" +msgstr "" + +msgid "Copy of IN" +msgstr "" + +msgid "Datetime, current or relative to PDT" +msgstr "" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" + +msgid "1 = integrate, 0 = hold" +msgstr "" + +msgid "Overriding reset" +msgstr "" + +msgid "Input variable" +msgstr "" + +msgid "Initial value" +msgstr "" + +msgid "Sampling period" +msgstr "" + +msgid "NOT R1" +msgstr "" + +msgid "Integrated output" +msgstr "" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "" + +msgid "0 = reset" +msgstr "" + +msgid "Input to be differentiated" +msgstr "" + +msgid "Differentiated output" +msgstr "" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" + +msgid "0 - manual , 1 - automatic" +msgstr "" + +msgid "Process variable" +msgstr "" + +msgid "Set point" +msgstr "" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "" + +msgid "Proportionality constant" +msgstr "" + +msgid "Reset time" +msgstr "" + +msgid "Derivative time constant" +msgstr "" + +msgid "PV - SP" +msgstr "" + +msgid "FB for integral term" +msgstr "" + +msgid "FB for derivative term" +msgstr "" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "" + +msgid "Ramp duration" +msgstr "" + +msgid "BUSY = 1 during ramping period" +msgstr "" + +msgid "Elapsed time of ramp" +msgstr "" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" + +msgid "" +"The 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." +msgstr "" + +msgid "first input parameter" +msgstr "" + +msgid "second input parameter" +msgstr "" + +msgid "first output parameter" +msgstr "" + +msgid "second output parameter" +msgstr "" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_ru_RU.po --- a/i18n/Beremiz_ru_RU.po Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/Beremiz_ru_RU.po Tue Jan 30 16:06:58 2018 +0100 @@ -1,23 +1,25 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Andrey Skvortsov , 2017 msgid "" msgstr "" "Project-Id-Version: Beremiz\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-01-12 14:39+0300\n" -"PO-Revision-Date: 2017-01-12 14:41+0300\n" -"Last-Translator: Andrey Skvortsov \n" -"Language-Team: Andrey Skvortsov \n" -"Language: ru_RU\n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Andrey Skvortsov , 2017\n" +"Language-Team: Russian (Russia) (https://www.transifex.com/beremiz/teams/75746/ru_RU/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Generator: Poedit 1.8.11\n" - -#: ../PLCOpenEditor.py:408 ../Beremiz.py:1191 +"Language: ru_RU\n" +"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 #, python-format msgid "" "\n" @@ -66,105 +68,107 @@ msgid " Temp" msgstr " Временный" -#: ../dialogs/PouTransitionDialog.py:99 ../dialogs/ProjectDialog.py:66 -#: ../dialogs/PouActionDialog.py:91 ../dialogs/PouDialog.py:113 +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 #, python-format msgid " and %s" msgstr "и %s" -#: ../ProjectController.py:1089 +#: ../ProjectController.py:1151 msgid " generation failed !\n" msgstr "неудачная генерация кода!\n" -#: ../plcopen/plcopen.py:881 +#: ../plcopen/plcopen.py:886 #, python-format msgid "\"%s\" Data Type doesn't exist !!!" msgstr "Тип данных \"%s\" не существует!!!" -#: ../plcopen/plcopen.py:899 +#: ../plcopen/plcopen.py:904 #, python-format msgid "\"%s\" POU already exists !!!" msgstr "POU \"%s\" уже существует!!!" -#: ../plcopen/plcopen.py:920 +#: ../plcopen/plcopen.py:925 #, python-format msgid "\"%s\" POU doesn't exist !!!" msgstr "POU \"%s\" не найден!!!" -#: ../editors/Viewer.py:246 +#: ../editors/Viewer.py:247 #, python-format msgid "\"%s\" can't use itself!" msgstr "\"%s\" не может использовать сам себя!!!" -#: ../IDEFrame.py:1652 ../IDEFrame.py:1671 +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 #, python-format msgid "\"%s\" config already exists!" msgstr "Конфигурация \"%s\" уже существует!!!" -#: ../plcopen/plcopen.py:467 +#: ../plcopen/plcopen.py:472 #, python-format msgid "\"%s\" configuration already exists !!!" msgstr "Конфигурация \"%s\" уже существует!!!" -#: ../IDEFrame.py:1602 +#: ../IDEFrame.py:1605 #, python-format msgid "\"%s\" data type already exists!" msgstr "Тип данных \"%s\" уже существует!!!" -#: ../dialogs/PouTransitionDialog.py:110 ../dialogs/BlockPreviewDialog.py:219 -#: ../dialogs/PouActionDialog.py:102 ../editors/Viewer.py:262 -#: ../editors/Viewer.py:330 ../editors/Viewer.py:354 ../editors/Viewer.py:374 +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 #: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 #: ../controls/VariablePanel.py:396 #, python-format msgid "\"%s\" element for this pou already exists!" msgstr "Элемент с именем \"%s\" уже существует в этом POU!!!" -#: ../Beremiz.py:994 +#: ../BeremizIDE.py:897 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "Директория \"%s\" не является проектом Beremiz\n" -#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:106 -#: ../dialogs/BlockPreviewDialog.py:207 ../dialogs/PouNameDialog.py:50 -#: ../dialogs/PouActionDialog.py:98 ../dialogs/PouDialog.py:120 -#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:584 -#: ../editors/CodeFileEditor.py:770 ../controls/VariablePanel.py:751 -#: ../IDEFrame.py:1593 +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 #, python-format msgid "\"%s\" is a keyword. It can't be used!" msgstr "\"%s\" является ключевым словом и не может быть использован!" -#: ../plcopen/plcopen.py:2412 +#: ../plcopen/plcopen.py:2417 #, python-format msgid "\"%s\" is an invalid value!" msgstr "\"%s\" недопустимое значение!" -#: ../PLCOpenEditor.py:339 ../PLCOpenEditor.py:381 +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 #, python-format msgid "\"%s\" is not a valid folder!" msgstr "\"%s\" не является директорией!" -#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:104 -#: ../dialogs/BlockPreviewDialog.py:203 ../dialogs/PouNameDialog.py:48 -#: ../dialogs/PouActionDialog.py:96 ../dialogs/PouDialog.py:118 -#: ../editors/DataTypeEditor.py:579 ../editors/CodeFileEditor.py:768 -#: ../controls/VariablePanel.py:749 ../IDEFrame.py:1591 +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 #, python-format msgid "\"%s\" is not a valid identifier!" msgstr "\"%s\" неверный идентификатор!" -#: ../IDEFrame.py:2396 +#: ../IDEFrame.py:2410 #, python-format msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" msgstr "\"%s\" используется более чем одним POU. Продолжить?" -#: ../dialogs/BlockPreviewDialog.py:211 ../dialogs/PouDialog.py:122 -#: ../editors/Viewer.py:260 ../editors/Viewer.py:315 ../editors/Viewer.py:345 -#: ../editors/Viewer.py:367 ../editors/TextViewer.py:270 +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 #: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 #: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 -#: ../IDEFrame.py:1611 +#: ../IDEFrame.py:1614 #, python-format msgid "\"%s\" pou already exists!" msgstr "POU \"%s\" уже существует!" @@ -179,41 +183,40 @@ msgid "\"%s\" value already defined!" msgstr "\"%s\" значение уже задано!" -#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:745 +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 #, python-format msgid "\"%s\" value isn't a valid array dimension!" msgstr "\"%s\" не является корректной размерностью для массива! " -#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:752 +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 #, python-format msgid "" "\"%s\" value isn't a valid array dimension!\n" "Right value must be greater than left value." -msgstr "\"%s\" не является корректной размерностью массива! Правое значение должно быть больше левого." +msgstr "" +"\"%s\" не является корректной размерностью массива! Правое значение должно " +"быть больше левого." #: ../PLCGenerator.py:1101 #, python-brace-format msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" msgstr "Функция \"{a1}\" не используется в POU \"{a2}\": входы не подключены" -#: ../editors/Viewer.py:250 +#: ../editors/Viewer.py:251 #, python-brace-format msgid "\"{a1}\" is already used by \"{a2}\"!" msgstr "\"{a1}\" уже используется \"{a2}\"!" -#: ../plcopen/plcopen.py:491 +#: ../plcopen/plcopen.py:496 #, python-brace-format msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" msgstr "Ресурс \"{a1}\" уже существует в конфигурации \"{a2}\"!!!" -#: ../plcopen/plcopen.py:509 +#: ../plcopen/plcopen.py:514 #, python-brace-format msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" msgstr "Ресурс \"{a1}\" отсутствует в конфигурации \"{a2}\"!!!" -msgid "%(codefile_name)s" -msgstr "%(codefile_name)" - #: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 #, python-format msgid "%03gms" @@ -247,12 +250,12 @@ msgid "%ds" msgstr "%dc" -#: ../PLCControler.py:1531 +#: ../PLCControler.py:1533 #, python-format msgid "%s Data Types" msgstr "%s типы данных" -#: ../PLCControler.py:1514 +#: ../PLCControler.py:1516 #, python-format msgid "%s POUs" msgstr "%s POU" @@ -262,15 +265,15 @@ msgid "%s Profile" msgstr "%s профиль" -#: ../plcopen/plcopen.py:1645 ../plcopen/plcopen.py:1652 -#: ../plcopen/plcopen.py:1664 ../plcopen/plcopen.py:1672 -#: ../plcopen/plcopen.py:1682 +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 #, python-format msgid "%s body don't have instances!" msgstr "Тело %s не содержит экземпляров!" -#: ../plcopen/plcopen.py:1700 ../plcopen/plcopen.py:1707 -#: ../plcopen/plcopen.py:1714 +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 #, python-format msgid "%s body don't have text!" msgstr "Тело %s не содержит никакой текст!" @@ -279,8 +282,8 @@ msgid "&Add Element" msgstr "&Добавить элемент" -#: ../dialogs/AboutDialog.py:65 ../dialogs/AboutDialog.py:113 -#: ../dialogs/AboutDialog.py:150 +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 msgid "&Close" msgstr "&Закрыть" @@ -316,7 +319,7 @@ msgid "&Help" msgstr "&Помощь" -#: ../dialogs/AboutDialog.py:64 +#: ../dialogs/AboutDialog.py:72 msgid "&License" msgstr "&Лицензия" @@ -324,11 +327,11 @@ msgid "&Program" msgstr "&Программы" -#: ../PLCOpenEditor.py:125 +#: ../PLCOpenEditor.py:127 msgid "&Properties" msgstr "&Свойства" -#: ../Beremiz.py:324 +#: ../BeremizIDE.py:219 msgid "&Recent Projects" msgstr "&Недавние проекты" @@ -356,36 +359,20 @@ msgid "(%d matches)" msgstr "(%d совпадений)" -#: ../PLCOpenEditor.py:396 ../PLCOpenEditor.py:398 ../PLCOpenEditor.py:399 +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 msgid ", " msgstr ", " -#: ../dialogs/PouTransitionDialog.py:101 ../dialogs/PouActionDialog.py:93 -#: ../dialogs/PouDialog.py:115 +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 #, python-format msgid ", %s" msgstr ", %s" -#: ../PLCOpenEditor.py:394 +#: ../PLCOpenEditor.py:404 msgid ". " msgstr ". " -#: Extra TC6 documentation strings -msgid "0 - current time, 1 - load time from PDT" -msgstr "0 - текущее время, 1 - отклонение от PDT" - -msgid "0 - manual , 1 - automatic" -msgstr "0 - ручной, 1 - автоматический" - -msgid "0 - track X0, 1 - ramp to/track X1" -msgstr "0 - вход X0, 1 - нарастание до значения X1" - -msgid "0 = reset" -msgstr "0 = сброс" - -msgid "1 = integrate, 0 = hold" -msgstr "1 = интегрировать, 0 = остановка" - #: ../controls/LogViewer.py:279 msgid "1d" msgstr "1 день" @@ -402,15 +389,19 @@ msgid "1s" msgstr "1 сек" -#: ../dialogs/PouDialog.py:124 ../IDEFrame.py:1614 ../IDEFrame.py:1660 -#: ../IDEFrame.py:1679 -#, python-format -msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" -msgstr "В POU присутствует элемент с именем \"%s\". Это может вызвать конфликт. Хотите продолжить?" - -#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:108 -#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:100 -#: ../controls/VariablePanel.py:753 ../IDEFrame.py:1628 ../IDEFrame.py:1641 +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"В POU присутствует элемент с именем \"%s\". Это может вызвать конфликт. " +"Хотите продолжить?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 #, python-format msgid "A POU named \"%s\" already exists!" msgstr "POU с именем \"%s\" уже существует!" @@ -420,22 +411,26 @@ msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" msgstr "Дочерний элемент с именем \"{a1}\" уже существует -> \"{a2}\"\n" -#: ../dialogs/BrowseLocationsDialog.py:216 +#: ../dialogs/BrowseLocationsDialog.py:218 msgid "A location must be selected!" msgstr "Необходимо выбрать размещение!" -#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:755 -#: ../IDEFrame.py:1630 ../IDEFrame.py:1643 +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Задача с таким именем уже существует!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 #, python-format msgid "A variable with \"%s\" as name already exists in this pou!" msgstr "Переменная с именем \"%s\" уже существует в этом POU!" -#: ../editors/CodeFileEditor.py:774 +#: ../editors/CodeFileEditor.py:780 #, python-format msgid "A variable with \"%s\" as name already exists!" msgstr "Переменная с именем \"%s\" уже существует!" -#: ../dialogs/AboutDialog.py:40 ../PLCOpenEditor.py:158 ../Beremiz.py:381 +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 msgid "About" msgstr "О программе" @@ -443,15 +438,15 @@ msgid "Absolute number" msgstr "Абсолютный номер" -#: ../dialogs/SFCStepDialog.py:72 ../dialogs/ActionBlockDialog.py:42 +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 msgid "Action" msgstr "Действие" -#: ../editors/Viewer.py:555 ../editors/Viewer.py:2345 +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 msgid "Action Block" msgstr "Блок действия" -#: ../dialogs/PouActionDialog.py:81 +#: ../dialogs/PouActionDialog.py:82 msgid "Action Name" msgstr "Имя действия" @@ -459,12 +454,12 @@ msgid "Action Name:" msgstr "Имя действия:" -#: ../plcopen/plcopen.py:1359 +#: ../plcopen/plcopen.py:1364 #, python-format msgid "Action with name %s doesn't exist!" msgstr "Действие с именем %s не существует!" -#: ../PLCControler.py:96 +#: ../PLCControler.py:98 msgid "Actions" msgstr "Действия" @@ -472,16 +467,16 @@ msgid "Actions:" msgstr "Действия:" -#: ../editors/Viewer.py:1100 +#: ../editors/Viewer.py:431 msgid "Active" msgstr "Активный" #: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 -#: ../editors/Viewer.py:588 ../Beremiz.py:1060 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 msgid "Add" msgstr "Добавить" -#: ../IDEFrame.py:1890 ../IDEFrame.py:1925 +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 msgid "Add Action" msgstr "Добавить действие" @@ -489,23 +484,23 @@ msgid "Add C code accessing located variables synchronously" msgstr "Добавить C-код с синхронным доступом к локальным переменным" -#: ../IDEFrame.py:1873 +#: ../IDEFrame.py:1876 msgid "Add Configuration" msgstr "Добавить конфигурацию" -#: ../IDEFrame.py:1853 +#: ../IDEFrame.py:1856 msgid "Add DataType" msgstr "Добавить тип данных" -#: ../editors/Viewer.py:513 +#: ../editors/Viewer.py:572 msgid "Add Divergence Branch" msgstr "Добавить ветвление" -#: ../dialogs/DiscoveryDialog.py:116 +#: ../dialogs/DiscoveryDialog.py:117 msgid "Add IP" msgstr "Добавить IP адрес" -#: ../IDEFrame.py:1861 +#: ../IDEFrame.py:1864 msgid "Add POU" msgstr "Добавить POU" @@ -513,15 +508,15 @@ msgid "Add Python code executed asynchronously" msgstr "Добавить асинхронно вызываемый код на Python" -#: ../IDEFrame.py:1901 ../IDEFrame.py:1951 +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 msgid "Add Resource" msgstr "Добавить ресурс" -#: ../IDEFrame.py:1879 ../IDEFrame.py:1922 +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 msgid "Add Transition" msgstr "Добавить переход" -#: ../editors/Viewer.py:500 +#: ../editors/Viewer.py:559 msgid "Add Wire Segment" msgstr "Добавить провод" @@ -529,7 +524,7 @@ msgid "Add a new initial step" msgstr "Добавить новый исходный шаг" -#: ../editors/Viewer.py:2706 ../editors/SFCViewer.py:770 +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 msgid "Add a new jump" msgstr "Добавить новый безусловный переход" @@ -569,22 +564,22 @@ msgid "Addition" msgstr "Сложение" -#: ../plcopen/definitions.py:47 +#: ../plcopen/definitions.py:49 msgid "Additional function blocks" msgstr "Дополнительные функциональные блоки" -#: ../editors/Viewer.py:571 +#: ../editors/Viewer.py:630 msgid "Adjust Block Size" msgstr "Скорректировать размер элемента" -#: ../editors/Viewer.py:1637 +#: ../editors/Viewer.py:1686 msgid "Alignment" msgstr "Выравнивание" -#: ../dialogs/BrowseLocationsDialog.py:39 -#: ../dialogs/BrowseLocationsDialog.py:47 -#: ../dialogs/BrowseLocationsDialog.py:140 -#: ../dialogs/BrowseLocationsDialog.py:143 ../controls/LogViewer.py:298 +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 #: ../controls/VariablePanel.py:70 msgid "All" msgstr "Все" @@ -593,16 +588,20 @@ msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" msgstr "Все файлы (*.*)|*.*|CSV files (*.csv)|*.csv" -#: ../ProjectController.py:1623 +#: ../ProjectController.py:1685 msgid "Already connected. Please disconnect\n" msgstr "Уже подключен. Пожалуйста, отключитесь сначала.\n" -#: ../editors/DataTypeEditor.py:594 +#: ../editors/DataTypeEditor.py:591 #, python-format msgid "An element named \"%s\" already exists in this structure!" msgstr "Поле с именем \"%s\" уже существует в данной структуре!" -#: ../dialogs/ConnectionDialog.py:96 +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Экземпляр задачи с таким именем уже существует!" + +#: ../dialogs/ConnectionDialog.py:100 msgid "Apply name modification to all continuations with the same name" msgstr "Переименовать все цепи с тем же самым именем" @@ -622,8 +621,8 @@ msgid "Arithmetic" msgstr "Математика" -#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:635 -#: ../controls/VariablePanel.py:829 +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 msgid "Array" msgstr "Массив" @@ -631,22 +630,19 @@ msgid "Assignment" msgstr "Присвоение" -#: ../dialogs/FBDVariableDialog.py:217 +#: ../dialogs/FBDVariableDialog.py:222 msgid "At least a variable or an expression must be selected!" msgstr "Переменная или выражение должно быть выбрано!" -#: ../controls/ProjectPropertiesPanel.py:99 +#: ../controls/ProjectPropertiesPanel.py:100 msgid "Author" msgstr "Автор" -#: ../controls/ProjectPropertiesPanel.py:96 +#: ../controls/ProjectPropertiesPanel.py:97 msgid "Author Name (optional):" msgstr "Имя автора (опционально):" -msgid "BUSY = 1 during ramping period" -msgstr "BOSY = 1 во время " - -#: ../dialogs/FindInPouDialog.py:79 +#: ../dialogs/FindInPouDialog.py:77 msgid "Backward" msgstr "Назад" @@ -663,25 +659,19 @@ msgid "Bad location size : %s" msgstr "Неправильный размер: %s" -#: ../dialogs/ArrayTypeDialog.py:55 ../editors/DataTypeEditor.py:175 +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 #: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 msgid "Base Type:" msgstr "Базовый тип:" -#: ../editors/DataTypeEditor.py:625 ../controls/VariablePanel.py:787 +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 msgid "Base Types" msgstr "Базовые типы" -msgid "BaseParams" -msgstr "Базовые параметры" - -#: ../Beremiz.py:553 +#: ../BeremizIDE.py:455 msgid "Beremiz" msgstr "Beremiz" -msgid "BeremizRoot" -msgstr "Настройки Beremiz " - #: ../plcopen/iec_std.csv:70 msgid "Binary selection (1 of 2)" msgstr "Бинарный выбор (1 или 2)" @@ -710,11 +700,11 @@ msgid "Bitwise inverting" msgstr "Битовое НЕ" -#: ../editors/Viewer.py:525 ../editors/Viewer.py:2358 +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 msgid "Block" msgstr "Блок" -#: ../dialogs/FBDBlockDialog.py:59 +#: ../dialogs/FBDBlockDialog.py:60 msgid "Block Properties" msgstr "Свойства блока" @@ -722,40 +712,40 @@ msgid "Block name" msgstr "Имя блока" -#: ../editors/Viewer.py:491 +#: ../editors/Viewer.py:550 msgid "Bottom" msgstr "Низ" -#: ../ProjectController.py:1301 +#: ../ProjectController.py:1363 msgid "Broken" msgstr "Ошибка" -#: ../dialogs/BrowseValuesLibraryDialog.py:37 +#: ../dialogs/BrowseValuesLibraryDialog.py:38 #, python-format msgid "Browse %s values library" -msgstr "Browse %s values library" +msgstr "Просмотр %s значений библиотеки" #: ../dialogs/BrowseLocationsDialog.py:65 msgid "Browse Locations" -msgstr "Просмотр директорий" - -#: ../ProjectController.py:1769 +msgstr "Просмотр доступных МЭК-адресов" + +#: ../ProjectController.py:1832 msgid "Build" msgstr "Сборка" -#: ../ProjectController.py:1235 +#: ../ProjectController.py:1297 msgid "Build directory already clean\n" msgstr "Директория сборки уже пуста\n" -#: ../ProjectController.py:1770 +#: ../ProjectController.py:1833 msgid "Build project into build folder" msgstr "Сборка проекта в директории сборки" -#: ../ProjectController.py:1018 +#: ../ProjectController.py:1080 msgid "C Build crashed !\n" msgstr "Крэш во время сборки C-кода!\n" -#: ../ProjectController.py:1015 +#: ../ProjectController.py:1077 msgid "C Build failed.\n" msgstr "Ошибка сборки C-кода.\n" @@ -763,7 +753,7 @@ msgid "C code" msgstr "C код " -#: ../ProjectController.py:1093 +#: ../ProjectController.py:1155 msgid "C code generated successfully.\n" msgstr "C-код успешно сгенерирован.\n" @@ -771,7 +761,7 @@ msgid "C compilation failed.\n" msgstr "Ошибка компиляции.\n" -#: ../targets/toolchain_gcc.py:156 +#: ../targets/toolchain_gcc.py:192 #, python-format msgid "C compilation of %s failed.\n" msgstr "Ошибка компиляции %s.\n" @@ -780,7 +770,7 @@ msgid "C extension" msgstr "С-расширение" -#: ../dialogs/AboutDialog.py:63 +#: ../dialogs/AboutDialog.py:71 msgid "C&redits" msgstr "&Благодарности" @@ -792,32 +782,20 @@ msgid "CANOpen slave" msgstr "CANOpen ведущий" -msgid "CAN_Baudrate" -msgstr "Скорость CAN" - -msgid "CAN_Device" -msgstr "CAN устройство" - -msgid "CAN_Driver" -msgstr "CAN драйвер" - #: ../features.py:31 msgid "CANopen support" msgstr "Поддержка CANOpen" -msgid "CFLAGS" -msgstr "CFLAGS" - -#: ../plcopen/plcopen.py:1584 ../plcopen/plcopen.py:1598 -#: ../plcopen/plcopen.py:1622 ../plcopen/plcopen.py:1638 +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 msgid "Can only generate execution order on FBD networks!" msgstr "Можно сгенерировать порядок исполнения только для FBD!" #: ../controls/VariablePanel.py:267 msgid "Can only give a location to local or global variables" -msgstr "Можно задать размещение только локальным или глобальным переменны" - -#: ../PLCOpenEditor.py:334 +msgstr "Можно задать адреса только локальным или глобальным переменным" + +#: ../PLCOpenEditor.py:344 #, python-format msgid "Can't generate program to file %s!" msgstr "Нельзя сгенерировать программу в файл %s!" @@ -826,7 +804,7 @@ msgid "Can't give a location to a function block instance" msgstr "Нельзя задать размещение для экземпляра функционального блока" -#: ../PLCOpenEditor.py:379 +#: ../PLCOpenEditor.py:389 #, python-format msgid "Can't save project to file %s!" msgstr "Нельзя сохранить проект в файл %s!" @@ -835,16 +813,6 @@ msgid "Can't set an initial value to a function block instance" msgstr "Нельзя задать исходное значение экземпляру функционального блока" -msgid "CanFestivalInstance" -msgstr "" - -msgid "CanFestivalNode" -msgstr "" - -#: Extra XSD strings -msgid "CanFestivalSlaveNode" -msgstr "" - #: ../ConfigTreeNode.py:529 #, python-brace-format msgid "Cannot create child {a1} of type {a2} " @@ -853,46 +821,50 @@ #: ../ConfigTreeNode.py:454 #, python-format msgid "Cannot find lower free IEC channel than %d\n" -msgstr "Не удалось найти свободный МЭК-канал с номером меньше чем %d\n" +msgstr "Не удалось найти свободный МЭК-канал с номером меньше, чем %d\n" #: ../connectors/PYRO/__init__.py:131 msgid "Cannot get PLC status - connection failed.\n" msgstr "Невозможно получить состояние ПЛК - ошибка подключения.\n" -#: ../ProjectController.py:881 +#: ../ProjectController.py:943 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "Не удалось открыть/прочитать VARIABLES.csv\n" #: ../canfestival/config_utils.py:374 #, python-brace-format -msgid "Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))" -msgstr "Невозможно установить битовое смещение для небулевой переменной '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" - -#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:88 +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Невозможно установить битовое смещение для не булевой переменной '{a1}' " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 msgid "Case sensitive" msgstr "Регистрозависимый" -#: ../editors/Viewer.py:486 +#: ../editors/Viewer.py:545 msgid "Center" msgstr "Центр" -#: ../Beremiz_service.py:266 +#: ../Beremiz_service.py:268 msgid "Change IP of interface to bind" msgstr "Сменить IP-адрес интерфейса для привязки сокета" -#: ../Beremiz_service.py:265 +#: ../Beremiz_service.py:267 msgid "Change Name" msgstr "Сменить имя" -#: ../IDEFrame.py:1943 +#: ../IDEFrame.py:1946 msgid "Change POU Type To" msgstr "Сменить тип POU на" -#: ../Beremiz_service.py:267 +#: ../Beremiz_service.py:269 msgid "Change Port Number" msgstr "Сменить номер порта" -#: ../Beremiz_service.py:268 +#: ../Beremiz_service.py:270 msgid "Change working directory" msgstr "Сменить рабочую директорию" @@ -900,39 +872,40 @@ msgid "Character string" msgstr "Строковые операции" -#: ../svgui/svgui.py:125 +#: ../svgui/svgui.py:128 msgid "Choose a SVG file" msgstr "Выберите SVG-файл" -#: ../ProjectController.py:451 +#: ../ProjectController.py:542 msgid "Choose a directory to save project" msgstr "Выберите директорию, чтобы сохранить проект" -#: ../canfestival/canfestival.py:160 ../PLCOpenEditor.py:292 -#: ../PLCOpenEditor.py:324 ../PLCOpenEditor.py:373 +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 msgid "Choose a file" msgstr "Выберите файл" -#: ../Beremiz.py:931 ../Beremiz.py:966 +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 msgid "Choose a project" msgstr "Выберите проект" -#: ../dialogs/BrowseValuesLibraryDialog.py:42 +#: ../dialogs/BrowseValuesLibraryDialog.py:41 #, python-format msgid "Choose a value for %s:" msgstr "Выберите значение для %s:" -#: ../Beremiz_service.py:323 +#: ../Beremiz_service.py:325 msgid "Choose a working directory " msgstr "Выберите рабочую директорию" -#: ../ProjectController.py:358 +#: ../ProjectController.py:449 msgid "Chosen folder doesn't contain a program. It's not a valid project!" msgstr "Выбранная директория не содержит программы. Это некорректный проект!" -#: ../ProjectController.py:325 +#: ../ProjectController.py:416 msgid "Chosen folder isn't empty. You can't use it for a new project!" -msgstr "Выбранная директория не пуста и не может использоваться для нового проекта!" +msgstr "" +"Выбранная директория не пуста и не может использоваться для нового проекта!" #: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Class" @@ -942,11 +915,11 @@ msgid "Class Filter:" msgstr "Фильтр класса:" -#: ../dialogs/FBDVariableDialog.py:69 +#: ../dialogs/FBDVariableDialog.py:70 msgid "Class:" msgstr "Класс:" -#: ../ProjectController.py:1773 +#: ../ProjectController.py:1836 msgid "Clean" msgstr "Очистить" @@ -954,11 +927,11 @@ msgid "Clean log messages" msgstr "Очистить лог" -#: ../ProjectController.py:1775 +#: ../ProjectController.py:1838 msgid "Clean project build folder" msgstr "Очистить директорию сборки проекта" -#: ../ProjectController.py:1232 +#: ../ProjectController.py:1294 msgid "Cleaning the build directory\n" msgstr "Очистка директории сборки\n" @@ -966,47 +939,49 @@ msgid "Clear Errors" msgstr "Очистить ошибки" -#: ../editors/Viewer.py:582 +#: ../editors/Viewer.py:641 msgid "Clear Execution Order" msgstr "Очистить порядок исполнения" -#: ../dialogs/SearchInProjectDialog.py:105 ../dialogs/FindInPouDialog.py:111 +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 msgid "Close" msgstr "Закрыть" -#: ../PLCOpenEditor.py:199 ../Beremiz.py:693 +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 msgid "Close Application" msgstr "Закрыть приложение" -#: ../PLCOpenEditor.py:108 ../Beremiz.py:333 ../Beremiz.py:637 -#: ../IDEFrame.py:1009 +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 msgid "Close Project" msgstr "Закрыть проект" -#: ../PLCOpenEditor.py:106 ../Beremiz.py:331 +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 msgid "Close Tab" msgstr "Закрыть вкладку" -#: ../editors/Viewer.py:541 ../editors/Viewer.py:2366 +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 msgid "Coil" msgstr "Катушка" -msgid "Command" -msgstr "Комманда" - -#: ../editors/Viewer.py:561 ../editors/LDViewer.py:506 +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 msgid "Comment" msgstr "Комментарий" -#: ../dialogs/ProjectDialog.py:57 +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Поддержка сообщества" + +#: ../dialogs/ProjectDialog.py:60 msgid "Company Name" msgstr "Имя компании" -#: ../controls/ProjectPropertiesPanel.py:94 +#: ../controls/ProjectPropertiesPanel.py:95 msgid "Company Name (required):" msgstr "Компания (обязательно):" -#: ../controls/ProjectPropertiesPanel.py:95 +#: ../controls/ProjectPropertiesPanel.py:96 msgid "Company URL (optional):" msgstr "Сайт компании (опционально):" @@ -1014,10 +989,7 @@ msgid "Comparison" msgstr "Сравнение" -msgid "Compiler" -msgstr "Компилятор" - -#: ../ProjectController.py:672 +#: ../ProjectController.py:734 msgid "Compiling IEC Program into C code...\n" msgstr "Компиляция МЭК-программы в C-код...\n" @@ -1025,7 +997,7 @@ msgid "Concatenation" msgstr "Объединение" -#: ../editors/ConfTreeNodeEditor.py:229 +#: ../editors/ConfTreeNodeEditor.py:230 msgid "Config" msgstr "Конфигурация" @@ -1033,47 +1005,47 @@ msgid "Config variables" msgstr "Конфигурационные переменные" -#: ../dialogs/SearchInProjectDialog.py:39 +#: ../dialogs/SearchInProjectDialog.py:40 msgid "Configuration" msgstr "Конфигурация" -#: ../PLCControler.py:97 +#: ../PLCControler.py:99 msgid "Configurations" msgstr "Конфигурации" -#: ../editors/Viewer.py:307 ../editors/Viewer.py:337 ../editors/Viewer.py:359 +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 #: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 #: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 msgid "Confirm or change variable name" msgstr "Подтвердить или поменять имя переменной" -#: ../ProjectController.py:1788 +#: ../ProjectController.py:1851 msgid "Connect" msgstr "Подключиться" -#: ../ProjectController.py:1789 +#: ../ProjectController.py:1852 msgid "Connect to the target PLC" msgstr "Подключиться к целевому ПЛК" -#: ../ProjectController.py:1292 +#: ../ProjectController.py:1354 #, python-format msgid "Connected to URI: %s" msgstr "Подключен к URI: %s" -#: ../dialogs/SFCTransitionDialog.py:76 ../editors/Viewer.py:527 -#: ../editors/Viewer.py:2359 +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 msgid "Connection" msgstr "Подключение" -#: ../dialogs/ConnectionDialog.py:52 +#: ../dialogs/ConnectionDialog.py:53 msgid "Connection Properties" -msgstr "Свойства подключение" - -#: ../ProjectController.py:1647 +msgstr "Свойства подключения" + +#: ../ProjectController.py:1709 msgid "Connection canceled!\n" msgstr "Подключение отменено!\n" -#: ../ProjectController.py:1672 +#: ../ProjectController.py:1734 #, python-format msgid "Connection failed to %s!\n" msgstr "Неудачное подключение к %s!\n" @@ -1087,15 +1059,15 @@ msgid "Connection to '%s' failed.\n" msgstr "Неудачное подключение к %s!\n" -#: ../dialogs/ConnectionDialog.py:64 ../editors/Viewer.py:1594 +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 msgid "Connector" msgstr "Коннектор" -#: ../dialogs/SFCStepDialog.py:65 +#: ../dialogs/SFCStepDialog.py:66 msgid "Connectors:" msgstr "Коннекторы:" -#: ../Beremiz.py:448 +#: ../BeremizIDE.py:350 msgid "Console" msgstr "Консоль" @@ -1103,15 +1075,15 @@ msgid "Constant" msgstr "Константа" -#: ../editors/Viewer.py:537 ../editors/Viewer.py:2362 +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 msgid "Contact" msgstr "Контакт" -#: ../controls/ProjectPropertiesPanel.py:197 +#: ../controls/ProjectPropertiesPanel.py:198 msgid "Content Description (optional):" msgstr "Описание содержимого (опционально):" -#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1595 +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 msgid "Continuation" msgstr "Продолжение" @@ -1131,12 +1103,12 @@ msgid "Conversion to time-of-day" msgstr "Преобразование во время суток" -#: ../editors/Viewer.py:597 ../controls/LogViewer.py:693 ../IDEFrame.py:370 +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 #: ../IDEFrame.py:425 msgid "Copy" msgstr "Копировать" -#: ../IDEFrame.py:1930 +#: ../IDEFrame.py:1933 msgid "Copy POU" msgstr "Копировать POU" @@ -1148,9 +1120,6 @@ msgid "Copy file from right folder to left" msgstr "Скопировать файл с правой директории в левую" -msgid "Copy of IN" -msgstr "Копия входа IN" - #: ../plcopen/iec_std.csv:28 msgid "Cosine" msgstr "Косинус" @@ -1164,7 +1133,7 @@ "Невозможно добавить дочерний элемент \"{a1}\", тип {a2}:\n" "{a3}\n" -#: ../py_ext/PythonFileCTNMixin.py:77 +#: ../py_ext/PythonFileCTNMixin.py:78 #, python-format msgid "Couldn't import old %s file." msgstr "Невозможно импортировать старый файл %s." @@ -1187,27 +1156,27 @@ "Невозможно загрузить параметры confnode {a1}:\n" " {a2}" -#: ../PLCControler.py:946 +#: ../PLCControler.py:948 msgid "Couldn't paste non-POU object." msgstr "Невозможно вставить не-POU." -#: ../ProjectController.py:1589 +#: ../ProjectController.py:1651 msgid "Couldn't start PLC !\n" msgstr "Невозможно запустить ПЛК!\n" -#: ../ProjectController.py:1597 +#: ../ProjectController.py:1659 msgid "Couldn't stop PLC !\n" msgstr "Невозможно остановить ПЛК!\n" -#: ../ProjectController.py:1561 +#: ../ProjectController.py:1623 msgid "Couldn't stop debugger.\n" msgstr "Невозможно остановить отладчик.\n" -#: ../svgui/svgui.py:47 +#: ../svgui/svgui.py:49 msgid "Create HMI" msgstr "Создать HMI" -#: ../dialogs/PouDialog.py:45 +#: ../dialogs/PouDialog.py:46 msgid "Create a new POU" msgstr "Создать новый POU" @@ -1271,7 +1240,7 @@ msgid "Create a new step" msgstr "Создать новый шаг" -#: ../dialogs/PouTransitionDialog.py:47 ../IDEFrame.py:156 +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 msgid "Create a new transition" msgstr "Создать новый переход" @@ -1279,15 +1248,15 @@ msgid "Create a new variable" msgstr "Создать новую переменную" -#: ../dialogs/AboutDialog.py:105 +#: ../dialogs/AboutDialog.py:113 msgid "Credits" msgstr "Благодарности" -#: ../Beremiz_service.py:432 +#: ../Beremiz_service.py:434 msgid "Current working directory :" msgstr "Текущая рабочая директория :" -#: ../editors/Viewer.py:596 ../IDEFrame.py:368 ../IDEFrame.py:424 +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 msgid "Cut" msgstr "Вырезать" @@ -1311,11 +1280,11 @@ msgid "DS-302 Profile" msgstr "Профиль DS-302" -#: ../dialogs/SearchInProjectDialog.py:35 +#: ../dialogs/SearchInProjectDialog.py:36 msgid "Data Type" msgstr "Тип данных" -#: ../PLCControler.py:96 +#: ../PLCControler.py:98 msgid "Data Types" msgstr "Типы данных" @@ -1336,32 +1305,31 @@ msgid "Date subtraction" msgstr "Вычитание дат" -msgid "Datetime, current or relative to PDT" -msgstr "Текущие дата и время, абсолютные или относительные от PDT" - -#: ../dialogs/DurationEditorDialog.py:43 +#: ../dialogs/DurationEditorDialog.py:44 msgid "Days:" msgstr "Дни:" -#: ../ProjectController.py:1694 +#: ../ProjectController.py:1756 msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" -msgstr "Отлаживаемая программа не соответствует программе в ПЛК - остановите/загрузите/запустите, чтобы разрешить отладку\n" +msgstr "" +"Отлаживаемая программа не соответствует программе в ПЛК - " +"остановите/загрузите/запустите, чтобы разрешить отладку\n" #: ../controls/PouInstanceVariablesPanel.py:134 msgid "Debug instance" msgstr "Отладка экземпляра" -#: ../editors/Viewer.py:1117 ../editors/Viewer.py:3653 +#: ../editors/Viewer.py:448 #, python-format msgid "Debug: %s" msgstr "Отладка: %s" -#: ../ProjectController.py:1350 +#: ../ProjectController.py:1412 #, python-format msgid "Debug: Unknown variable '%s'\n" msgstr "Отладка: неизвестная переменная '%s'\n" -#: ../ProjectController.py:1348 +#: ../ProjectController.py:1410 #, python-format msgid "Debug: Unsupported type to debug '%s'\n" msgstr "Отладка: неподдерживамый отладкой тип '%s'\n" @@ -1370,23 +1338,23 @@ msgid "Debugger" msgstr "Отладчик" -#: ../ProjectController.py:1530 +#: ../ProjectController.py:1592 msgid "Debugger disabled\n" msgstr "Отладчик запрещен\n" -#: ../ProjectController.py:1691 +#: ../ProjectController.py:1753 msgid "Debugger ready\n" msgstr "Отладчик готов\n" -#: ../ProjectController.py:1563 +#: ../ProjectController.py:1625 msgid "Debugger stopped.\n" msgstr "Отладчик остановлен.\n" -#: ../editors/Viewer.py:572 ../Beremiz.py:1064 ../IDEFrame.py:1959 +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 msgid "Delete" msgstr "Удалить" -#: ../editors/Viewer.py:514 +#: ../editors/Viewer.py:573 msgid "Delete Divergence Branch" msgstr "Удалить ветвь" @@ -1394,7 +1362,7 @@ msgid "Delete File" msgstr "Удалить файл" -#: ../editors/Viewer.py:501 +#: ../editors/Viewer.py:560 msgid "Delete Wire Segment" msgstr "Удалить сегмент цепи" @@ -1410,25 +1378,23 @@ msgid "Derivation Type:" msgstr "Механизм создания типа:" -msgid "Derivative time constant" -msgstr "Постоянная времени дифференцирования" +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Описание" #: ../controls/VariablePanel.py:432 msgid "Description:" msgstr "Описание:" -msgid "Differentiated output" -msgstr "Дифференцированный выход" - -#: ../dialogs/ArrayTypeDialog.py:61 ../editors/DataTypeEditor.py:321 +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 msgid "Dimensions:" msgstr "Размеры:" -#: ../dialogs/FindInPouDialog.py:68 +#: ../dialogs/FindInPouDialog.py:66 msgid "Direction" msgstr "Направление" -#: ../dialogs/BrowseLocationsDialog.py:90 +#: ../dialogs/BrowseLocationsDialog.py:91 msgid "Direction:" msgstr "Направление:" @@ -1436,22 +1402,19 @@ msgid "Directly" msgstr "Синоним" -msgid "Disable_Extensions" -msgstr "Запретить расширения" - -#: ../ProjectController.py:1797 +#: ../ProjectController.py:1860 msgid "Disconnect" msgstr "Отключиться" -#: ../ProjectController.py:1799 +#: ../ProjectController.py:1862 msgid "Disconnect from PLC" msgstr "Отключиться от ПЛК" -#: ../ProjectController.py:1302 +#: ../ProjectController.py:1364 msgid "Disconnected" msgstr "Отключено" -#: ../editors/Viewer.py:556 ../editors/Viewer.py:2354 +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 msgid "Divergence" msgstr "Ветвление" @@ -1468,19 +1431,19 @@ msgid "Documentation" msgstr "Описание" -#: ../PLCOpenEditor.py:328 +#: ../PLCOpenEditor.py:338 msgid "Done" msgstr "Завершено" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Duration" msgstr "Длительность" -#: ../canfestival/canfestival.py:163 +#: ../canfestival/canfestival.py:165 msgid "EDS files (*.eds)|*.eds|All files|*.*" msgstr "Файлы EDS (*.eds)|*.eds|All files|*.*" -#: ../editors/Viewer.py:570 +#: ../editors/Viewer.py:629 msgid "Edit Block" msgstr "Редактировать блок" @@ -1496,11 +1459,11 @@ msgid "Edit Duration" msgstr "Редактировать длительность" -#: ../dialogs/SFCStepDialog.py:50 +#: ../dialogs/SFCStepDialog.py:51 msgid "Edit Step" msgstr "Редактировать шаг" -#: ../wxglade_hmi/wxglade_hmi.py:36 +#: ../wxglade_hmi/wxglade_hmi.py:38 msgid "Edit a WxWidgets GUI with WXGlade" msgstr "Редактировать WxWidgets GUI с помощью WXGlade" @@ -1508,11 +1471,11 @@ msgid "Edit action block properties" msgstr "Редактировать свойства блока действия" -#: ../dialogs/ArrayTypeDialog.py:45 +#: ../dialogs/ArrayTypeDialog.py:44 msgid "Edit array type properties" msgstr "Редактировать свойства массива" -#: ../editors/Viewer.py:2575 ../editors/Viewer.py:3004 +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 msgid "Edit comment" msgstr "Редактировать комментарий" @@ -1524,19 +1487,20 @@ msgid "Edit item" msgstr "Редактировать элемент" -#: ../editors/Viewer.py:2963 +#: ../editors/Viewer.py:3014 msgid "Edit jump target" msgstr "Редактирование безусловного перехода" -#: ../ProjectController.py:1811 +#: ../ProjectController.py:1874 msgid "Edit raw IEC code added to code generated by PLCGenerator" -msgstr "Редактировать МЭК-код добавленный к коду сгенерированному PLCGenerator" +msgstr "" +"Редактировать МЭК-код добавленный к коду сгенерированному PLCGenerator" #: ../editors/SFCViewer.py:799 msgid "Edit step name" msgstr "Редактировать имя шага" -#: ../dialogs/SFCTransitionDialog.py:51 +#: ../dialogs/SFCTransitionDialog.py:52 msgid "Edit transition" msgstr "Редактировать переход" @@ -1544,18 +1508,15 @@ msgid "Editor ToolBar" msgstr "Редактор панели инструментов" -#: ../ProjectController.py:1195 +#: ../ProjectController.py:1257 msgid "Editor selection" msgstr "Редактор выделения" -msgid "Elapsed time of ramp" -msgstr "Прошедшее время нарастания" - #: ../editors/DataTypeEditor.py:348 msgid "Elements :" msgstr "Элементы:" -#: ../ProjectController.py:1300 +#: ../ProjectController.py:1362 msgid "Empty" msgstr "Нет программы" @@ -1563,18 +1524,15 @@ msgid "Enable Undo/Redo" msgstr "Разрешить отмену и повтор операций" -msgid "Enabled" -msgstr "Разрешено" - -#: ../Beremiz_service.py:331 +#: ../Beremiz_service.py:333 msgid "Enter a name " msgstr "Введите имя" -#: ../Beremiz_service.py:316 +#: ../Beremiz_service.py:318 msgid "Enter a port number " msgstr "Введите номер порта" -#: ../Beremiz_service.py:307 +#: ../Beremiz_service.py:309 msgid "Enter the IP of the interface to bind" msgstr "Введите IP-адрес используемого интерфейса" @@ -1586,44 +1544,50 @@ msgid "Equal to" msgstr "Равно" -#: ../dialogs/ForceVariableDialog.py:179 +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 #: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 #: ../dialogs/DurationEditorDialog.py:121 -#: ../dialogs/DurationEditorDialog.py:163 ../dialogs/PouTransitionDialog.py:112 -#: ../dialogs/BlockPreviewDialog.py:236 ../dialogs/ProjectDialog.py:71 -#: ../dialogs/ArrayTypeDialog.py:97 ../dialogs/ArrayTypeDialog.py:103 -#: ../dialogs/PouNameDialog.py:54 ../dialogs/BrowseLocationsDialog.py:216 -#: ../dialogs/BrowseValuesLibraryDialog.py:83 ../dialogs/PouActionDialog.py:104 -#: ../dialogs/PouDialog.py:134 ../PLCOpenEditor.py:335 ../PLCOpenEditor.py:340 -#: ../PLCOpenEditor.py:420 ../PLCOpenEditor.py:430 ../editors/Viewer.py:423 -#: ../editors/LDViewer.py:666 ../editors/LDViewer.py:882 -#: ../editors/LDViewer.py:886 ../editors/DataTypeEditor.py:550 -#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:579 -#: ../editors/DataTypeEditor.py:584 ../editors/DataTypeEditor.py:594 -#: ../editors/DataTypeEditor.py:745 ../editors/DataTypeEditor.py:752 -#: ../editors/TextViewer.py:389 ../editors/CodeFileEditor.py:783 -#: ../ProjectController.py:293 ../ProjectController.py:421 -#: ../ProjectController.py:428 ../controls/FolderTree.py:217 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 #: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 #: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 #: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 -#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:772 -#: ../Beremiz.py:1203 ../IDEFrame.py:1003 ../IDEFrame.py:1614 -#: ../IDEFrame.py:1655 ../IDEFrame.py:1660 ../IDEFrame.py:1674 -#: ../IDEFrame.py:1679 ../Beremiz_service.py:211 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 msgid "Error" msgstr "Ошибка" -#: ../ProjectController.py:727 -msgid "Error : At least one configuration and one resource must be declared in PLC !\n" -msgstr "Ошибка: Как минимум одна конфигурация и один ресурс должны быть задекларированы в ПЛК!\n" - -#: ../ProjectController.py:719 +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" +"Ошибка: Как минимум одна конфигурация и один ресурс должны быть " +"задекларированы в ПЛК!\n" + +#: ../ProjectController.py:781 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "Ошибка: компилятор МЭК в C вернул код ошибки %d\n" -#: ../ProjectController.py:621 +#: ../ProjectController.py:712 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" @@ -1635,28 +1599,28 @@ msgid "Error while saving \"%s\"\n" msgstr "Ошибка во время сохранения \"%s\"\n" -#: ../canfestival/canfestival.py:168 +#: ../canfestival/canfestival.py:170 msgid "Error: Export slave failed\n" msgstr "Ошибка: неудачный экспорт ведомого\n" -#: ../canfestival/canfestival.py:369 +#: ../canfestival/canfestival.py:371 msgid "Error: No Master generated\n" msgstr "Ошибка: мастер не сгенерирован\n" -#: ../canfestival/canfestival.py:364 +#: ../canfestival/canfestival.py:366 msgid "Error: No PLC built\n" msgstr "Ошибка: ПЛК не собран\n" -#: ../ProjectController.py:1666 +#: ../ProjectController.py:1728 #, python-format msgid "Exception while connecting %s!\n" msgstr "Исключение во время подключения %s!\n" -#: ../dialogs/FBDBlockDialog.py:117 +#: ../dialogs/FBDBlockDialog.py:120 msgid "Execution Control:" msgstr "Управление исполнением:" -#: ../dialogs/FBDVariableDialog.py:79 ../dialogs/FBDBlockDialog.py:105 +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 msgid "Execution Order:" msgstr "Порядок исполнения:" @@ -1672,7 +1636,7 @@ msgid "Exponentiation" msgstr "Взятие экспоненты" -#: ../canfestival/canfestival.py:174 +#: ../canfestival/canfestival.py:176 msgid "Export CanOpen slave to EDS file" msgstr "Экспортировать CanOpen ведомое устройство в EDS файл" @@ -1680,11 +1644,11 @@ msgid "Export graph values to clipboard" msgstr "Экспортировать график значений в буфер обмена" -#: ../canfestival/canfestival.py:173 +#: ../canfestival/canfestival.py:175 msgid "Export slave" msgstr "Экспортировать ведомое устройство" -#: ../dialogs/FBDVariableDialog.py:89 +#: ../dialogs/FBDVariableDialog.py:90 msgid "Expression:" msgstr "Выражение:" @@ -1692,44 +1656,38 @@ msgid "External" msgstr "Внешний" -#: ../ProjectController.py:740 +#: ../ProjectController.py:802 msgid "Extracting Located Variables...\n" msgstr "Экспорт локальных переменных...\n" -msgid "FB for derivative term" -msgstr "ФБ дифференцирования" - -msgid "FB for integral term" -msgstr "ФД интегрирования" - -#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "FBD" msgstr "FBD" -#: ../ProjectController.py:1729 +#: ../ProjectController.py:1791 msgid "Failed : Must build before transfer.\n" msgstr "Ошибка: необходима сборка перед передачей.\n" -#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:462 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 msgid "Falling Edge" msgstr "Спадающий фронт" -#: ../ProjectController.py:1008 +#: ../ProjectController.py:1070 msgid "Fatal : cannot get builder.\n" msgstr "Ошибка: невозможно получить сборщик.\n" -#: ../Beremiz.py:118 +#: ../Beremiz.py:156 #, python-format msgid "Fetching %s" msgstr "Проверка %s" -#: ../dialogs/DurationEditorDialog.py:160 +#: ../dialogs/DurationEditorDialog.py:164 #, python-format msgid "Field %s hasn't a valid value!" msgstr "Поле %s имеет неверное значение!" -#: ../dialogs/DurationEditorDialog.py:162 +#: ../dialogs/DurationEditorDialog.py:166 #, python-format msgid "Fields %s haven't a valid value!" msgstr "Поля %s имеют неверные значения!" @@ -1739,8 +1697,8 @@ msgid "File '%s' already exists!" msgstr "Файл '%s' уже существует!" -#: ../dialogs/SearchInProjectDialog.py:100 ../dialogs/FindInPouDialog.py:36 -#: ../dialogs/FindInPouDialog.py:106 ../IDEFrame.py:375 +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 msgid "Find" msgstr "Поиск" @@ -1756,7 +1714,7 @@ msgid "Find position" msgstr "Поиск позиции" -#: ../dialogs/FindInPouDialog.py:57 +#: ../dialogs/FindInPouDialog.py:55 msgid "Find:" msgstr "Поиск:" @@ -1764,35 +1722,35 @@ msgid "Force runtime reload\n" msgstr "Принудительный перезапуск системы исполнения\n" -#: ../editors/Viewer.py:1553 +#: ../editors/Viewer.py:1600 msgid "Force value" -msgstr "Фиксировать значение" +msgstr "Форсировать значение" #: ../dialogs/ForceVariableDialog.py:162 msgid "Forcing Variable Value" msgstr "Форсировать значение переменной" -#: ../dialogs/SFCTransitionDialog.py:179 ../dialogs/PouTransitionDialog.py:102 -#: ../dialogs/ProjectDialog.py:70 ../dialogs/PouActionDialog.py:94 -#: ../dialogs/PouDialog.py:116 +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 #, python-format msgid "Form isn't complete. %s must be filled!" msgstr "Форма заполнена неполностью. %s должен быть заполнен!" -#: ../dialogs/SFCStepDialog.py:144 ../dialogs/FBDBlockDialog.py:232 -#: ../dialogs/ConnectionDialog.py:160 +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 msgid "Form isn't complete. Name must be filled!" msgstr "Форма заполнена неполностью. Имя должно быть заполнено!" -#: ../dialogs/FBDBlockDialog.py:228 +#: ../dialogs/FBDBlockDialog.py:232 msgid "Form isn't complete. Valid block type must be selected!" msgstr "Форма заполнена неполностью. Должен быть выбран корректный тип блока!" -#: ../dialogs/FindInPouDialog.py:74 +#: ../dialogs/FindInPouDialog.py:72 msgid "Forward" msgstr "Вперед" -#: ../dialogs/SearchInProjectDialog.py:36 ../IDEFrame.py:1746 +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 msgid "Function" msgstr "Функция" @@ -1800,43 +1758,40 @@ msgid "Function &Block" msgstr "Функциональный &блок" -#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1745 -#: ../IDEFrame.py:1938 +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 msgid "Function Block" msgstr "Функциональный блок" -#: ../controls/VariablePanel.py:825 +#: ../controls/VariablePanel.py:854 msgid "Function Block Types" msgstr "Типы функциональных блоков" -#: ../PLCControler.py:95 +#: ../PLCControler.py:97 msgid "Function Blocks" msgstr "Функциональные блоки" -#: ../editors/Viewer.py:248 +#: ../editors/Viewer.py:249 msgid "Function Blocks can't be used in Functions!" msgstr "Функциональные блоки не могут использоваться в функциях!" -#: ../PLCControler.py:2337 +#: ../PLCControler.py:2343 #, python-format msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" msgstr "Функциональный блок \"%s\" не может быть вставлен в функцию!!!" -#: ../PLCControler.py:95 +#: ../PLCControler.py:97 msgid "Functions" msgstr "Функции" -#: ../PLCOpenEditor.py:115 +#: ../PLCOpenEditor.py:117 msgid "Generate Program" msgstr "Сгенерировать программу" -#: ../ProjectController.py:612 +#: ../ProjectController.py:703 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "Генерация МЭК-61131 ST/IL/SFC кода ПЛК...\n" -msgid "Generic" -msgstr "Generic" - #: ../controls/VariablePanel.py:73 msgid "Global" msgstr "Глобальный" @@ -1845,7 +1800,7 @@ msgid "Go to current value" msgstr "Перейти к текущему значению" -#: ../controls/ProjectPropertiesPanel.py:173 +#: ../controls/ProjectPropertiesPanel.py:174 msgid "Graphics" msgstr "Графика" @@ -1857,15 +1812,15 @@ msgid "Greater than or equal to" msgstr "Больше или равно" -#: ../controls/ProjectPropertiesPanel.py:134 +#: ../controls/ProjectPropertiesPanel.py:135 msgid "Grid Resolution:" msgstr "Шаг сетки:" -#: ../runtime/NevowServer.py:181 +#: ../runtime/NevowServer.py:182 msgid "HTTP interface port :" msgstr "Порт HTTP-интерфейса :" -#: ../controls/ProjectPropertiesPanel.py:120 +#: ../controls/ProjectPropertiesPanel.py:121 msgid "Height:" msgstr "Высота:" @@ -1873,18 +1828,16 @@ msgid "Home Directory:" msgstr "Домашняя директория:" -#: ../controls/ProjectPropertiesPanel.py:150 +#: ../controls/ProjectPropertiesPanel.py:151 msgid "Horizontal:" msgstr "Горизонтальный:" -#: ../dialogs/DurationEditorDialog.py:44 +#: ../dialogs/DurationEditorDialog.py:45 msgid "Hours:" msgstr "Часы:" -msgid "IEC_Channel" -msgstr "МЭК-канал" - -#: ../dialogs/PouActionDialog.py:31 ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "IL" msgstr "IL" @@ -1892,20 +1845,20 @@ msgid "IP" msgstr "IP" -#: ../Beremiz_service.py:308 ../Beremiz_service.py:309 +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 msgid "IP is not valid!" msgstr "Неверный IP-адрес" -#: ../svgui/svgui.py:42 ../svgui/svgui.py:43 +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 msgid "Import SVG" msgstr "Импорт SVG" -#: ../dialogs/FBDVariableDialog.py:38 ../editors/Viewer.py:1580 +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 #: ../controls/VariablePanel.py:71 msgid "InOut" msgstr "Вход/Выход" -#: ../editors/Viewer.py:1100 +#: ../editors/Viewer.py:431 msgid "Inactive" msgstr "Неактивный" @@ -1924,11 +1877,15 @@ msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" msgstr "\"{a1}\" и \"{a2}\" имеют несовместимый размер данных" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Indicator" msgstr "Индикатор" -#: ../editors/Viewer.py:552 +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "Исходное значение" + +#: ../editors/Viewer.py:611 msgid "Initial Step" msgstr "Исходный шаг" @@ -1942,32 +1899,23 @@ msgid "Initial Value:" msgstr "Исходное значение:" -msgid "Initial value" -msgstr "Исходное значение" - -#: ../svgui/svgui.py:46 +#: ../svgui/svgui.py:48 msgid "Inkscape" msgstr "Inkscape" -#: ../dialogs/SFCTransitionDialog.py:75 ../dialogs/ActionBlockDialog.py:42 +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 msgid "Inline" msgstr "Непосредственно" -#: ../dialogs/SFCStepDialog.py:70 ../dialogs/FBDVariableDialog.py:37 -#: ../dialogs/BrowseLocationsDialog.py:40 ../editors/Viewer.py:289 -#: ../editors/Viewer.py:1578 ../editors/TextViewer.py:307 +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 #: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 #: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Input" msgstr "Вход" -msgid "Input to be differentiated" -msgstr "Вход для дифференцирования" - -msgid "Input variable" -msgstr "Входная переменная" - -#: ../dialogs/FBDBlockDialog.py:93 +#: ../dialogs/FBDBlockDialog.py:96 msgid "Inputs:" msgstr "Входы:" @@ -1975,7 +1923,7 @@ msgid "Insertion (into)" msgstr "Вставка подстроки" -#: ../plcopen/plcopen.py:1691 +#: ../plcopen/plcopen.py:1696 #, python-format msgid "Instance with id %d doesn't exist!" msgstr "Экземпляр с id %d не существует!" @@ -1984,9 +1932,6 @@ msgid "Instances:" msgstr "Экземпляры:" -msgid "Integrated output" -msgstr "Интегрированный выход" - #: ../controls/VariablePanel.py:70 msgid "Interface" msgstr "Интерфейс" @@ -1999,7 +1944,7 @@ msgid "Interval" msgstr "Интервал" -#: ../PLCControler.py:2325 +#: ../PLCControler.py:2331 msgid "Invalid plcopen element(s)!!!" msgstr "Некорректный PlcOpen элемент(ы)!!!" @@ -2025,12 +1970,12 @@ msgid "Invalid value \"%s\" for variable grid element" msgstr "Неверное значение \"%s\" для значения размещения переменной" -#: ../editors/Viewer.py:233 ../editors/Viewer.py:236 +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 #, python-format msgid "Invalid value \"%s\" for viewer block" msgstr "Неверное значение \"%s\" для вставки в редактор" -#: ../dialogs/ForceVariableDialog.py:177 +#: ../dialogs/ForceVariableDialog.py:195 #, python-brace-format msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" msgstr "Неверное значение \"{a1}\" для переменной \"{a2}\"!" @@ -2043,54 +1988,53 @@ "Неверное значение!\n" "Необходимо ввести числовое значение." -#: ../editors/Viewer.py:557 ../editors/Viewer.py:2343 +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 msgid "Jump" msgstr "Безусловный переход" -#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "LD" msgstr "LD" -msgid "LDFLAGS" -msgstr "LDFLAGS" - #: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 #, python-format msgid "Ladder element with id %d is on more than one rung." msgstr "Элемент лестничной диаграммы с id %d более чем на одной ступени." -#: ../dialogs/PouTransitionDialog.py:91 ../dialogs/PouActionDialog.py:83 -#: ../dialogs/PouDialog.py:104 +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 msgid "Language" msgstr "Язык" -#: ../controls/ProjectPropertiesPanel.py:186 +#: ../controls/ProjectPropertiesPanel.py:187 msgid "Language (optional):" msgstr "Язык (опционально):" -#: ../dialogs/PouTransitionDialog.py:65 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 #: ../dialogs/PouDialog.py:73 msgid "Language:" msgstr "Язык:" -#: ../ProjectController.py:1735 +#: ../ProjectController.py:1797 msgid "Latest build already matches current target. Transfering anyway...\n" -msgstr "Загружаемая программа совпадает с текущий программой в целевом ПЛК. Загрузка продолжена...\n" - -#: ../Beremiz_service.py:271 +msgstr "" +"Загружаемая программа совпадает с текущий программой в целевом ПЛК. Загрузка" +" продолжена...\n" + +#: ../Beremiz_service.py:273 msgid "Launch WX GUI inspector" msgstr "Запустить WX GUI Inspector" -#: ../Beremiz_service.py:270 +#: ../Beremiz_service.py:272 msgid "Launch a live Python shell" msgstr "Запустить консоль Python" -#: ../editors/Viewer.py:485 +#: ../editors/Viewer.py:544 msgid "Left" msgstr "Слева" -#: ../dialogs/LDPowerRailDialog.py:62 +#: ../dialogs/LDPowerRailDialog.py:63 msgid "Left PowerRail" msgstr "Левая шина питания" @@ -2106,14 +2050,11 @@ msgid "Less than or equal to" msgstr "Меньше либо равно" -msgid "Libraries" -msgstr "Библиотеки" - #: ../IDEFrame.py:631 msgid "Library" msgstr "Библиотеки" -#: ../dialogs/AboutDialog.py:143 +#: ../dialogs/AboutDialog.py:151 msgid "License" msgstr "Лицензия" @@ -2121,35 +2062,29 @@ msgid "Limitation" msgstr "Ограничение" -msgid "Linker" -msgstr "Линковщик" - -#: ../targets/toolchain_gcc.py:166 +#: ../targets/toolchain_gcc.py:202 msgid "Linking :\n" msgstr "Линковка:\n" -msgid "Linux" -msgstr "GNU/Linux" - -#: ../dialogs/DiscoveryDialog.py:111 ../controls/VariablePanel.py:72 +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 msgid "Local" msgstr "Локальный" -#: ../canfestival/canfestival.py:346 +#: ../canfestival/canfestival.py:348 msgid "Local entries" msgstr "Локальные записи" -#: ../ProjectController.py:1641 +#: ../ProjectController.py:1703 msgid "Local service discovery failed!\n" msgstr "Локальный сервис не найден!\n" #: ../controls/VariablePanel.py:53 msgid "Location" -msgstr "Размещение" +msgstr "Адрес" #: ../dialogs/BrowseLocationsDialog.py:72 msgid "Locations available:" -msgstr "Доступные размещения:" +msgstr "Доступные адреса:" #: ../plcopen/iec_std.csv:25 msgid "Logarithm to base 10" @@ -2160,9 +2095,6 @@ msgid "MDNS resolution failure for '%s'\n" msgstr "MDNS разрешение неудачно для '%s'\n" -msgid "Manual output adjustment - Typically from transfer station" -msgstr "Ручной выход" - #: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 msgid "Map Variable" msgstr "Отображение переменной" @@ -2178,7 +2110,8 @@ #: ../ConfigTreeNode.py:539 #, python-brace-format msgid "Max count ({a1}) reached for this confnode of type {a2} " -msgstr "Достигнуто максимальное количество ({a1}) для типа узла конфигурации {a2} " +msgstr "" +"Достигнуто максимальное количество ({a1}) для типа узла конфигурации {a2} " #: ../plcopen/iec_std.csv:71 msgid "Maximum" @@ -2188,7 +2121,7 @@ msgid "Maximum:" msgstr "Максимум:" -#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:289 +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 #: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 #: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Memory" @@ -2198,15 +2131,15 @@ msgid "Menu ToolBar" msgstr "Меню" -#: ../dialogs/DurationEditorDialog.py:48 +#: ../dialogs/DurationEditorDialog.py:49 msgid "Microseconds:" msgstr "Микросекунды:" -#: ../editors/Viewer.py:490 +#: ../editors/Viewer.py:549 msgid "Middle" msgstr "Посередине" -#: ../dialogs/DurationEditorDialog.py:47 +#: ../dialogs/DurationEditorDialog.py:48 msgid "Milliseconds:" msgstr "Миллисекунды:" @@ -2218,11 +2151,11 @@ msgid "Minimum:" msgstr "Минимум:" -#: ../dialogs/DurationEditorDialog.py:45 +#: ../dialogs/DurationEditorDialog.py:46 msgid "Minutes:" msgstr "Минуты:" -#: ../controls/ProjectPropertiesPanel.py:210 +#: ../controls/ProjectPropertiesPanel.py:211 msgid "Miscellaneous" msgstr "Прочее" @@ -2232,8 +2165,12 @@ #: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 #, python-brace-format -msgid "More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" -msgstr "Более одного коннектора соответствуют продолжению цепи \"{a1}\" в POU \"{a2}\"" +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" +"Более одного коннектора соответствуют продолжению цепи \"{a1}\" в POU " +"\"{a2}\"" #: ../dialogs/ActionBlockDialog.py:140 msgid "Move action down" @@ -2303,21 +2240,18 @@ msgid "NAME" msgstr "Имя" -msgid "NOT R1" -msgstr "НЕ R1" - #: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 -#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 -#: ../controls/VariablePanel.py:54 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Name" msgstr "Имя" -#: ../Beremiz_service.py:332 +#: ../Beremiz_service.py:334 msgid "Name must not be null!" msgstr "Имя не может быть null!" -#: ../dialogs/SFCStepDialog.py:56 ../dialogs/FBDBlockDialog.py:83 -#: ../dialogs/ConnectionDialog.py:75 +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 msgid "Name:" msgstr "Имя:" @@ -2325,20 +2259,20 @@ msgid "Natural logarithm" msgstr "Натуральный логарифм" -#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:460 +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 msgid "Negated" msgstr "Инверсия" -#: ../Beremiz_service.py:578 +#: ../Beremiz_service.py:580 msgid "Nevow Web service failed. " msgstr "Ошибка Web сервиса Nevow. " -#: ../Beremiz_service.py:554 +#: ../Beremiz_service.py:556 msgid "Nevow/Athena import failed :" msgstr "Ошибка импорта Nevow/Athena :" -#: ../PLCOpenEditor.py:102 ../PLCOpenEditor.py:144 ../Beremiz.py:321 -#: ../Beremiz.py:356 +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 msgid "New" msgstr "Новый" @@ -2346,11 +2280,11 @@ msgid "New item" msgstr "Новый элемент" -#: ../editors/Viewer.py:459 +#: ../editors/Viewer.py:518 msgid "No Modifier" msgstr "Нет модификатора" -#: ../ProjectController.py:1763 +#: ../ProjectController.py:1826 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "Нет ПЛК для передачи (была сборка успешна?)\n" @@ -2364,7 +2298,7 @@ msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" msgstr "Не найден коннектор, соответствующий продолжению цепи \"{a1}\" в POU \"{a2}\"" -#: ../PLCOpenEditor.py:347 +#: ../PLCOpenEditor.py:357 msgid "" "No documentation available.\n" "Coming soon." @@ -2379,14 +2313,16 @@ #: ../PLCGenerator.py:1194 #, python-brace-format -msgid "No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken" +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" msgstr "Выходная переменная {a1} не найдена в блоке {a2} в POU {a3}." #: ../controls/SearchResultPanel.py:169 msgid "No search results available." msgstr "Ничего не найдено." -#: ../svgui/svgui.py:131 +#: ../svgui/svgui.py:134 #, python-format msgid "No such SVG file: %s\n" msgstr "Нет такого SVG файла: %s\n" @@ -2410,9 +2346,6 @@ msgid "No variable defined in \"%s\" POU" msgstr "Переменная не определена в POU \"%s\"" -msgid "NodeId" -msgstr "ID узла" - #: ../canfestival/config_utils.py:355 #, python-brace-format msgid "Non existing node ID : {a1} (variable {a2})" @@ -2429,7 +2362,8 @@ #: ../canfestival/config_utils.py:389 #, python-brace-format msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" -msgstr "Не переменная для отображения в PDO: '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Не переменная для отображения в PDO: '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" #: ../plcopen/iec_std.csv:80 msgid "Not equal to" @@ -2443,40 +2377,49 @@ msgid "Numerical" msgstr "Математические функции" -#: ../dialogs/SearchInProjectDialog.py:86 +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "При изменении" + +#: ../dialogs/SearchInProjectDialog.py:84 msgid "Only Elements" msgstr "Только элементы" -#: ../PLCOpenEditor.py:104 ../PLCOpenEditor.py:145 ../Beremiz.py:323 -#: ../Beremiz.py:357 +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 msgid "Open" msgstr "Открыть" -#: ../svgui/svgui.py:140 +#: ../svgui/svgui.py:143 msgid "Open Inkscape" msgstr "Открыть Inkscape" -#: ../version.py:66 -msgid "Open Source framework for automation, implemented IEC 61131 IDE with constantly growing set of extensions and flexible PLC runtime." -msgstr "Свободное программное обеспечение для промышленной автоматизации, состоящие из среды разработки программ по стандарту МЭК 61131 с постоянно расширяющимся набором плагинов и гибкой системой исполнения для ПЛК." - -#: ../ProjectController.py:1815 +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" +"Свободное программное обеспечение для промышленной автоматизации, состоящие " +"из среды разработки программ по стандарту МЭК 61131 с постоянно " +"расширяющимся набором плагинов и гибкой системой исполнения для ПЛК." + +#: ../ProjectController.py:1878 msgid "Open a file explorer to manage project files" msgstr "Открыть файловый менеджер для просмотра файлов проекта" -#: ../wxglade_hmi/wxglade_hmi.py:138 +#: ../wxglade_hmi/wxglade_hmi.py:155 msgid "Open wxGlade" msgstr "Открыть wxGlade" #: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Option" -msgstr "Настройка" - -#: ../dialogs/FindInPouDialog.py:83 +msgstr "Квалификатор" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 msgid "Options" msgstr "Настройки" -#: ../controls/ProjectPropertiesPanel.py:97 +#: ../controls/ProjectPropertiesPanel.py:98 msgid "Organization (optional):" msgstr "Организация (опционально):" @@ -2484,17 +2427,14 @@ msgid "Other Profile" msgstr "Другой профиль" -#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:39 -#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:289 -#: ../editors/Viewer.py:1579 ../editors/TextViewer.py:307 +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 #: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 #: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Output" msgstr "Выход" -msgid "Overriding reset" -msgstr "Сброс интегратора" - #: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 msgid "PDO Receive" msgstr "PDO приема" @@ -2503,30 +2443,27 @@ msgid "PDO Transmit" msgstr "PDO передачи" -msgid "PLC" -msgstr "ПЛК" - -#: ../targets/toolchain_gcc.py:131 +#: ../targets/toolchain_gcc.py:167 msgid "PLC :\n" msgstr "ПЛК:\n" -#: ../Beremiz.py:453 +#: ../BeremizIDE.py:355 msgid "PLC Log" msgstr "Лог ПЛК" -#: ../ProjectController.py:992 +#: ../ProjectController.py:1054 msgid "PLC code generation failed !\n" msgstr "Неудачная генерация кода!\n" -#: ../Beremiz_service.py:295 +#: ../Beremiz_service.py:297 msgid "PLC is empty or already started." msgstr "В ПЛК нет программы или он уже запущен." -#: ../Beremiz_service.py:302 +#: ../Beremiz_service.py:304 msgid "PLC is not started." msgstr "ПЛК незапущен." -#: ../PLCOpenEditor.py:196 ../PLCOpenEditor.py:309 +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 #, python-brace-format msgid "" "PLC syntax error at line {a1}:\n" @@ -2535,15 +2472,15 @@ "Синтаксическая ошибка в строке {a1}:\n" "{a2}" -#: ../PLCOpenEditor.py:292 ../PLCOpenEditor.py:373 +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" msgstr "PLCOpen файлы (*.xml)|*.xml|All files|*.*" -#: ../PLCOpenEditor.py:152 ../PLCOpenEditor.py:209 +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 msgid "PLCOpenEditor" msgstr "PLCOpenEditor" -#: ../PLCOpenEditor.py:355 +#: ../PLCOpenEditor.py:365 msgid "" "PLCOpenEditor is part of Beremiz project.\n" "\n" @@ -2557,7 +2494,7 @@ msgid "PORT" msgstr "Порт" -#: ../dialogs/PouDialog.py:100 +#: ../dialogs/PouDialog.py:101 msgid "POU Name" msgstr "Имя POU" @@ -2565,7 +2502,7 @@ msgid "POU Name:" msgstr "Имя POU:" -#: ../dialogs/PouDialog.py:102 +#: ../dialogs/PouDialog.py:103 msgid "POU Type" msgstr "Тип POU" @@ -2573,9 +2510,6 @@ msgid "POU Type:" msgstr "Тип POU:" -msgid "PV - SP" -msgstr "Ошибка, PV - SP" - #: ../connectors/PYRO/__init__.py:45 #, python-format msgid "PYRO connecting to URI : %s\n" @@ -2586,15 +2520,15 @@ msgid "PYRO using certificates in '%s' \n" msgstr "PYRO использует сертификаты в '%s'\n" -#: ../PLCOpenEditor.py:118 ../Beremiz.py:336 +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 msgid "Page Setup" msgstr "Настройки страницы" -#: ../controls/ProjectPropertiesPanel.py:110 +#: ../controls/ProjectPropertiesPanel.py:111 msgid "Page Size (optional):" msgstr "Размер страницы (опционально):" -#: ../IDEFrame.py:2599 +#: ../IDEFrame.py:2613 #, python-format msgid "Page: %d" msgstr "Страница: %d" @@ -2603,11 +2537,11 @@ msgid "Parent instance" msgstr "Родительский экземпляр" -#: ../editors/Viewer.py:598 ../IDEFrame.py:372 ../IDEFrame.py:426 +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 msgid "Paste" msgstr "Вставить" -#: ../IDEFrame.py:1865 +#: ../IDEFrame.py:1868 msgid "Paste POU" msgstr "Вставить POU" @@ -2615,11 +2549,11 @@ msgid "Pattern to search:" msgstr "Шаблон поиска:" -#: ../dialogs/LDPowerRailDialog.py:73 +#: ../dialogs/LDPowerRailDialog.py:74 msgid "Pin number:" msgstr "Номер пина:" -#: ../editors/Viewer.py:2706 ../editors/Viewer.py:2963 +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 #: ../editors/SFCViewer.py:770 msgid "Please choose a target" msgstr "Выберите цель перехода" @@ -2628,7 +2562,7 @@ msgid "Please enter a block name" msgstr "Введите имя блока" -#: ../editors/Viewer.py:2576 ../editors/Viewer.py:3005 +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 msgid "Please enter comment text" msgstr "Введите текст комментария" @@ -2637,7 +2571,7 @@ msgid "Please enter step name" msgstr "Введите имя шага" -#: ../Beremiz_service.py:194 +#: ../Beremiz_service.py:196 msgid "Please enter text" msgstr "Введите текст" @@ -2646,26 +2580,23 @@ msgid "Please enter value for a \"%s\" variable:" msgstr "Введите значение для переменной \"%s\":" -#: ../Beremiz_service.py:317 +#: ../Beremiz_service.py:319 msgid "Port number must be 0 <= port <= 65535!" msgstr "Номер порта должен быть в диапазоне от 0 до 65535!" -#: ../Beremiz_service.py:317 +#: ../Beremiz_service.py:319 msgid "Port number must be an integer!" msgstr "Номер порта должен быть целым числом!" -#: ../editors/Viewer.py:536 ../editors/Viewer.py:2367 +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 msgid "Power Rail" msgstr "Шина питания" -#: ../dialogs/LDPowerRailDialog.py:50 +#: ../dialogs/LDPowerRailDialog.py:51 msgid "Power Rail Properties" msgstr "Свойства шины питания" -msgid "Preset datetime" -msgstr "Основное время" - -#: ../PLCOpenEditor.py:120 ../Beremiz.py:338 +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 msgid "Preview" msgstr "Просмотр" @@ -2673,12 +2604,12 @@ msgid "Preview:" msgstr "Просмотр:" -#: ../PLCOpenEditor.py:122 ../PLCOpenEditor.py:148 ../Beremiz.py:340 -#: ../Beremiz.py:360 +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 msgid "Print" msgstr "Печать" -#: ../IDEFrame.py:1075 +#: ../IDEFrame.py:1079 msgid "Print preview" msgstr "Предварительный просмотр" @@ -2686,56 +2617,53 @@ msgid "Priority" msgstr "Приоритет" -#: ../dialogs/SFCTransitionDialog.py:89 +#: ../dialogs/SFCTransitionDialog.py:90 msgid "Priority:" msgstr "Приоритет:" -#: ../runtime/PLCObject.py:370 +#: ../runtime/PLCObject.py:369 #, python-format msgid "Problem starting PLC : error %d" msgstr "Проблема запуска ПЛК: ошибка %d" -msgid "Process variable" -msgstr "Текущее значение регулируемой переменной" - -#: ../dialogs/ProjectDialog.py:55 +#: ../dialogs/ProjectDialog.py:58 msgid "Product Name" msgstr "Имя продукта" -#: ../controls/ProjectPropertiesPanel.py:80 +#: ../controls/ProjectPropertiesPanel.py:81 msgid "Product Name (required):" msgstr "Имя продукта (обязательно):" -#: ../controls/ProjectPropertiesPanel.py:82 +#: ../controls/ProjectPropertiesPanel.py:83 msgid "Product Release (optional):" -msgstr "Релиз продукта (опцинально):" - -#: ../dialogs/ProjectDialog.py:56 +msgstr "Релиз продукта (опционально):" + +#: ../dialogs/ProjectDialog.py:59 msgid "Product Version" msgstr "Версия продукта" -#: ../controls/ProjectPropertiesPanel.py:81 +#: ../controls/ProjectPropertiesPanel.py:82 msgid "Product Version (required):" msgstr "Версия продукта (обязательно):" -#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1744 -#: ../IDEFrame.py:1941 +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 msgid "Program" msgstr "Программа" -#: ../PLCOpenEditor.py:337 +#: ../PLCOpenEditor.py:347 msgid "Program was successfully generated!" msgstr "Программа успешно сгенерирована!" -#: ../PLCControler.py:96 +#: ../PLCControler.py:98 msgid "Programs" msgstr "Программы" -#: ../editors/Viewer.py:242 +#: ../editors/Viewer.py:243 msgid "Programs can't be used by other POUs!" msgstr "Программы не могут использоваться другими POU!" -#: ../controls/ProjectPropertiesPanel.py:84 ../IDEFrame.py:584 +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 msgid "Project" msgstr "Проект" @@ -2744,23 +2672,23 @@ msgid "Project '%s':" msgstr "Проект '%s':" -#: ../ProjectController.py:1814 +#: ../ProjectController.py:1877 msgid "Project Files" msgstr "Файлы проекта" -#: ../dialogs/ProjectDialog.py:54 +#: ../dialogs/ProjectDialog.py:57 msgid "Project Name" msgstr "Имя проекта" -#: ../controls/ProjectPropertiesPanel.py:78 +#: ../controls/ProjectPropertiesPanel.py:79 msgid "Project Name (required):" msgstr "Имя проекта (обязательно):" -#: ../controls/ProjectPropertiesPanel.py:79 +#: ../controls/ProjectPropertiesPanel.py:80 msgid "Project Version (optional):" msgstr "Версия проекта (опционально):" -#: ../PLCControler.py:3158 +#: ../PLCControler.py:3164 msgid "" "Project file syntax error:\n" "\n" @@ -2768,7 +2696,7 @@ "Синтаксическая ошибка в файле проекта:\n" "\n" -#: ../dialogs/ProjectDialog.py:32 ../editors/ProjectNodeEditor.py:37 +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 msgid "Project properties" msgstr "Свойства проекта" @@ -2777,18 +2705,15 @@ msgid "Project tree layout do not match confnode.xml {a1}!={a2} " msgstr "Дерево проекта не соответствует confnode.xml {a1}!={a2} " -#: ../dialogs/ConnectionDialog.py:94 +#: ../dialogs/ConnectionDialog.py:98 msgid "Propagate Name" msgstr "Для всей цепи" -#: ../PLCControler.py:97 +#: ../PLCControler.py:99 msgid "Properties" msgstr "Свойства" -msgid "Proportionality constant" -msgstr "Коэффициент пропорциональности" - -#: ../Beremiz_service.py:440 +#: ../Beremiz_service.py:442 msgid "Publishing service on local network" msgstr "Сервис доступен в локальной сети" @@ -2797,11 +2722,11 @@ msgid "Pyro exception: %s\n" msgstr "Исключение Pyro: %s\n" -#: ../Beremiz_service.py:427 +#: ../Beremiz_service.py:429 msgid "Pyro object's uri :" msgstr "URI для Pyro :" -#: ../Beremiz_service.py:426 +#: ../Beremiz_service.py:428 msgid "Pyro port :" msgstr "Порт Pyro :" @@ -2813,26 +2738,23 @@ msgid "Python file" msgstr "Python файл" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Qualifier" msgstr "Спецификатор" -#: ../PLCOpenEditor.py:128 ../Beremiz.py:343 ../Beremiz_service.py:273 +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 msgid "Quit" msgstr "Выход" -msgid "Ramp duration" -msgstr "Длительность нарастания" - #: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 msgid "Range:" msgstr "Диапазон:" -#: ../ProjectController.py:1810 +#: ../ProjectController.py:1873 msgid "Raw IEC code" msgstr "МЭК-код" -#: ../Beremiz.py:1143 +#: ../BeremizIDE.py:1047 #, python-format msgid "Really delete node '%s'?" msgstr "Действительно удалить элемент '%s'?" @@ -2841,11 +2763,11 @@ msgid "Redo" msgstr "Повторить" -#: ../dialogs/SFCTransitionDialog.py:74 +#: ../dialogs/SFCTransitionDialog.py:75 msgid "Reference" msgstr "Ссылка" -#: ../dialogs/DiscoveryDialog.py:106 ../IDEFrame.py:432 +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 msgid "Refresh" msgstr "Обновить" @@ -2853,11 +2775,11 @@ msgid "Regular expression" msgstr "Регулярное выражение" -#: ../dialogs/FindInPouDialog.py:98 +#: ../dialogs/FindInPouDialog.py:96 msgid "Regular expressions" msgstr "Регулярные выражения" -#: ../editors/Viewer.py:1556 +#: ../editors/Viewer.py:1603 msgid "Release value" msgstr "Освободить значение" @@ -2865,16 +2787,16 @@ msgid "Remainder (modulo)" msgstr "Остаток от деления (modulo)" -#: ../Beremiz.py:1144 +#: ../BeremizIDE.py:1048 #, python-format msgid "Remove %s node" msgstr "Удалить %s элемент" -#: ../IDEFrame.py:2405 +#: ../IDEFrame.py:2419 msgid "Remove Datatype" msgstr "Удалить тип данных" -#: ../IDEFrame.py:2410 +#: ../IDEFrame.py:2424 msgid "Remove Pou" msgstr "Удалить POU" @@ -2906,7 +2828,7 @@ msgid "Remove variable" msgstr "Удалить переменную" -#: ../IDEFrame.py:1945 +#: ../IDEFrame.py:1948 msgid "Rename" msgstr "Переименовать" @@ -2914,7 +2836,7 @@ msgid "Replace File" msgstr "Заменить файл" -#: ../editors/Viewer.py:502 +#: ../editors/Viewer.py:561 msgid "Replace Wire by connections" msgstr "Заменить цепь подключениями" @@ -2926,7 +2848,7 @@ msgid "Reset" msgstr "Сброс" -#: ../editors/Viewer.py:583 +#: ../editors/Viewer.py:642 msgid "Reset Execution Order" msgstr "Сбросить порядок исполнения" @@ -2938,10 +2860,7 @@ msgid "Reset search result" msgstr "Сбросить результаты поиска" -msgid "Reset time" -msgstr "Постоянная времени интегрирования" - -#: ../PLCControler.py:97 ../Beremiz.py:1075 +#: ../BeremizIDE.py:979 ../PLCControler.py:99 msgid "Resources" msgstr "Ресурсы" @@ -2953,15 +2872,15 @@ msgid "Return Type:" msgstr "Возвращаемый тип:" -#: ../editors/Viewer.py:487 +#: ../editors/Viewer.py:546 msgid "Right" msgstr "Право" -#: ../dialogs/LDPowerRailDialog.py:63 +#: ../dialogs/LDPowerRailDialog.py:64 msgid "Right PowerRail" msgstr "Правая шина питания" -#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:461 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 msgid "Rising Edge" msgstr "Нарастающий фронт" @@ -2977,15 +2896,15 @@ msgid "Rounding up/down" msgstr "Округление вверх/вниз" -#: ../ProjectController.py:1778 +#: ../ProjectController.py:1841 msgid "Run" msgstr "Старт" -#: ../ProjectController.py:1037 +#: ../ProjectController.py:1099 msgid "Runtime IO extensions C code generation failed !\n" msgstr "Ошибка генерации C-кода для расширений ввода-вывода!\n" -#: ../ProjectController.py:1046 +#: ../ProjectController.py:1108 msgid "Runtime library extensions C code generation failed !\n" msgstr "Ошибка генерации C-кода для библиотеки расширений системы исполнения!\n" @@ -2997,30 +2916,32 @@ msgid "SDO Server" msgstr "SDO сервер" -#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "SFC" msgstr "SFC" #: ../PLCGenerator.py:1392 #, python-brace-format msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" -msgstr "Безусловный переход в POU \"{a1}\" ссылается на несуществующий SFC шаг \"{a2}\"" +msgstr "" +"Безусловный переход в POU \"{a1}\" ссылается на несуществующий SFC шаг " +"\"{a2}\"" #: ../PLCGenerator.py:773 #, python-format msgid "SFC transition in POU \"%s\" must be connected." msgstr "SFC переход в POU \"%s\" должен быть подключен." -#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "ST" msgstr "ST" -#: ../PLCOpenEditor.py:324 +#: ../PLCOpenEditor.py:334 msgid "ST files (*.st)|*.st|All files|*.*" msgstr "ST файлы (*.st)|*.st|Все файлы|*.*" -#: ../svgui/svgui.py:125 +#: ../svgui/svgui.py:128 msgid "SVG files (*.svg)|*.svg|All files|*.*" msgstr "SVG файлы (*.svg)|*.svg|Все файлы|*.*" @@ -3028,23 +2949,20 @@ msgid "SVGUI" msgstr "SVGUI" -msgid "Sampling period" -msgstr "Период сэмплирования" - -#: ../PLCOpenEditor.py:111 ../PLCOpenEditor.py:146 ../Beremiz.py:327 -#: ../Beremiz.py:358 +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 msgid "Save" msgstr "Сохранить" -#: ../PLCOpenEditor.py:113 ../PLCOpenEditor.py:147 ../Beremiz.py:359 +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 msgid "Save As..." msgstr "Сохранить как..." -#: ../Beremiz.py:329 +#: ../BeremizIDE.py:224 msgid "Save as" msgstr "Сохранить как" -#: ../ProjectController.py:420 +#: ../ProjectController.py:511 msgid "Save path is the same as path of a project! \n" msgstr "Выбранный путь совпадает с путём проекта!\n" @@ -3056,11 +2974,12 @@ msgid "Search" msgstr "Поиск" -#: ../dialogs/SearchInProjectDialog.py:44 ../IDEFrame.py:382 ../IDEFrame.py:428 +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 msgid "Search in Project" msgstr "Поиск в проекте" -#: ../dialogs/DurationEditorDialog.py:46 +#: ../dialogs/DurationEditorDialog.py:47 msgid "Seconds:" msgstr "Секунды:" @@ -3068,17 +2987,17 @@ msgid "Select All" msgstr "Выделить все" -#: ../editors/Viewer.py:288 ../editors/TextViewer.py:306 +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 #: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 #: ../controls/VariablePanel.py:350 msgid "Select a variable class:" msgstr "Выберите класс переменной:" -#: ../ProjectController.py:1195 +#: ../ProjectController.py:1257 msgid "Select an editor:" msgstr "Выберите редактор:" -#: ../controls/PouInstanceVariablesPanel.py:276 +#: ../controls/PouInstanceVariablesPanel.py:281 msgid "Select an instance" msgstr "Выберите экземпляр" @@ -3086,7 +3005,7 @@ msgid "Select an object" msgstr "Выберите объект" -#: ../ProjectController.py:427 +#: ../ProjectController.py:518 msgid "Selected directory already contains another project. Overwrite? \n" msgstr "Выбранная директория уже содержит другой проект. Перезаписать?\n" @@ -3108,15 +3027,12 @@ #: ../dialogs/DiscoveryDialog.py:85 msgid "Services available:" -msgstr "Сервисы доступны:" +msgstr "Доступные сервисы:" #: ../dialogs/LDElementDialog.py:76 msgid "Set" msgstr "Установить" -msgid "Set point" -msgstr "Уставка" - #: ../plcopen/iec_std.csv:62 msgid "Shift left" msgstr "Сдвиг влево" @@ -3125,19 +3041,19 @@ msgid "Shift right" msgstr "Сдвиг вправо" -#: ../ProjectController.py:1804 +#: ../ProjectController.py:1867 msgid "Show IEC code generated by PLCGenerator" -msgstr "Показать год, сгенерированный PLCGenerator" - -#: ../canfestival/canfestival.py:387 +msgstr "Показать код, сгенерированный PLCGenerator" + +#: ../canfestival/canfestival.py:389 msgid "Show Master" msgstr "Показать ведущего" -#: ../canfestival/canfestival.py:388 +#: ../canfestival/canfestival.py:390 msgid "Show Master generated by config_utils" msgstr "Показать ведущий узел сгенерированный config_utils" -#: ../ProjectController.py:1802 +#: ../ProjectController.py:1865 msgid "Show code" msgstr "Показать код" @@ -3163,59 +3079,62 @@ #: ../PLCGenerator.py:397 #, python-brace-format -msgid "Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'." -msgstr "Для задачи '{a1}' в ресурсе '{a2}.{a3}' отсутсвует задание источника." +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" +"Для задачи '{a1}' в ресурсе '{a2}.{a3}' отсутствует задание источника." #: ../plcopen/iec_std.csv:23 msgid "Square root (base 2)" msgstr "Квадратный корень" -#: ../plcopen/definitions.py:46 +#: ../plcopen/definitions.py:48 msgid "Standard function blocks" msgstr "Стандартные функциональные блоки" -#: ../ProjectController.py:1780 ../Beremiz_service.py:261 +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 msgid "Start PLC" msgstr "Запустить ПЛК" -#: ../ProjectController.py:984 +#: ../ProjectController.py:1046 #, python-format msgid "Start build in %s\n" msgstr "Сборка запущена в %s\n" -#: ../ProjectController.py:1298 +#: ../ProjectController.py:1360 msgid "Started" msgstr "Работа" -#: ../ProjectController.py:1586 +#: ../ProjectController.py:1648 msgid "Starting PLC\n" msgstr "ПЛК запускается\n" -#: ../Beremiz.py:463 +#: ../BeremizIDE.py:365 msgid "Status ToolBar" msgstr "Панель статуса" -#: ../editors/Viewer.py:553 ../editors/Viewer.py:2342 +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 msgid "Step" msgstr "Шаг" -#: ../ProjectController.py:1783 +#: ../ProjectController.py:1846 msgid "Stop" msgstr "Стоп" -#: ../Beremiz_service.py:262 +#: ../Beremiz_service.py:264 msgid "Stop PLC" msgstr "Остановить ПЛК" -#: ../ProjectController.py:1785 +#: ../ProjectController.py:1848 msgid "Stop Running PLC" msgstr "Остановить запущенный ПЛК" -#: ../ProjectController.py:1299 +#: ../ProjectController.py:1361 msgid "Stopped" msgstr "Стоп" -#: ../ProjectController.py:1558 +#: ../ProjectController.py:1620 msgid "Stopping debugger...\n" msgstr "Остановка отладчика...\n" @@ -3231,7 +3150,7 @@ msgid "Subtraction" msgstr "Вычитание" -#: ../ProjectController.py:1023 +#: ../ProjectController.py:1085 msgid "Successfully built.\n" msgstr "Сборка прошла успешно.\n" @@ -3239,16 +3158,7 @@ msgid "Switch perspective" msgstr "Сменить представление" -msgid "Sync_Align" -msgstr "" - -msgid "Sync_Align_Ratio" -msgstr "" - -msgid "Sync_TPDOs" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:172 +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 msgid "Syntax error in regular expression of pattern to search!" msgstr "Синтаксическая ошибка в регулярном выражении шаблона поиска!" @@ -3260,9 +3170,6 @@ msgid "Tangent" msgstr "Тангенс" -msgid "TargetType" -msgstr "Целевая платформа" - #: ../editors/ResourceEditor.py:83 msgid "Task" msgstr "Задача" @@ -3275,23 +3182,27 @@ msgid "Temp" msgstr "Временный" -msgid "The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." -msgstr "ПИД (Пропорциональный Интегральный Дифференциальный) ФБ - классический регулятор, используемый в системах с обратной связью." - -msgid "The RAMP function block is modelled on example given in the standard." -msgstr "Ограничитель скорости изменения сигнала. Функциональный блок написан согласно примеру, приведенному в стандарте." - -msgid "The RS bistable is a latch where the Reset dominates." -msgstr "RS-триггер с приоритетом выключения." - -msgid "The SR bistable is a latch where the Set dominates." -msgstr "SR-триггер с приоритетом включения." - -msgid "The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." -msgstr "Функциональный блок формирует выход XOUT пропорционально частоте изменения входа XIN." - -msgid "The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." -msgstr "Декрементный счетчик может использоваться, когда необходимо сигнализировать, что счетчик достиг нулевого значения с исходного заданного значения." +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" +"Самым правильным местом для вопросов\n" +"о Beremiz/PLCOpenEditor\n" +"является список рассылки проекта:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"Это основной канал общения сообщества.\n" +"Для написания сообщений туда необходимо быть подписанным на список рассылки.\n" +"\n" +"Вы можете подписаться на список рассылки здесь:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" #: ../editors/FileManagementPanel.py:180 #, python-format @@ -3306,49 +3217,20 @@ msgid "The group of block must be coherent!" msgstr "Группа блоков должна быть связанной!" -msgid "The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." -msgstr "Функциональный блок формирует дискретный выход с гистерезисом в зависимости от разницы двух вещественных входов XIN1 и XIN2." - -msgid "The integral function block integrates the value of input XIN over time." -msgstr "Функциональный блок интегрирует входное значение XIN во времени." - -msgid "The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." -msgstr "Таймер выключения может быть использован, чтобы внести задержку установки выхода в FALSE на фиксированный период времени после того, как вход стал FALSE." - -msgid "The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." -msgstr "Таймер выключения может быть использован, чтобы внести задержку установки выхода в TRUE на фиксированный период времени после того, как вход стал TRUE." - -msgid "The output produces a single pulse when a falling edge is detected." -msgstr "Детектор падающего фронта. На выходе формируется одиночный импульс, если обнаружен падающий фронт." - -msgid "The output produces a single pulse when a rising edge is detected." -msgstr "Детектор нарастающего фронта. На выходе формируется одиночный импульс, если обнаружен нарастающий фронт." - -msgid "The pulse timer can be used to generate output pulses of a given time duration." -msgstr "Генератор импульсов. Функциональный блок используется для генерации выходных импульсов заданной длительности." - -msgid "The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." -msgstr "Часы реального времени используется для получения меток времени, установки даты и времени дня в отчетах, сообщениях об авариях и пр." - -msgid "The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." -msgstr "Семафор предоставляет собой программный механизм синхронизации для обеспечения исключительного доступа к определенным ресурсам." - -msgid "The up-counter can be used to signal when a count has reached a maximum value." -msgstr "Инкрементный счетчик может использоваться, когда необходимо сигнализировать, что счетчик достиг максимального значения." - -msgid "The 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." -msgstr "Инкрементный/декрементный счетчик имеет два входа CU и CD. Он может использоваться для счета вверх по одному входу и для счета низ по другому." - -#: ../Beremiz.py:640 ../IDEFrame.py:1011 +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 msgid "There are changes, do you want to save?" msgstr "Хотите сохранить изменения?" -#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 -#, python-format -msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" -msgstr "Существует POU с именем \"%s\". Это может вызвать конфликт. Хотите продолжить?" - -#: ../IDEFrame.py:1098 +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" +"Существует POU с именем \"%s\". Это может вызвать конфликт. Хотите " +"продолжить?" + +#: ../IDEFrame.py:1102 msgid "" "There was a problem printing.\n" "Perhaps your current printer is not set correctly?" @@ -3398,40 +3280,49 @@ msgid "Time-of-day subtraction" msgstr "Вычитание времени суток" -#: ../editors/Viewer.py:489 +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "Переключить значение" + +#: ../editors/Viewer.py:548 msgid "Top" msgstr "Верх" -#: ../ProjectController.py:1792 +#: ../ProjectController.py:1855 msgid "Transfer" msgstr "Передать" -#: ../ProjectController.py:1794 +#: ../ProjectController.py:1857 msgid "Transfer PLC" msgstr "Передать ПЛК" -#: ../ProjectController.py:1758 +#: ../ProjectController.py:1820 msgid "Transfer completed successfully.\n" msgstr "Передача успешно завершена.\n" -#: ../ProjectController.py:1760 +#: ../ProjectController.py:1823 msgid "Transfer failed\n" msgstr "Ошибка передачи\n" -#: ../editors/Viewer.py:554 ../editors/Viewer.py:2344 ../editors/Viewer.py:2371 +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 msgid "Transition" msgstr "Переход" #: ../PLCGenerator.py:1518 #, python-format -msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" -msgstr "Тело перехода \"%s\" должно содержать выходную переменную или катушку, ссылающуюся на его имя" - -#: ../dialogs/PouTransitionDialog.py:89 +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" +"Тело перехода \"%s\" должно содержать выходную переменную или катушку, " +"ссылающуюся на его имя" + +#: ../dialogs/PouTransitionDialog.py:84 msgid "Transition Name" msgstr "Имя перехода" -#: ../dialogs/PouTransitionDialog.py:58 +#: ../dialogs/PouTransitionDialog.py:53 msgid "Transition Name:" msgstr "Имя перехода:" @@ -3442,19 +3333,21 @@ #: ../PLCGenerator.py:1598 #, python-brace-format -msgid "Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU" +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" msgstr "Переход с содержимым \"{a1}\" не подключен к предыдущему шагу в POU \"{a2}\"" -#: ../plcopen/plcopen.py:1318 +#: ../plcopen/plcopen.py:1323 #, python-format msgid "Transition with name %s doesn't exist!" msgstr "Переход с именем %s отсутствует!" -#: ../PLCControler.py:96 +#: ../PLCControler.py:98 msgid "Transitions" msgstr "Переходы" -#: ../dialogs/AboutDialog.py:123 +#: ../dialogs/AboutDialog.py:131 msgid "Translated by" msgstr "Перевод" @@ -3462,24 +3355,24 @@ msgid "Triggering" msgstr "Запуск" -#: ../Beremiz_service.py:476 +#: ../Beremiz_service.py:478 msgid "Twisted unavailable." msgstr "Модуль Twisted недоступен." -#: ../dialogs/ActionBlockDialog.py:38 ../editors/ResourceEditor.py:83 -#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 -#: ../controls/VariablePanel.py:54 +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Type" msgstr "Тип" -#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:49 msgid "Type and derivated" msgstr "Тип и его производные" #: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 #, python-format msgid "Type conflict for location \"%s\"" -msgstr "Конфликт типов \"%s\"" +msgstr "Конфликт типов для адреса \"%s\"" #: ../plcopen/iec_std.csv:16 msgid "Type conversion" @@ -3489,19 +3382,16 @@ msgid "Type infos:" msgstr "Информация о типе:" -#: ../dialogs/BrowseLocationsDialog.py:49 +#: ../dialogs/BrowseLocationsDialog.py:50 msgid "Type strict" msgstr "Только данный тип" -#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:57 -#: ../dialogs/LDPowerRailDialog.py:56 ../dialogs/BrowseLocationsDialog.py:99 -#: ../dialogs/FBDBlockDialog.py:65 ../dialogs/ConnectionDialog.py:58 +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 msgid "Type:" msgstr "Тип:" -msgid "URI_location" -msgstr "URI системы исполнения" - #: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 #, python-format msgid "Unable to define PDO mapping for node %02x" @@ -3526,20 +3416,20 @@ msgid "Undo" msgstr "Отмена" -#: ../ProjectController.py:332 +#: ../ProjectController.py:423 msgid "Unknown" msgstr "Неизвестно" -#: ../editors/Viewer.py:393 +#: ../editors/Viewer.py:394 #, python-format msgid "Unknown variable \"%s\" for this POU!" msgstr "Неизвестная переменная \"%s\" для этого POU!" -#: ../ProjectController.py:329 ../ProjectController.py:330 +#: ../ProjectController.py:420 ../ProjectController.py:421 msgid "Unnamed" msgstr "Unnamed" -#: ../PLCControler.py:636 +#: ../PLCControler.py:638 #, python-format msgid "Unnamed%d" msgstr "Unnamed%d" @@ -3549,7 +3439,7 @@ msgid "Unrecognized data size \"%s\"" msgstr "Неопределенный размер данных \"%s\"" -#: ../editors/DataTypeEditor.py:632 ../controls/VariablePanel.py:798 +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 msgid "User Data Types" msgstr "Пользовательские типы данных" @@ -3557,11 +3447,11 @@ msgid "User Type" msgstr "Пользовательский тип" -#: ../PLCControler.py:95 +#: ../PLCControler.py:97 msgid "User-defined POUs" msgstr "Пользовательские POU" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Value" msgstr "Значение" @@ -3569,28 +3459,28 @@ msgid "Values:" msgstr "Значения:" -#: ../dialogs/ActionBlockDialog.py:42 ../editors/Viewer.py:526 -#: ../editors/Viewer.py:2374 +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 msgid "Variable" msgstr "Переменная" -#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 #: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 #: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 msgid "Variable Drop" msgstr "Перетаскивание переменной" -#: ../dialogs/FBDVariableDialog.py:63 +#: ../dialogs/FBDVariableDialog.py:64 msgid "Variable Properties" msgstr "Свойства переменных" -#: ../editors/Viewer.py:288 ../editors/TextViewer.py:306 +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 #: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 #: ../controls/VariablePanel.py:350 msgid "Variable class" msgstr "Класс переменной" -#: ../editors/Viewer.py:395 ../editors/TextViewer.py:387 +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 msgid "Variable don't belong to this POU!" msgstr "Переменная не принадлежит этому POU!" @@ -3602,11 +3492,11 @@ msgid "Variables" msgstr "Переменные" -#: ../controls/ProjectPropertiesPanel.py:151 +#: ../controls/ProjectPropertiesPanel.py:152 msgid "Vertical:" msgstr "Вертикальный:" -#: ../Beremiz_service.py:586 +#: ../Beremiz_service.py:588 msgid "WAMP client startup failed. " msgstr "Ошибка импорта WAMP. " @@ -3624,38 +3514,35 @@ msgid "WAMP connection to '%s' failed.\n" msgstr "Не удалось установить WAMP подключение к %s.\n" -#: ../Beremiz_service.py:562 +#: ../Beremiz_service.py:564 msgid "WAMP import failed :" msgstr "Ошибка импорта WAMP :" -#: ../wxglade_hmi/wxglade_hmi.py:35 +#: ../wxglade_hmi/wxglade_hmi.py:37 msgid "WXGLADE GUI" msgstr "WXGLADE GUI" -#: ../dialogs/PouDialog.py:128 ../editors/LDViewer.py:891 +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 msgid "Warning" msgstr "Предупреждение" -#: ../ProjectController.py:616 +#: ../ProjectController.py:707 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "Предупреждения в ST/IL/SFC коде генераторе:\n" #: ../dialogs/SearchInProjectDialog.py:78 msgid "Whole Project" -msgstr "Целый проект" - -#: ../controls/ProjectPropertiesPanel.py:119 +msgstr "Весь проект" + +#: ../controls/ProjectPropertiesPanel.py:120 msgid "Width:" msgstr "Ширина:" -msgid "Win32" -msgstr "Win32" - -#: ../dialogs/FindInPouDialog.py:93 +#: ../dialogs/FindInPouDialog.py:91 msgid "Wrap search" msgstr "Продолжить поиск сначала" -#: ../dialogs/AboutDialog.py:122 +#: ../dialogs/AboutDialog.py:130 msgid "Written by" msgstr "Авторы" @@ -3663,13 +3550,7 @@ msgid "WxGlade GUI" msgstr "WxGlade GUI" -msgid "XenoConfig" -msgstr "Настройки Xenomai" - -msgid "Xenomai" -msgstr "Xenomai" - -#: ../svgui/svgui.py:139 +#: ../svgui/svgui.py:142 msgid "" "You don't have write permissions.\n" "Open Inkscape anyway ?" @@ -3677,7 +3558,7 @@ "У вас недостаточно прав для записи.\n" "Открыть Inkscape все равно?" -#: ../wxglade_hmi/wxglade_hmi.py:137 +#: ../wxglade_hmi/wxglade_hmi.py:154 msgid "" "You don't have write permissions.\n" "Open wxGlade anyway ?" @@ -3685,7 +3566,7 @@ "У вас недостаточно прав для записи.\n" "Открыть wxGlade все равно?" -#: ../ProjectController.py:292 +#: ../ProjectController.py:371 msgid "" "You must have permission to work on the project\n" "Work on a project copy ?" @@ -3694,8 +3575,11 @@ "Работать с копией проекта?" #: ../editors/LDViewer.py:886 -msgid "You must select the block or group of blocks around which a branch should be added!" -msgstr "Выберите блок или группу блоков, вокруг которых нужно добавить ветвление!" +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "" +"Выберите блок или группу блоков, вокруг которых нужно добавить ветвление!" #: ../editors/LDViewer.py:666 msgid "You must select the wire where a contact should be added!" @@ -3705,7 +3589,7 @@ msgid "You must type a name!" msgstr "Введите имя!" -#: ../dialogs/ForceVariableDialog.py:175 +#: ../dialogs/ForceVariableDialog.py:193 msgid "You must type a value!" msgstr "Введите значение!" @@ -3713,17 +3597,11 @@ msgid "Zoom" msgstr "Приближение" -msgid "class" -msgstr "класс" - -#: ../dialogs/DurationEditorDialog.py:151 +#: ../dialogs/DurationEditorDialog.py:155 msgid "days" msgstr "дни" -msgid "desc" -msgstr "" - -#: ../PLCOpenEditor.py:333 +#: ../PLCOpenEditor.py:343 #, python-format msgid "error: %s\n" msgstr "ошибка: %s\n" @@ -3733,70 +3611,43 @@ msgid "exited with status {a1} (pid {a2})\n" msgstr "завершился с кодом {a1} (pid {a2})\n" -#: ../PLCOpenEditor.py:396 ../PLCOpenEditor.py:398 +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 msgid "file : " msgstr "файл:" -msgid "first input parameter" -msgstr "первый входной параметр" - -msgid "first output parameter" -msgstr "первый выходной параметр" - -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "function" msgstr "функция" -#: ../PLCOpenEditor.py:399 +#: ../PLCOpenEditor.py:409 msgid "function : " msgstr "функция: " -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "functionBlock" msgstr "функциональный блок" -#: ../dialogs/DurationEditorDialog.py:151 +#: ../dialogs/DurationEditorDialog.py:155 msgid "hours" msgstr "часы" -msgid "initial" -msgstr "исходный" - -msgid "internal state: 0-reset, 1-counting, 2-set" -msgstr "состояние: 0 - сброс, 1 - счёт, 2 - установка" - -#: ../PLCOpenEditor.py:399 +#: ../PLCOpenEditor.py:409 msgid "line : " msgstr "строка:" -#: ../dialogs/DurationEditorDialog.py:153 +#: ../dialogs/DurationEditorDialog.py:157 msgid "milliseconds" msgstr "милисекунды" -#: ../dialogs/DurationEditorDialog.py:152 +#: ../dialogs/DurationEditorDialog.py:156 msgid "minutes" msgstr "минуты" -msgid "name" -msgstr "имя" - -msgid "onchange" -msgstr "" - -msgid "opts" -msgstr "" - -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "program" msgstr "программа" -msgid "second input parameter" -msgstr "второй входной параметр" - -msgid "second output parameter" -msgstr "второй выходной параметр" - -#: ../dialogs/DurationEditorDialog.py:152 +#: ../dialogs/DurationEditorDialog.py:156 msgid "seconds" msgstr "секунды" @@ -3812,25 +3663,16 @@ msgid "string right of" msgstr "строка справа от " -msgid "type" -msgstr "тип" - -#: ../Beremiz.py:126 +#: ../Beremiz.py:164 msgid "update info unavailable." msgstr "информация об обновлениях недоступна." -msgid "variable" -msgstr "переменная" - -msgid "variables" -msgstr "переменные" - -#: ../PLCOpenEditor.py:331 +#: ../PLCOpenEditor.py:341 #, python-format msgid "warning: %s\n" msgstr "предупреждение: %s\n" -#: ../PLCControler.py:970 +#: ../PLCControler.py:972 #, python-brace-format msgid "{a1} \"{a2}\" can't be pasted as a {a3}." msgstr "{a1} \"{a2}\" не может быть вставлен как {a3}." @@ -3844,220 +3686,321 @@ "{a1} XML файл не следует XSD-схеме в строке {a2}:\n" "{a3}" -#~ msgid "" -#~ "\n" -#~ "An error has occurred.\n" -#~ "\n" -#~ "Click OK to save an error report.\n" -#~ "\n" -#~ "Please be kind enough to send this file to:\n" -#~ "edouard.tisserant@gmail.com\n" -#~ "\n" -#~ "Error:\n" -#~ msgstr "" -#~ "\n" -#~ "Произошла ошибка.\n" -#~ "\n" -#~ "Нажмите OK, чтобы сохранить репорт об ошибке.\n" -#~ "\n" -#~ "Будьте так добры, пошлите этот файл:\n" -#~ "edouard.tisserant@gmail.com\n" -#~ "\n" -#~ "Ошибка:\n" - -#~ msgid " -> Nothing to do\n" -#~ msgstr " -> Ничего не нужно делать\n" - -#~ msgid " : " -#~ msgstr " : " - -#~ msgid "\"%s\" element can't be pasted here!!!" -#~ msgstr "Элемент \"%s\" не может быть вставлен сюда!!!" - -#~ msgid "... debugger recovered\n" -#~ msgstr "... отладчик восстановлен\n" - -#~ msgid "About Beremiz" -#~ msgstr "О Beremiz" - -#~ msgid "About PLCOpenEditor" -#~ msgstr "О PLCOpenEditor" - -#~ msgid "Clear the graph values" -#~ msgstr "Очистить значения на графике" - -#~ msgid "" -#~ "Derivative\n" -#~ "The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." -#~ msgstr "" -#~ "Производная\n" -#~ "Функциональный блок формирует выход XOUT пропорционально частоте изменения входа XIN." - -#~ msgid "Description" -#~ msgstr "Описание" - -#~ msgid "" -#~ "Down-counter\n" -#~ "The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." -#~ msgstr "" -#~ "Декрементный счетчик\n" -#~ "Декрементный счетчик может использоваться, когда необходимо сигнализировать, что счетчик достиг нулевого значения с исходного заданного значения." - -#~ msgid "Enable_Native_Library" -#~ msgstr "Разрешить библиотеку Native" - -#~ msgid "" -#~ "Falling edge detector\n" -#~ "The output produces a single pulse when a falling edge is detected." -#~ msgstr "" -#~ "Детектор падающего фронта\n" -#~ "На выходе формируется одиночный импульс, если обнаружен падающий фронт." - -#~ msgid "Form isn't complete. Pattern to search must be filled!" -#~ msgstr "Форма заполнена неполностью. Шаблон поиска должен быть заполнен!" - -#~ msgid "" -#~ "Hysteresis\n" -#~ "The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." -#~ msgstr "" -#~ "Гистерезис\n" -#~ "Функциональный блок формирует дискретный выход с гистерезисом в зависимости от разницы двух вещественных входов XIN1 и XIN2." - -#~ msgid "IEC-61131-3 code generation failed !\n" -#~ msgstr "Неудачная генерация МЭК-61131-3 кода!\n" - -#~ msgid "In section: " -#~ msgstr "В секции: " - -#~ msgid "Initial" -#~ msgstr "Исходное значение" - -#~ msgid "" -#~ "Integral\n" -#~ "The integral function block integrates the value of input XIN over time." -#~ msgstr "" -#~ "Интеграл\n" -#~ "Функциональный блок интегрирует входное значение XIN во времени." - -#~ msgid "Move debug variable down" -#~ msgstr "Переместить отлаживаемую переменную ниже" - -#~ msgid "Move debug variable up" -#~ msgstr "Переместить отлаживаемую переменную выше" - -#~ msgid "No PLC project found" -#~ msgstr "Не найден проект ПЛК" - -#~ msgid "No output variable found" -#~ msgstr "Выходная переменная не найдена" - -#~ msgid "" -#~ "Off-delay timer\n" -#~ "The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." -#~ msgstr "" -#~ "Таймер выключения\n" -#~ "Таймер выключения может быть использован, чтобы внести задержку установки выхода в FALSE на фиксированный период времени после того, как вход стал FALSE." - -#~ msgid "" -#~ "On-delay timer\n" -#~ "The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." -#~ msgstr "" -#~ "Таймер включения\n" -#~ "Таймер выключения может быть использован, чтобы внести задержку установки выхода в TRUE на фиксированный период времени после того, как вход стал TRUE." - -#~ msgid "" -#~ "PID\n" -#~ "The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." -#~ msgstr "" -#~ "ПИД\n" -#~ "ПИД (Пропорциональный Интегральный Дифференциальный) ФБ - классический регулятор, используемый в системах с обратной связью." - -#~ msgid "Position:" -#~ msgstr "Позиция:" - -#~ msgid "" -#~ "Pulse timer\n" -#~ "The pulse timer can be used to generate output pulses of a given time duration." -#~ msgstr "" -#~ "Генератор импульсов\n" -#~ "Функциональный блок используется для генерации выходных импульсов заданной длительности." - -#~ msgid "" -#~ "RS bistable\n" -#~ "The RS bistable is a latch where the Reset dominates." -#~ msgstr "" -#~ "RS триггер\n" -#~ "RS триггер - переключатель с доминантой выключения." - -#~ msgid "" -#~ "Ramp\n" -#~ "The RAMP function block is modelled on example given in the standard." -#~ msgstr "" -#~ "Ограничитель скорости изменения сигнала\n" -#~ "Функциональный блок написан согласно примеру, приведенному в стандарте." - -#~ msgid "" -#~ "Real time clock\n" -#~ "The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." -#~ msgstr "" -#~ "RTC часы\n" -#~ "Часы реального времени используется для получения меток времени, установки даты и времени дня в отчетах, сообщениях об авариях и пр." - -#~ msgid "Remove debug variable" -#~ msgstr "Удалить отлаживаемую переменную" - -#~ msgid "Reset zoom and offset" -#~ msgstr "Сбросить приближение и смещение" - -#~ msgid "" -#~ "Rising edge detector\n" -#~ "The output produces a single pulse when a rising edge is detected." -#~ msgstr "" -#~ "Детектор нарастающего фронта\n" -#~ "На выходе формируется одиночный импульс, если обнаружен нарастающий фронт." - -#~ msgid "" -#~ "SR bistable\n" -#~ "The SR bistable is a latch where the Set dominates." -#~ msgstr "" -#~ "SR триггер\n" -#~ "SR триггер - переключатель с доминантой включения." - -#~ msgid "" -#~ "Semaphore\n" -#~ "The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." -#~ msgstr "" -#~ "Семафор\n" -#~ "Семафор предоставляет собой программный механизм синхронизации для обеспечения исключительного доступа к определенным ресурсам." - -#~ msgid "Tick" -#~ msgstr "Тик" - -#~ msgid "" -#~ "Up-counter\n" -#~ "The up-counter can be used to signal when a count has reached a maximum value." -#~ msgstr "" -#~ "Инкрементный счетчик\n" -#~ "Инкрементный счетчик может использоваться, когда необходимо сигнализировать, что счетчик достиг максимального значения." - -#~ msgid "" -#~ "Up-down counter\n" -#~ "The 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." -#~ msgstr "" -#~ "Инкрементный/декрементный счетчик\n" -#~ "Инкрементный/декрементный счетчик имеет два входа CU и CD. Он может использоваться для счета вверх по одному входу и для счета низ по другому." - -#~ msgid "Values" -#~ msgstr "Значения" - -#~ msgid "Waiting debugger to recover...\n" -#~ msgstr "Ожидание восстановления отладчика...\n" - -#~ msgid "Zoom:" -#~ msgstr "Приближение:" - -#~ msgid "about.html" -#~ msgstr "about.ru.html" - -#~ msgid "plcopen_about.html" -#~ msgstr "plcopen_about.ru.html" +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "Подчинённое устройство CanOpen" + +msgid "CAN_Device" +msgstr "CAN устройство" + +msgid "CAN_Baudrate" +msgstr "Скорость CAN" + +msgid "NodeId" +msgstr "ID узла" + +msgid "Sync_Align" +msgstr "Sync_Aligh" + +msgid "Sync_Align_Ratio" +msgstr "Sync_Align_Ratio" + +msgid "CanFestivalNode" +msgstr "Узел CanFestival" + +msgid "Sync_TPDOs" +msgstr "Sync_TPDOs" + +msgid "CanFestivalInstance" +msgstr "Экземпляр CanFestival" + +msgid "CAN_Driver" +msgstr "CAN драйвер" + +msgid "Generic" +msgstr "Generic" + +msgid "Command" +msgstr "Команда" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "Настройки Xenomai" + +msgid "Compiler" +msgstr "Компилятор" + +msgid "CFLAGS" +msgstr "CFLAGS" + +msgid "Linker" +msgstr "Компоновщик" + +msgid "LDFLAGS" +msgstr "LDFLAGS" + +msgid "Linux" +msgstr "GNU/Linux" + +msgid "Win32" +msgstr "Win32" + +msgid "BaseParams" +msgstr "Базовые параметры" + +msgid "IEC_Channel" +msgstr "МЭК-канал" + +msgid "Enabled" +msgstr "Разрешено" + +msgid "BeremizRoot" +msgstr "Настройки Beremiz " + +msgid "TargetType" +msgstr "Целевая платформа" + +msgid "Libraries" +msgstr "Библиотеки" + +msgid "URI_location" +msgstr "URI системы исполнения" + +msgid "Disable_Extensions" +msgstr "Запретить расширения" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)" + +msgid "variables" +msgstr "переменные" + +msgid "variable" +msgstr "переменная" + +msgid "name" +msgstr "имя" + +msgid "type" +msgstr "тип" + +msgid "class" +msgstr "класс" + +msgid "initial" +msgstr "исходный" + +msgid "desc" +msgstr "описание" + +msgid "onchange" +msgstr "по изменению" + +msgid "opts" +msgstr "опции" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "0 - текущее время, 1 - отклонение от PDT" + +msgid "Preset datetime" +msgstr "Основное время" + +msgid "Copy of IN" +msgstr "Копия входа IN" + +msgid "Datetime, current or relative to PDT" +msgstr "Текущие дата и время, абсолютные или относительные от PDT" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" +"Часы реального времени используется для получения меток времени, установки " +"даты и времени дня в отчетах, сообщениях об авариях и пр." + +msgid "1 = integrate, 0 = hold" +msgstr "1 = интегрировать, 0 = остановка" + +msgid "Overriding reset" +msgstr "Сброс интегратора" + +msgid "Input variable" +msgstr "Входная переменная" + +msgid "Initial value" +msgstr "Исходное значение" + +msgid "Sampling period" +msgstr "Период сэмплирования" + +msgid "NOT R1" +msgstr "НЕ R1" + +msgid "Integrated output" +msgstr "Интегрированный выход" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "Функциональный блок интегрирует входное значение XIN во времени." + +msgid "0 = reset" +msgstr "0 = сброс" + +msgid "Input to be differentiated" +msgstr "Вход для дифференцирования" + +msgid "Differentiated output" +msgstr "Дифференцированный выход" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" +"Функциональный блок формирует выход XOUT пропорционально частоте изменения " +"входа XIN." + +msgid "0 - manual , 1 - automatic" +msgstr "0 - ручной, 1 - автоматический" + +msgid "Process variable" +msgstr "Текущее значение регулируемой переменной" + +msgid "Set point" +msgstr "Уставка" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "Ручной выход" + +msgid "Proportionality constant" +msgstr "Коэффициент пропорциональности" + +msgid "Reset time" +msgstr "Постоянная времени интегрирования" + +msgid "Derivative time constant" +msgstr "Постоянная времени дифференцирования" + +msgid "PV - SP" +msgstr "Ошибка, PV - SP" + +msgid "FB for integral term" +msgstr "ФД интегрирования" + +msgid "FB for derivative term" +msgstr "ФБ дифференцирования" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" +"ПИД (Пропорциональный Интегральный Дифференциальный) ФБ - классический " +"регулятор, используемый в системах с обратной связью." + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "0 - вход X0, 1 - нарастание до значения X1" + +msgid "Ramp duration" +msgstr "Длительность нарастания" + +msgid "BUSY = 1 during ramping period" +msgstr "BUSY = 1 во время " + +msgid "Elapsed time of ramp" +msgstr "Прошедшее время нарастания" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" +"Ограничитель скорости изменения сигнала. Функциональный блок написан " +"согласно примеру, приведенному в стандарте." + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" +"Функциональный блок формирует дискретный выход с гистерезисом в зависимости " +"от разницы двух вещественных входов XIN1 и XIN2." + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "SR-триггер с приоритетом включения." + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "RS-триггер с приоритетом выключения." + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" +"Семафор предоставляет собой программный механизм синхронизации для " +"обеспечения исключительного доступа к определенным ресурсам." + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "" +"Детектор нарастающего фронта. На выходе формируется одиночный импульс, если " +"обнаружен нарастающий фронт." + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "" +"Детектор падающего фронта. На выходе формируется одиночный импульс, если " +"обнаружен падающий фронт." + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" +"Инкрементный счетчик может использоваться, когда необходимо сигнализировать," +" что счетчик достиг максимального значения." + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" +"Декрементный счетчик может использоваться, когда необходимо сигнализировать," +" что счетчик достиг нулевого значения с исходного заданного значения." + +msgid "" +"The 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." +msgstr "" +"Инкрементный/декрементный счетчик имеет два входа CU и CD. Он может " +"использоваться для счета вверх по одному входу и для счета низ по другому." + +msgid "first input parameter" +msgstr "первый входной параметр" + +msgid "second input parameter" +msgstr "второй входной параметр" + +msgid "first output parameter" +msgstr "первый выходной параметр" + +msgid "second output parameter" +msgstr "второй выходной параметр" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "состояние: 0 - сброс, 1 - счёт, 2 - установка" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" +"Генератор импульсов. Функциональный блок используется для генерации выходных" +" импульсов заданной длительности." + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" +"Таймер включения может быть использован, чтобы внести задержку установки " +"выхода в TRUE на фиксированный период времени после того, как вход стал " +"TRUE." + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" +"Таймер выключения может быть использован, чтобы внести задержку установки " +"выхода в FALSE на фиксированный период времени после того, как вход стал " +"FALSE." diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_sl_SI.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Beremiz_sl_SI.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,4004 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Janez Pregelj , 2017 +# Smarteh Smarteh , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Smarteh Smarteh , 2017\n" +"Language-Team: Slovenian (Slovenia) (https://www.transifex.com/beremiz/teams/75746/sl_SI/)\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: sl_SI\n" +"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" +"\n" +"Nepredvidena izjema se je zgodila v programu( bug). Poročilo je shranjeno na:\n" +"(%s)\n" +"\n" +"Prosim pošljite to datoteko na naslov :\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"Potrebno je na novo zagnati program.\n" +"\n" +"Izvor:\n" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr "Zunanji" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr "Vhod-Izhod" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr "Vhod" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr "Lokalni" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr "Izhod" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr "Začasni" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr " in %s" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr "ustvarjanje prekinjeno !\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "Podatkovni tip \"%s\" ne obstaja !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "POU \"%s\" že obstaja !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "POU \"%s\" ne obstaja!!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" ne morem uporabiti sebe!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "\"%s\" konfiguracija že obstaja !" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "\"%s\" konfiguracija že obstaja !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "Podatkovni tip \"%s\" že obstaja!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "Element \"%s\" tega POU že obstaja!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "Mapa \"%s\" ni veljaven Beremiz projekt\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "Beseda \"%s\" je rezervirana beseda in se ne more uporabit!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "\"%s\" je neveljavna vrednost!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "\"%s\" ni veljavna mapa!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "\"%s\" ni veljavena označba!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "\"%s\" je uporabljen v ene ali več POU. Želiš nadaljevati?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "POU \"%s\" že obstaja!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "Korak \"%s\" že obstaja!" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "Vrednost \"%s\" je že definirana!" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "\"%s\" vrednost ni veljavna dimenzija tabele!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"\"%s\" vrednost ni veljavna dimenzija tabele!\n" +"Desna vrednost mora biti večja od leve vrednosti." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "Funkcija \"{a1}\" je bila preklicana v POU \"{a2}\": Nobeden vhod ni povezan" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" je že uporabljen v \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "Vir \"{a1}\" že obstaja v konfiguraciji \"{a2}\" !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "Vir \"{a1}\" ne obstaja v konfiguraciji \"{a2}\" !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dd" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "Podatkovni tipi %s " + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%s POU-ji" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "%s profil" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "Telo %s nima Primerkov!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "%s telo nima besedila!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "&Dodaj Element" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Zapri" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "%Konfiguracija" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "&Podatkovni tip" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "&Pobriši" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "&Prikaži" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "&Uredi" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "&Datoteka" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "&Funkcija" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "&Pomoč" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&Licenca" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "&Program" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "&Lastnosti" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "&Nedavni Projekti" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "&Vir" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} ujema v Projektu" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} ujema v Projektu" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' je najden v {a2}\n" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "(%d ujemanj)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr "," + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr "." + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1d" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" +msgstr "" +"POU že ima element poimenovan \"%s\". To lahko povzroči težave. Ali želiš " +"nadaljevati?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "POU z imenom \"%s\" že obstaja!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "Otrok z imenom \"{a1}\" že obstaja -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "Lokacija mora biti izbrana!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "Opravilo z enakim imenom že obstaja!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "Spremenljivka z imenom \"%s\" že obstaja v tem POU!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "Spremenljivka z imenom \"%s\" že obstaja!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "O programu" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "Absolutna številka" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "Akcija" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "Akcijski blok" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "Ime Akcije" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "Ime Akcije:" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "Akcija z imenom %s ne obstaja!" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "Akcije" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "Akcije:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Aktiven" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "Dodaj" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "Dodaj Akcijo" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "Dodaj C programsko kodo z sočasnim dostopom do lokalnih spremenljivk" + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "Dodaj Konfiguracijo" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "Dodaj Podatkovni tip" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "Dodaj Razhajališče" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "Dodaj IP" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "Dodaj POU" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "Dodaj Python programsko koda, ki se izvaja asinhrono" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "Dodaj Vir" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "Dodaj Prehod" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "Dodaj odsek žice" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "Dodaj začetni skok" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "Dodaj nov skok" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "Dodaj nov korak" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "Dodaj enostaven WxGlade GUI." + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "Dodaj Akcijo" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "Dodaj ELEMENT" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "Dodaj Primerek" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "Dodaj podrejeno napravo" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "Dodaj Opravilo" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "Dodaj spremenljivko!" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "Dodajanje" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "Dodatni funkcijski bloki" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "Nastavi velikost bloka" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "Poravnava" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "Vse" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "Vse datoteke (*.*)|*.*|CSV datoteke (*.csv)|*.csv" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "Že povezan. Prosim najprej prekini\n" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "Element z imenom \"%s\" že obstaja v tej strukturi!" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "Primerek z enakim imenom že obstaja" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "Uveljavi spremembo imena za vse nadaljnje objekte z enakim imenom" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "Arkus kosinus" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "Arkus sinus" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "Arkus tangens" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "Aritmetičen" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "Tabela" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "Prireditev" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "Vsaj ena spremenljivka ali logični izraz mora biti izbran!" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "Avtor" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "Avtorjevo ime (ni obvezno):" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "Nazaj" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "Napačno domensko ime (krožno) na " + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "Napačno domensko ime na " + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "Nepravilna velikost lokacije : %s" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "Osnovni Tip:" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "Osnovni Tipi" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "Beremiz" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "Bitna izbira (1 od 2)" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "Bitno premikanje" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "Bitna operacija" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "Bitna operacija IN" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "Bitna operacija ALI" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "Bitni XOR" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "Bitni invertiranje" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "Blok" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "Lastnosti bloka" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Ime bloka" + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "Spodaj" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "Prekinjen" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "Razišči %s vrednosti v knjižnici" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "Razišči lokacije" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "Gradnja" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "Projektna mapa - gradnje je že bila očiščena\n" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "Gradnja projekta v mapo projekta - gradnje" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "Gradnja C programske kode ni uspela !\n" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "Gradnja C programske kode neuspešna.\n" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "C programska koda" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "C programska koda uspešno ustvarjena.\n" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "Generiranje C programske kode ni uspelo.\n" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "Generiranje C programske kode %s ni uspelo.\n" + +#: ../features.py:32 +msgid "C extension" +msgstr "C razširitev" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "Zasluge C&" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "CANOpen mreža" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "CANOpen podrejena naprava" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "CANopen podpora" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "Lahko ustvarim vrstni red izvajanja le za FBD stavke!" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "Lokacijo lahko določiš le lokalni ali globalni spremenljivki." + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "Ne morem ustvariti programa v datoteko %s!" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "Ne morem dodeliti lokacije Primerku funkcijskega bloka" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "Ne morem shraniti projekt v datoteko %s!" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "Ne morem dodeliti začetne vrednosti Primerku funkcijskega bloka" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "Ne morem ustvariti otroka {a1} tipa {a2} " + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "Ne morem najti nižjega IEC kanala kot je %d\n" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "Ne morem pridobiti status krmilnika - povezava prekinjena.\n" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "Ne morem odpreti/razvozlati VARIABLES.csv!\n" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Ne morem nastaviti bit offset za nebitno '{a1}' spremenljivko " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "Razlikovanje velikih in malih črk" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "Sredinsko" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "Spremeni IP vmesnika za povezavo" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "Spremeni ime" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "Spremeni tip POU v" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "Spremeni številko vrat" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "Spremeni delovno mapo" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "Niz znakov" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "Izberi SVG datoteko" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "Izberi mapo za shranjevanje projekta" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "Izberi datoteko" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "Izberi projekt" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "Izberi vrednost za %s:" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "Izberi delovno mapo" + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "Izbrana mapa ne vsebuje programa. Ni veljaven projekt!" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "Mapa ni prazna. Ne morem uporabiti za nov projekt!" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "Razred" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "Filter Razreda:" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "Razred:" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "Počisti" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Počisti dnevniška sporočila" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "Počisti projektno mapo - gradnje" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "Čistim projektno mapo - gradnje\n" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "Počisti napake" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "Počisti vrstni red izvajanja" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Zapri" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "Zapri program" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "Zapri projekt" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "Zapri zavihek" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "Tuljava" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "Komentar" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Podpora skupnosti" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Ime podjetja" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "Ime podjetja (obvezno)" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "Spletn stran podjetja (ni obvezno):" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "Primerjava" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "Prevajam IEC program v C programsko kodo...\n" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "Združevanje" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "Konfiguracija" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "Konfiguracijske spremenljivke" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "Konfiguracija" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "Konfiguracije" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Potrdi ali spremeni ime spremenljivke" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "Poveži" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "Poveži na krmilnik" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "Povezan na URI: %s" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "Povezava" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "Lastnosti povezave" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "Povezava prekinjena!\n" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "Povezava do %s prekinjena!\n" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Povezava prekinjena!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "Povezava na %s' ni bila uspešna.\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "Konektor-vhod" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "Konektor-vhodi:" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "Konzola" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "Konstanta" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "Kontakt" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "Opis vsebine (ni obvezno):" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "Nadaljevanje" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "Pretvorba iz BCD" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "Pretvorba v BCD" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "Pretvorba v datum" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "Pretvorba v čas-dneva" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "Kopiraj" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "Kopiraj POU" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "Kopiraj datoteko iz leve mape v desno" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "Kopiraj datoteko iz desne mape v levo" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "Kosinus" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" +"Ne morem dodati otroka \"{a1}\", tip {a2} :\n" +"{a3}\n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Ne morem uvoziti staro datoteko %s ." + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" +"Ne morem naložiti confnode osnovne parametre {a1} :\n" +" {a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" +"Ne moreme naložiti confnode parametre {a1} :\n" +" {a2}" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "Prilepim lahko samo objekte tipa POU." + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "Ne morem zagnati krmilnik !\n" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "Ne morem ustaviti krmilnika !\n" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "Ne morem ustaviti razhroščevalnik.\n" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "Kreiraj HMI" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "Ustvari nov POU" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "Ustvari novo Akcijo" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "Ustvari nov Akcijski blok" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "Ustvari nov blok" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "Ustvari nov skok" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "Ustvari novo tuljavo" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "Ustvari nov komentar" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "Ustvari novo povezavo" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "Ustvari nov kontakt" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "Ustvari novo Razhajališče" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "Ustvari novo Razhajališče ali Stekališče" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "Ustvari nov začetni korak" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "Ustvari nov skok" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "Ustvari nov napajalni vodnik" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "Ustvari novo prečko" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "Ustvari nov Korak" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "Ustvari nov prehod" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "Ustvari novo spremenljivko" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Zasluge" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Trenutna delovna mapa :" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "Izreži" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "Cikličen" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "OPUŠČENO" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "DS-301 Profile" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "DS-302 Profile" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "Podatkovni tip" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "Podatkovni tipi" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "Pretvorba podatkovnega tipa" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "Dodajanje datuma" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "Odštevanje Datum in ure" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "Odštevanje Datuma" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "Dnevi:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "" +"Razhroščevalnik in krmilnik nimata enakega programa - Izvedi " +"Ustavi/Prenos/Zaženi za ponovno omogočanje razhroščevalnika\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "Razhroščevalnik Primerka" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "Razhroščevalnik: %s" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "Razhroščevalnik: Neznana spremenljivka '%s'\n" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "Razhroščevalnik: Nepodprti podatkovni tip '%s'\n" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "Razhroščevalnik" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "Razhroščevalnik onemogočen\n" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "Razhroščevalnik pripravljen\n" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "Razhroščevalnik ustavljen..\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "Izbriši" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "Izbriši Razhajališče" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "Izbriši datoteko" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "Pobriši odsek žice" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "Izbriši element" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "Izbriši (znotraj)" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "Izpeljani podatkovni tip:" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Opis" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "Opis:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "Dimenzije:" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "Smer" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "Smer:" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "Direkten" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "Prekini" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "Prekini povezavo do krmilnika" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "Prekinjen" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "Razhajališče" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "Deljenje" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "Ali res želiš izbrisati datoteko '%s'?" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "Dokumentacija" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "Končano" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "Čas trajanja" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "EDS datoteke (*.eds)|*.eds|Vse datoteke|*.*" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "Uredi Blok" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "Uredi vrednost Tuljave" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "Uredi vrednost Kontakta" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "Uredi čas trajanja" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "Uredi Korak" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "Uredi WxWidgets GUI z WXGlade" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "Uredi lastnosti Akcijskega bloka" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "Uredi lastnosti tipa tabele" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "Uredi komentar" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "Uredi datoteko" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "Uredi element" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "Nastavi Skok na kateri Korak" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "Urejanje čiste IEC programske kode je bilo dodano PLCGenerator-ju" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "Uredi ime koroka" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "Uredi ime prehoda" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "Urejevalnik orodne vrrstice" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "Izbira urejevalnika" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "Elementi :" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "Prazen" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "Omogoči Razveljavi/Ponovi" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "Vstavi ime" + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "Vnesi števiko vrat" + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "Vnesi IP vmesnika za povezavo" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "Naštevni" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "Enak" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "Napaka" + +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" +msgstr "" +"Napaka: V krmilniku mora biti definirana vsaj ena Konfiguracija in Vir!\n" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "Napaka: Prevajalnik IEC v C programsko kodo je vrnil napako %d\n" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" +"Napaka v ST/IL/SFC ustvarjalniku programske kode :\n" +"%s\n" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "Napaka pri shranjevanju \"%s\"\n" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "Napaka: Izvoz podrejene naprave ni uspešen\n" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "Napaka: Ni bil ustvarjena nadrejena naprava\n" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "Napaka: Program za krmilnik ni bil generiran\n" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "Izjemna napaka pri povezovanju na %s!\n" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "Nadzor izvajanja:" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "Vrstni red izvajanja:" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "Preizkusni spletni HMI" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "Eksponent" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "Potenciranje" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "Izvoz CanOpen podrejene naprave v EDS datoteko" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "Izvozi vrednosti grafa v Odlagališče" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "Izvozi podrejeno napravo" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "Logičen izraz:" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "Zunanji" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "Povzemam locirane spremenjljivke...\n" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "FBD" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "Nauspešno: Potrebno zfraditi programsko kodo pred prenosom.\n" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "Negativna fronta" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "Usodna napaka: ne morem najti programa za izgradnjo.\n" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "Prejemam %s" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "Polje %s nima veljavne vrednosti!" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "Polja %s nimajo veljavne vrednosti!" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "Datoteka '%s' že obstaja!" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "Išči" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "Išči naslednjega" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "Išči prejšnjega" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "Išči pozicijo" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "Išči:" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "Ponovno naloži izvajanje kode\n" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "Vsili vrednost" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "Vsili vrednost spremenljivke" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "Obrazec ni dokončan! . %s mora biti vpisano!" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "Obrazec ni dokončan! Ime mora biti vpisano!" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "Obrazec ni dokončan! Izbran mora biti veljaven Tip bloka!" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "Naprej" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "Funkcija" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "Funkcijski &Blok" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "Funkcijski blok" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "Tipi funkcijskih blokov" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "Funkcijski bloki" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "Funkcijski blok ne more biti uporabljen v funkcijah" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "Funkcijski blok \"%s\" ne more biti prilepljen v Funkcijo!!!" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "Funkcije" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "Ustvari Program" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "Generiram SoftPLC IEC-61131 ST/IL/SFC programsko kodo...\n" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "Globalen" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "Pojdi na trenutno vrednost" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "Grafika" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "Večji kot" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "Večji kot ali enak" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "Resolucija mreže:" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "HTTP vmesnik - številka vrat :" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "Višina:" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "Domači direktorij:" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "Horizontalno:" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "Ure:" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "IL" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "IP" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "IP naslov ni veljaven!" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "Uvoz SVG" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "Vhod-Izhod" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "Neaktiven" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "Podatkovna tipa \"{a1}\" in \"{a2}\" sta med seboj nezdružljiva" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "Neprimerljiva velikost podatkov med \"%s\" and \"BOOL\"" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "Neprimerljiva velikost podatkov med \"{a1}\" in \"{a2}\"" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "Indikator" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "Začetni" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "Začetni korak" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "Začetna vrednost" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "Začetna vrednost:" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "Inkscape" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "Vstavljen" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "Vhod" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "Vhodi:" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "Vstavljanje ( v ) " + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "Primerek z oznako %d ne obstaja!" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "Primerki:" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "Vmesnik" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "Prekinitev" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "Interval" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "Neveljavni plcopen element(-i)!!!" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "Neveljavni Tip \"{a1}\"-> {a2} != {a3} za lokacijo \"{a4}\"" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "Neveljavni Tip \"{a1}\"-> {a2} != {a3} za lokacijo \"{a4}\"" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "Neveljavna vrednost \"%s\" za razhroščevalno spremenljivko" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "Neveljavna vrednost \"%s\" za element liste spremenljivk" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "Neveljavna vrednost \"%s\" za pogled bloka" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "Neveljavna vrednost \"{a1}\" za spremenljivko \"{a2}\" !" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" +"Neveljavna vrednost!\n" +"Vnesti moraš numerično vrednost." + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "Skok" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "LD" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "Lestvični element z oznako %d je uporabljen več kot v eni prečki." + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "Jezik" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "Jezik (ni obvezno):" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "Jezik:" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "" +"Zadnji gradnja projekta že ustreza izbranemu krmilniku. Izvajam ponovno " +"prenašanje...\n" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "Odpri WX GUI nadzor izvajanja" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "Odpri Python lupino" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "Levo" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "Levi napajalni vodnik" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "Dolžina niza" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "Manj kot" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "Manj kot ali enak" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "Knjižnica" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "Licenca" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "Omejitve" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "Povezovanje :\n" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "Lokalni" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "Lokalni vpisi" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "Pregled lokalnih storitev ni uspelo!\n" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "Lokacija" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "Razpoložljive lokacije:" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "Logaritem osnova 10" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "Napaka MDNS razpoznave za '%s'\n" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "Preslikava spremenljivke" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "Preslikaj najdeno spremenljivko na CANopen" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "Nadrejena naprava" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "Maksimalno število ({a1}) doseženo za confnode tipa {a2} " + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "Maksimum" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "Maksimum" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "Spomin" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "Meni orodna vrstica" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "Mikrosekunde:" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "Sredina" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "Milisekunde:" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "Minimum" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "Minimum:" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "Minute:" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "Razno" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "Modifikator:" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "" +"Najden več kot en Konektor-vhod, ki ustreza \"{a1}\" Konektor-izhodu v " +"\"{a2}\" POU" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "Premakni Akcijo navzdol" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "Premakni Akcijo navzgor" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "Premakni navzdol" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "Premakni element navzdol" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "Premakni element navzgor" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "Premakni Primerek navzdol" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "Premakni Primerek navzgor" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "Premakni Opravilo navzdol" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "Premakni Opravilo navzgor" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "Premakni pogled" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "Premakni navzgor" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "Premakni spremenljivko navzdol" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "Premakni spremenljivko navzgor" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "Multiplekser (izbira 1 od N)" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "Množenje" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "Moj računalnik:" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NAME" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "Ime" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "Ime ne sme biti prazno!" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "Ime:" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "Naravni logaritem" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "Negiran" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "Storitev Nevow Web ni uspešna. " + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "Uvoz Nevow/Athena ni uspešen :" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "Nov" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "Nov element" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "Ni modifikator" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "Ni programa za prenos na krmilnik (je bila gradnja projekta uspešna?)\n" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "Ni definirana vsebina POU \"%s\"" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "" +"Ni bil najden Konektor-vhod, ki ustreza \"{a1}\" Konektor-izhodu \"{a2}\" " +"POU" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" +"Dokumentacija ni na voljo.\n" +"Pride kmalu." + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "Ni informacij za \"%s\" blok" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "" +"Nisem našel izhodne spremenljivke {a1} v bloku {a2} znotraj POU {a3}. " +"Povezava mora biti prekinjena" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "Ni rezultata iskanja." + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "Ni take SVG datoteke: %s\n" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "Ni take kombinacije index/subindex ({a1},{a2}) (spremenljivka {a3})" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "" +"Ni takege kombinacije index/subindex ({a1},{a2}) in ID : {a3} (spremenljivka" +" {a4})" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "Izbrana napačna vrednost!" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "Ni definiranih spremenljivk v POU \"%s\"" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "Ne obstaja element z oznako : {a1} (spremenljivka {a2})" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "Ne-obdrži v spominu" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "Normal" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "" +"Ni PDO preslikana spremnenljivka : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "Ni enako " + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "Število sekvenc:" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "Numeričen" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "Na spremembo" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "Samo Elementi" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "Odpri" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "Odpri Inkscape" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "" +"Odprtokodno ogrodje za avtomatizacijo, povzeto po standardu IEC 61131 z " +"nepretrgano rastjo razširitev na prilagodljivo izvajalno kodo za krmilnike." + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "Odpri Urejevalnik datotek za upravljanje z projektnimi datotekami" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "Odpri wxGlade" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "Nastavitev" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "Nastavitve" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "Organizacija (ni obvezno):" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "Drugi Profil" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "Izhod" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "PDO Sprejem" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "PDO oddaja" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "Krmilnik :\n" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "Dnevnik krmilnika" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "Ustvarjanje kode krmilnika neuspešno !\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "Krmilnik je prazen ali že deluje." + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "Krmilnik ni zagnan." + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" +"Krmilniška sintaktična napaka v vrstici {a1}:\n" +"{a2}" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "PLCOpen datoteke (*.xml)|*.xml|Vse datoteke|*.*" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "PLCOpenEditor" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" +"PLCOpenEditor je del Beremiz projekta.\n" +"\n" +"Beremiz je " + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "VRATA" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "Ime POU" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "Ime POU:" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "Tip POU" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "Tip POU:" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "PYRO povezuje na URI : %s\n" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "PYRO uporablja certifikate v '%s' \n" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "Nastavitev strani" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "Velikost strani (ni obvezno):" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "Stran: %d" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "Starš Primerka" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "Prilepi" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "Prilepi POU" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "Vzorec za iskanje:" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "Številka prečke:" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "Prosim izberi ciljni krmilnik" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "Prosim vnesi ime bloka" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "Prosim vnesi komentar" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "Prosim vnesi ime koraka" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "Prosim vnesi besedilo" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "Prosim vnesi vrednost za spremenljivko \"%s\" :" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "Številka vrat mora biti med 0 <= številka vrat <= 65535!" + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "Številka vrat mora biti celoštevilčna!" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "Napajalni vodnik" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "Lastnosti napajalnega vodnika" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "Predogled" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "Predogled:" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "Natisni" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "Predogled tiskanja" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "Prioriteta" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "Prioriteta:" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "Napaka pri zagonu krmilnika: %d" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "Ime Produkta" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "Ime Produkta (obvezno)" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "Izdaja Produkta (ni obvezno):" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "Verzija Produkta" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "Verzija produkta (obvezno)" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "Program" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "Program je bil uspešno ustvarjen!" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "Programi" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "Programi ne morejo biti uporabljeni v drugih POU!" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "Projekt" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "Projekt '%s':" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "Projektne datoteke" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "Ime projekta" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "Ime projekta (obvezno):" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "Verzija projekta (ni obvezno):" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" +"Sintaktična napaka v imenu Projektne datoteke:\n" +"\n" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "Lastnosti Projekta" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "Drevesna struktura Projekta ne ustreza confnode.xml {a1}!={a2} " + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "Razmnoži ime" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "Lastnosti" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "Objava storitve na lokalnem omrežju" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "Pyro izjema: %s\n" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "Pyro objekten uri :" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "Pyro številka vrat :" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "Python programska koda" + +#: ../features.py:33 +msgid "Python file" +msgstr "Python datoteka" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "Kvalifikator" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "Izhod" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "Obseg:" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "IEC programska koda" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "Ali želiš izbrisati element '%s'?" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "Ponovi" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "Reference" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "Osveži" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "Regularni izraz" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "Regularni izrazi" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "Prenehaj vsiljevati vrednost" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "Ostanek (modulo)" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "Odstrani element %s" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "Odstrani Podatkovni tip" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "Odstrani POU" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "Odstrani Akcijo" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "Odstrani element" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "Odstrani datoteko iz leve mape" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "Odstrani Primerek" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "Odstrani podrejeno napravo" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "Odstrani Opravilo" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "Odstrani spremenljivko" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "Preimenuj" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "Zamenjaj datoteko" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "Zamenjaj žico z Konektorjema." + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "Zamenjaj (znotraj)" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "Nastavi na 0" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "Ponastavi vrstni red izvajanja" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "Ponastavi pogled" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "Počisti rezultate iskanja" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "Viri" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "Obdrži v spominu" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "Tip rezultata:" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "Desno" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "Desni napajalni vodnik" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "Pozitivna fronta" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "Rotiraj na levo" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "Rotiraj na desno" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "Zaokroževanje navzgor/navzdol" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "Delovanje" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "Neuspešno generiranje C programske kode za I/O knjižnice!\n" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "Neuspešno generiranje C programske kode za izvajalne knjižnice!\n" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "SDO Odjemalec" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "SDO Strežnik" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "SFC" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "SFC skok v POU \"{a1}\" se sklicuje na neobstoječi SFC skok \"{a2}\"" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "SFC prehod v POU \"%s\" mora biti povezan." + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "ST" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "ST datoteke (*.st)|*.st|Vse datoteke|*.*" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "SVG datoteke (*.svg)|*.svg|Vse datoteke|*.*" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "SVGUI" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "Shrani" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "Shrani kot..." + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "Shrani kot" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "Pot za shranjevanje je enaka kot pot za shranjevanje projekta!! \n" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "Obseg iskanja" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "Išči" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "Išči v projektu" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "Sekunde:" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "Izberi vse" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "Izberi Razred spremenljivke" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "Izberi urejevalnik:" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "Izberi Primerek" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "Izberi Objekt" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "Izbrana mapa že vsebuje drugi Projekt. Prepišem? \n" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "Izbira" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "Izbira Stekališče" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "Izbira Razhajališče" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "Pregled storitev na voljo:" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "Storitve na voljo:" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "Nastavi na 1" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "Pomakni v levo" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "Pomakni v desno" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "Prikaži IEC kodo, ki jo je ustvaril PLCGenerator" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "Prikaži nadrejeno napravo" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "Prikaži nadrejeno napravo, izdelano z config_utils" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "Prikaži programsko kodo" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "Simultano Steklališče" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "Simultano Razhajališče" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "Sinus" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "Posamičen" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "Izvorna koda se ni spremenila, ne bo nove gradnje projekta.\n" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "" +"Izvorni signal mora biti definiran za eno Opravilo '{a1}' v Konfiguraciji " +"'{a2}.{a3}'." + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "Kvadratni koren (base 2)" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "Standardni funkcijski bloki" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "Zaženi krmilnik" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "Začni izvajati gradnjo projekta v %s\n" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "Zagnan" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "Krmilnik se zaganja\n" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "Statusna vrstica" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "Korak" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "Ustavi" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "Ustavi krmilnik" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "Ustavi delujoč krmilnik" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "Ustavljen" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "Ustavljam razhroščevalnik...\n" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "Struktura" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "Intervalni" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "Odštevanje" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "Uspešno izgrajeno.\n" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "Zamenjaj pogled" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "Sintaktična napaka v izrazu vzorca za iskanje!" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "TYPE" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "Tangens" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "Opravilo" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "Opravila:" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "Začasno" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" +"Vsa vprašanja o Beremiz/PLCOpenEditor projektu lahko postavite\n" +"v projektni poštni seznam: beremiz-devel@lists.sourceforge.net\n" +"\n" +"To je glavni podporni kanal skupnosti.\n" +"Potrebna je prijava na poštni seznam pred objavo vprašanja.\n" +"\n" +"Naročilo na poštni seznam skleneš na naslovu:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" +"Datoteka '%s' že obstaja.\n" +"Ali jo želiš zamenjati?" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "Skupina blokov mora biti skladna!" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "Narejene so bile spremembe, želiš shraniti?" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" +msgstr "" +"POU z imenom \"%s\" že obstaja. To lahko povzroči težave. Ali želiš " +"nadaljevati?" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "Težava z tiskanjem. Mogoče vaš tiskalnik ni nastavljen pravilno?" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "Ta opcija še ni na voljo!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "Tik-tak: %d" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "Ura" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "Dodajanje ure" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "Ura združevanje" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "Ura deljenje" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "Ura množenje" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "Odštevanje časa" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "Dodajanje današnje ure" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "Odštevanje dejanskega časa" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "Spremeni vrednost" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "Zgoraj" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "Prenesi" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "Prenesi na krmilnik" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "Uspešno zaključen prenos.\n" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "Prenos ni uspešen\n" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "Prehod" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "" +"Telo Prehoda \"%s\" mora vsebovati izhodno spremenljivko ali Tuljavo, " +"referencirano po njenem imenu" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "Ime prehoda" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "Ime prehoda:" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "Prehod z vsebino \"{a1}\" ni povezan z naslednjim Korakom v POU \"{a2}\"" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "Prehod z vsebino \"{a1}\" ni povezan z prejšnjim Korakom v POU \"{a2}\"" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "Prehod z imenom %s ne obstaja!" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "Prehodi" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "Prevedel" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "Proženje" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "Twisted programski paket ni na voljo." + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "Tip" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "Enak tip in izpeljanke" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "Neustrezen Tip za lokacijo \"%s\"" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "Sprememba podatkovnega tipa" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "Detajlne informacije o podatkovnem tipu:" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "Enak Tip" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "Tip:" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "Nisem uspel ustvariti PDO preslikave za elemnent %02x" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "Nisem uspel dobiti Xenomai's %s \n" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "Nedefiniran tip bloka \"{a1}\" v POU \"{a2}\"" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "Nedefiniran tip POU \"%s\"" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "Razveljavi" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "Neznano" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "Neznana spremenljivka \"%s\" za ta POU!" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "Neimenovan" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "Neimenovan%d" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "Nerazpoznavna velikost podatkovnega tipa \"%s\"" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "Uporabniški Podatkovni tipi" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "Uporabniški podatkovni tip" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "Uporabniški POU-ji" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "Vrednost" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "Vrednosti:" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "Spremenljivka" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "Spusti spremenljivko" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "Lastnosti spremenljivke" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "Spremenljivka Razreda" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "Spremenljivka ne pripadata temu POU!" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "Spremenljivka:" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "Spremenljivke" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "Vertikalno:" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "WAMP podrejena naprava - napaka v zagonu. " + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "WAMP povezuje na URL : %s\n" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "WAMP potekel čas za vzpostavitev" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "WAMP povezava na '%s' ni bila uspešna.\n" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "WAMP uvoz - napaka :" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "WXGLADE GUI" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "Opozorila" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "Opozorila v ST/IL/SFC ustvarjalniku programske kode :\n" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "Celoten projekt" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "Širina:" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "Iskanje po celotnem dokumentu" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "Napisal" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "WxGlade GUI" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" +"Nimaš dovoljenja za pisanje.\n" +"Vseeno odprem Inkscape ?" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" +"Nimaš dovoljenja za pisanje.\n" +"Vseeno odprem wxGlade ?" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" +"Potrebuješ pravice dostopa za delo na Projektu\n" +"Nadaljuj delo na kopiji Projekta ?" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" +msgstr "Moraš izbrati blok ali skupino blokov, kjer želiš dodati Skok!" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "Moraš izbrati Žico, na katero želiš dodati Kontakt!" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "Prosim vnesi ime!" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "Prosim vnesi vrednost!" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "Povečaj" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "dnevi" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "napaka: %s\n" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "končal z statusom {a1} (pid {a2})\n" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "datoteka : " + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "funkcija" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "funkcija : " + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "Funkcijski blok" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "ure" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "vrstica :" + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "milisekunde" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "minute" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "program" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "sekunde" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "Niz iz sredine niza " + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "Leva stran niza od" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "Desna stran niza od" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "ni posodobljenih informacij." + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "opozorilo: %s\n" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "{a1} \"{a2}\" ne more biti prilepljeno kot {a3}." + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" +"{a1} XML datoteka ni zgrajena po XSD shemi v vrstici %{a2}:\n" +"{a3}" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "CanFestivalSlaveNode" + +msgid "CAN_Device" +msgstr "CAN_Device" + +msgid "CAN_Baudrate" +msgstr "CAN_Baudrate" + +msgid "NodeId" +msgstr "NodeId" + +msgid "Sync_Align" +msgstr "Sync_Align" + +msgid "Sync_Align_Ratio" +msgstr "Sync_Align_Ratio" + +msgid "CanFestivalNode" +msgstr "CanFestivalNode" + +msgid "Sync_TPDOs" +msgstr "Sync_TPDOs" + +msgid "CanFestivalInstance" +msgstr "CanFestival Primerek" + +msgid "CAN_Driver" +msgstr "CAN_Driver" + +msgid "Generic" +msgstr "Splošen" + +msgid "Command" +msgstr "Ukaz" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "XenoConfig" + +msgid "Compiler" +msgstr "Prevajalnik" + +msgid "CFLAGS" +msgstr "CFLAGS" + +msgid "Linker" +msgstr "Povezovalnik" + +msgid "LDFLAGS" +msgstr "LDFLAGS" + +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + +msgid "BaseParams" +msgstr "BaseParams" + +msgid "IEC_Channel" +msgstr "IEC_Channel" + +msgid "Enabled" +msgstr "Omogočen" + +msgid "BeremizRoot" +msgstr "BeremizRoot" + +msgid "TargetType" +msgstr "TargetType" + +msgid "Libraries" +msgstr "Knjižnice" + +msgid "URI_location" +msgstr "URI_location" + +msgid "Disable_Extensions" +msgstr "Disable_Extensions" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)s" + +msgid "variables" +msgstr "spremenljivke" + +msgid "variable" +msgstr "spremenljivka" + +msgid "name" +msgstr "ime" + +msgid "type" +msgstr "type" + +msgid "class" +msgstr "Razred" + +msgid "initial" +msgstr "začetna" + +msgid "desc" +msgstr "opis" + +msgid "onchange" +msgstr "onchange" + +msgid "opts" +msgstr "opts" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "0 - trenutni čas, 1 - naložen iz PDT" + +msgid "Preset datetime" +msgstr "Trenutni datetime" + +msgid "Copy of IN" +msgstr "Copy of IN" + +msgid "Datetime, current or relative to PDT" +msgstr "Datetime, trenuten ali relativen trenutnemu DT" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "" +"Ura realnega časa se uporablja na več načinov, kot označevanje z časovno " +"značko, nastavljanje datuma in časa v poročilih, alarmna sporočila, itd." + +msgid "1 = integrate, 0 = hold" +msgstr "1 = integriraj, 0 = ustavi" + +msgid "Overriding reset" +msgstr "Blokira ponastavitev" + +msgid "Input variable" +msgstr "Vhodna spremenljivka" + +msgid "Initial value" +msgstr "Začetna vrednost" + +msgid "Sampling period" +msgstr "Perioda vzorčenja" + +msgid "NOT R1" +msgstr "Integrator deluje" + +msgid "Integrated output" +msgstr "Integracijski izhod" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "Integracijska funkcija integrira vrednost vhoda XIN skozi čas." + +msgid "0 = reset" +msgstr "0 = ponastavi" + +msgid "Input to be differentiated" +msgstr "Vhod v diferencialno funkcijo" + +msgid "Differentiated output" +msgstr "Diferenciran izhod" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "" +"Diferencialna funkcija generira izhod XOUT proporcionalno časovni spremembi " +"vrednosti vhoda XIN." + +msgid "0 - manual , 1 - automatic" +msgstr "0 - ročni , 1 - avtomatski" + +msgid "Process variable" +msgstr "Procesna spremenljivka" + +msgid "Set point" +msgstr "Nastavitvena točka" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "Ročna nastavitev izhoda" + +msgid "Proportionality constant" +msgstr "Proporcionalno ojačenje " + +msgid "Reset time" +msgstr "Ponastavi čas" + +msgid "Derivative time constant" +msgstr "Diferencialna časovna konstanta" + +msgid "PV - SP" +msgstr "PV - SP" + +msgid "FB for integral term" +msgstr "Funkcijski bloki za integral" + +msgid "FB for derivative term" +msgstr "Funkcijski bloki za diferencial" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "" +"Funkcijski blok PID (proportional, Integral, Derivative) ponuja standardno 3" +" parametersko izvedbo regulatorja za zaprtozančne procese." + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "0 - sledi X0, 1 - rampa do X1 in nato sledenje X1" + +msgid "Ramp duration" +msgstr "Trajanje rampe" + +msgid "BUSY = 1 during ramping period" +msgstr "BUSY = 1 med trajanjem časa rampe" + +msgid "Elapsed time of ramp" +msgstr "Čas delovanja rampe" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "" +"Funkcija RAMP generira rampo na izhod XOUT-REAL glede na vhodni parameter RUN\n" +"RUN=0 - Izhod sledi vhodu X0. \n" +"RUN=1 - Izhod gre po rampi do X1 v času TR, v tem času je BUSY=1. Ko izhod doseže X1 ostane na tej vrednosti, BUSY gre na 0." + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "" +"Funkcijski blok Histereza aktivira binarni izhod, ko je vhod XIN1(REAL) " +"večji kot XIN2+EPS, in deaktivira binarni izhod, ko je vhod manjši od " +"XIN2-EPS." + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "Bistabilno vezje SR, kjer ima SET prioriteto." + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "Bistabilno vezje RS, kjer ima RESET prioriteto." + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "" +"Funkcijski blok Semafor omogoča izvedbo mehanizma, kjer je dovoljen dostop " +"do posameznega vira samo enemu programskemu elementu naenkrat." + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "Izhod je aktiven za en cikel, ko detektira pozitivno fronto vhoda." + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "Izhod je aktiven za en cikel, ko detektira negativno fronto vhoda." + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "" +"CTU - Števec navzgor šteje vsako pozitivno fronto CU in jo prepiše na izhod " +"CV-INT. Ko pride doseže željeno vrednosti PV-INT, aktivira izhod Q dokler se" +" ne ponastavi števec z vhodom R=1." + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "" +"CTD - Števec navzdol šteje vsako pozitivno fronto CD in jo prepiše na izhod " +"CV-INT. Ko CV doseže vrednost 0, aktivira izhod Q dokler se ne ponastavi " +"števec z vhodom LD=1, ki ponastavi vrednost števca na vrednost vhoda PV." + +msgid "" +"The 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." +msgstr "" +"CTUD - Števec navzgor in navzdol lahko sočasno šteje navzgor-CU ali navzdol " +"- CD. Začetna vrednost CV se nastavi z R=1 ->CV:=0; ali LD=1 ->CV:=PV. Izhod" +" QU je aktiven ko je CV>=PV. Izhod QD je aktiven ko je CV<=0." + +msgid "first input parameter" +msgstr "prvi vhodni parameter" + +msgid "second input parameter" +msgstr "drugi vhodni parameter" + +msgid "first output parameter" +msgstr "prvi izhodni parameter" + +msgid "second output parameter" +msgstr "drugi izhodni parameter" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "notranja stanja: 0-reset, 1-šteje, 2-set" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "" +"Pulzni časovnik se lahko uporabi za ustvarjanje izhodnih pulzov določene " +"časovne dolžine." + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "" +"Časovnik zakasnjenega vklopa se uporablja za zakasnjeno aktiviranje izhoda," +" ko je vhod aktiven aktiven vsaj toliko časa kot je nastavitvena vrednost." + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "" +"Časovnik zakasnjenega izklopa se uporablja za zakasnjeno deaktiviranje " +"izhoda, ko je izhod aktiven aktiven še za nastavitveno vrednost časa po " +"deaktiviranem vhodu." diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Beremiz_zh_CN.po --- a/i18n/Beremiz_zh_CN.po Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/Beremiz_zh_CN.po Tue Jan 30 16:06:58 2018 +0100 @@ -1,47 +1,28 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +# Translators: +# Andrey Skvortsov , 2017 +# frank guan , 2017 +# Tango_Wu , 2017 msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: Beremiz\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-09-07 01:17+0200\n" -"PO-Revision-Date: 2012-09-09 18:36+0100\n" -"Last-Translator: Laurent BESSARD \n" -"Language-Team: LANGUAGE \n" -"Language: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Tango_Wu , 2017\n" +"Language-Team: Chinese (China) (https://www.transifex.com/beremiz/teams/75746/zh_CN/)\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" - -#: ../PLCOpenEditor.py:520 -#, fuzzy -msgid "" -"\n" -"An error has occurred.\n" -"\n" -"Click OK to save an error report.\n" -"\n" -"Please be kind enough to send this file to:\n" -"edouard.tisserant@gmail.com\n" -"\n" -"Error:\n" -msgstr "" -"\n" -"一个未处理的异常(漏洞)出现。漏洞报告存为:\n" -"(%s)\n" -"\n" -"或者请将文件发送至下列邮箱:\n" -"edouard.tisserant@gmail.com\n" -"\n" -"你现在必须重新启动Beremiz。\n" -"\n" -"回溯:\n" - -#: ../Beremiz.py:1071 -#, fuzzy, python-format +"Language: zh_CN\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format msgid "" "\n" "An unhandled exception (bug) occured. Bug report saved at :\n" @@ -50,223 +31,166 @@ "Please be kind enough to send this file to:\n" "beremiz-devel@lists.sourceforge.net\n" "\n" -"You should now restart Beremiz.\n" +"You should now restart program.\n" "\n" "Traceback:\n" msgstr "" "\n" -"一个未处理的异常(漏洞)出现。漏洞报告存为:\n" +"未处理的异常发生(错误) 。缺陷报告保存在:\n" "(%s)\n" "\n" -"或者请将文件发送至下列邮箱:\n" -"edouard.tisserant@gmail.com\n" +"请好心地把这个文件发送到:\n" +"beremiz-devel@lists.sourceforge.net\n" "\n" -"你现在必须重新启动Beremiz。\n" +"您现在应该重新启动程序.\n" "\n" -"回溯:\n" - -#: ../controls/VariablePanel.py:77 +"追溯:\n" + +#: ../controls/VariablePanel.py:72 msgid " External" msgstr " 外部" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " InOut" -msgstr " 输入" - -#: ../controls/VariablePanel.py:76 +msgstr " 输入输出" + +#: ../controls/VariablePanel.py:71 msgid " Input" msgstr " 输入" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid " Local" msgstr " 本地" -#: ../controls/VariablePanel.py:76 +#: ../controls/VariablePanel.py:71 msgid " Output" msgstr " 输出" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid " Temp" -msgstr " 缓冲" - -#: ../PLCOpenEditor.py:530 -msgid " : " -msgstr ":" - -#: ../dialogs/PouTransitionDialog.py:94 -#: ../dialogs/PouActionDialog.py:91 -#: ../dialogs/PouDialog.py:111 -#: ../dialogs/SFCTransitionDialog.py:144 +msgstr "临时" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 #, python-format msgid " and %s" msgstr "和 %s" -#: ../ProjectController.py:890 +#: ../ProjectController.py:1151 msgid " generation failed !\n" msgstr "生成失败!\n" -#: ../plcopen/plcopen.py:1051 +#: ../plcopen/plcopen.py:886 #, python-format msgid "\"%s\" Data Type doesn't exist !!!" msgstr "\"%s\" 数据类型尚不存在!!!" -#: ../plcopen/plcopen.py:1069 +#: ../plcopen/plcopen.py:904 #, python-format msgid "\"%s\" POU already exists !!!" -msgstr "\"%s\"编程组织单元已经存在!!!" - -#: ../plcopen/plcopen.py:1090 +msgstr "\"%s\"POU已经存在!!!" + +#: ../plcopen/plcopen.py:925 #, python-format msgid "\"%s\" POU doesn't exist !!!" msgstr "\"%s\" POU不存在!!!" -#: ../editors/Viewer.py:234 +#: ../editors/Viewer.py:247 #, python-format msgid "\"%s\" can't use itself!" msgstr "\"%s\" 不能自己使用!" -#: ../IDEFrame.py:1706 -#: ../IDEFrame.py:1725 +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 #, python-format msgid "\"%s\" config already exists!" msgstr "\"%s\" 配置已存在!" -#: ../plcopen/plcopen.py:315 +#: ../plcopen/plcopen.py:472 #, python-format msgid "\"%s\" configuration already exists !!!" msgstr "\"%s\" 配置已存在!!!" -#: ../IDEFrame.py:1660 +#: ../IDEFrame.py:1605 #, python-format msgid "\"%s\" data type already exists!" msgstr "\"%s\" 数据类型已存在!" -#: ../PLCControler.py:2040 -#: ../PLCControler.py:2044 -#, python-format -msgid "\"%s\" element can't be pasted here!!!" -msgstr "\"%s\" 元素不能粘贴在这里!!!" - -#: ../editors/TextViewer.py:305 -#: ../editors/TextViewer.py:325 -#: ../editors/Viewer.py:252 -#: ../dialogs/PouTransitionDialog.py:105 -#: ../dialogs/ConnectionDialog.py:150 -#: ../dialogs/PouActionDialog.py:102 -#: ../dialogs/FBDBlockDialog.py:162 +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 #, python-format msgid "\"%s\" element for this pou already exists!" -msgstr "\"%s\" " - -#: ../Beremiz.py:894 +msgstr "\"%s\" 元素对于此POU已经存在!" + +#: ../BeremizIDE.py:897 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "\"%s\" 文件夹不是有效的Beremiz项目\n" -#: ../plcopen/structures.py:106 -#, python-format -msgid "\"%s\" function cancelled in \"%s\" POU: No input connected" -msgstr "\"%s\" 功能被取消 \"%s\" 在POU中:没有输入连接" - -#: ../controls/VariablePanel.py:656 -#: ../IDEFrame.py:1651 -#: ../editors/DataTypeEditor.py:548 -#: ../editors/DataTypeEditor.py:577 -#: ../dialogs/PouNameDialog.py:49 -#: ../dialogs/PouTransitionDialog.py:101 -#: ../dialogs/SFCStepNameDialog.py:51 -#: ../dialogs/ConnectionDialog.py:146 -#: ../dialogs/FBDVariableDialog.py:199 -#: ../dialogs/PouActionDialog.py:98 -#: ../dialogs/PouDialog.py:118 -#: ../dialogs/SFCStepDialog.py:122 -#: ../dialogs/FBDBlockDialog.py:158 +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 #, python-format msgid "\"%s\" is a keyword. It can't be used!" msgstr "\"%s\" 是一个关键词。它不能被使用!" -#: ../editors/Viewer.py:240 -#, python-format -msgid "\"%s\" is already used by \"%s\"!" -msgstr "\"%s\" 已被 \"%s\" 使用!" - -#: ../plcopen/plcopen.py:2786 +#: ../plcopen/plcopen.py:2417 #, python-format msgid "\"%s\" is an invalid value!" msgstr "\"%s\"不是有效值!" -#: ../PLCOpenEditor.py:362 -#: ../PLCOpenEditor.py:399 +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 #, python-format msgid "\"%s\" is not a valid folder!" msgstr "\"%s\"不是有效文件夹!" -#: ../controls/VariablePanel.py:654 -#: ../IDEFrame.py:1649 -#: ../editors/DataTypeEditor.py:572 -#: ../dialogs/PouNameDialog.py:47 -#: ../dialogs/PouTransitionDialog.py:99 -#: ../dialogs/SFCStepNameDialog.py:49 -#: ../dialogs/ConnectionDialog.py:144 -#: ../dialogs/PouActionDialog.py:96 -#: ../dialogs/PouDialog.py:116 -#: ../dialogs/SFCStepDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:156 +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 #, python-format msgid "\"%s\" is not a valid identifier!" msgstr "\"%s\"不是有效标识符!" -#: ../IDEFrame.py:214 -#: ../IDEFrame.py:2445 -#: ../IDEFrame.py:2464 -#, python-format -msgid "\"%s\" is used by one or more POUs. It can't be removed!" -msgstr "%s 正在被一个或多个POU使用。不能被删除!" - -#: ../controls/VariablePanel.py:311 -#: ../IDEFrame.py:1669 -#: ../editors/TextViewer.py:303 -#: ../editors/TextViewer.py:323 -#: ../editors/TextViewer.py:360 -#: ../editors/Viewer.py:250 -#: ../editors/Viewer.py:295 -#: ../editors/Viewer.py:312 -#: ../dialogs/ConnectionDialog.py:148 -#: ../dialogs/PouDialog.py:120 -#: ../dialogs/FBDBlockDialog.py:160 +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "\"%s\"被一个或多个程序组织单元使用,你确定要继续吗? " + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 #, python-format msgid "\"%s\" pou already exists!" msgstr "\"%s\"编程组织单元已经存在!" -#: ../plcopen/plcopen.py:346 -#, python-format -msgid "\"%s\" resource already exists in \"%s\" configuration !!!" -msgstr "\"%s\" 资源已经存在于 \"%s\" 配置中!!!" - -#: ../plcopen/plcopen.py:362 -#, python-format -msgid "\"%s\" resource doesn't exist in \"%s\" configuration !!!" -msgstr "\"%s\" 资源不存在于 \"%s\" 配置之内!!!" - -#: ../dialogs/SFCStepNameDialog.py:57 -#: ../dialogs/SFCStepDialog.py:128 +#: ../dialogs/SFCStepNameDialog.py:58 #, python-format msgid "\"%s\" step already exists!" msgstr "\"%s\"步骤已经存在!" -#: ../editors/DataTypeEditor.py:543 +#: ../editors/DataTypeEditor.py:550 #, python-format msgid "\"%s\" value already defined!" msgstr "\"%s\" 值已经被定义!" -#: ../editors/DataTypeEditor.py:719 -#: ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 #, python-format msgid "\"%s\" value isn't a valid array dimension!" msgstr "\"%s\" 值不是有效数组维数!" -#: ../editors/DataTypeEditor.py:726 -#: ../dialogs/ArrayTypeDialog.py:103 +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 #, python-format msgid "" "\"%s\" value isn't a valid array dimension!\n" @@ -275,379 +199,412 @@ "\"%s\" 不是一个有效的数组维数值!\n" "右边的数值必须大于左边的数值。" -#: ../PLCControler.py:793 -#, fuzzy, python-format -msgid "%s \"%s\" can't be pasted as a %s." -msgstr "\"%s\" 元素不能粘贴在这里!!!" - -#: ../PLCControler.py:1422 -#, fuzzy, python-format +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "\"{a1}\" 功能 被取消在 \"{a2}\" POU:没有输入被连接" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" 被用在了 \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" 资源已经存在于 \"{a2}\" 配置 !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" 资源部存在于 \"{a2}\" 配置!!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dd" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 +#, python-format msgid "%s Data Types" -msgstr "数据类型 " - -#: ../editors/GraphicViewer.py:278 -#, python-format -msgid "%s Graphics" -msgstr "%s 图形" - -#: ../PLCControler.py:1417 +msgstr "%s 数据类型" + +#: ../PLCControler.py:1516 #, python-format msgid "%s POUs" -msgstr "" - -#: ../canfestival/SlaveEditor.py:42 -#: ../canfestival/NetworkEditor.py:72 +msgstr "%s POUs" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 #, python-format msgid "%s Profile" -msgstr "" - -#: ../plcopen/plcopen.py:1780 -#: ../plcopen/plcopen.py:1790 -#: ../plcopen/plcopen.py:1800 -#: ../plcopen/plcopen.py:1810 -#: ../plcopen/plcopen.py:1819 +msgstr "%s Profile" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 #, python-format msgid "%s body don't have instances!" msgstr "%s 未包含实例!" -#: ../plcopen/plcopen.py:1842 -#: ../plcopen/plcopen.py:1849 +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 #, python-format msgid "%s body don't have text!" msgstr "%s 未包含文本!" -#: ../IDEFrame.py:364 -#, fuzzy +#: ../IDEFrame.py:386 msgid "&Add Element" -msgstr "插入" - -#: ../IDEFrame.py:334 -#, fuzzy +msgstr "&增加元素" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&关闭" + +#: ../IDEFrame.py:356 msgid "&Configuration" -msgstr "配置" - -#: ../IDEFrame.py:325 -#, fuzzy +msgstr "&配置" + +#: ../IDEFrame.py:345 msgid "&Data Type" -msgstr "数据类型" - -#: ../IDEFrame.py:368 -#, fuzzy +msgstr "&数据类型" + +#: ../IDEFrame.py:390 msgid "&Delete" -msgstr "删除" - -#: ../IDEFrame.py:317 -#, fuzzy +msgstr "&删除" + +#: ../IDEFrame.py:337 msgid "&Display" -msgstr "显示" - -#: ../IDEFrame.py:316 -#, fuzzy +msgstr "&显示" + +#: ../IDEFrame.py:336 msgid "&Edit" -msgstr "编辑" - -#: ../IDEFrame.py:315 -#, fuzzy +msgstr "&编辑" + +#: ../IDEFrame.py:335 msgid "&File" -msgstr "文件" - -#: ../IDEFrame.py:327 -#, fuzzy +msgstr "&文件" + +#: ../IDEFrame.py:347 msgid "&Function" -msgstr "功能" - -#: ../IDEFrame.py:318 -#, fuzzy +msgstr "&功能" + +#: ../IDEFrame.py:338 msgid "&Help" -msgstr "帮助" - -#: ../IDEFrame.py:331 -#, fuzzy +msgstr "&帮助" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&许可" + +#: ../IDEFrame.py:351 msgid "&Program" -msgstr "程序" - -#: ../PLCOpenEditor.py:148 -#, fuzzy +msgstr "&程序" + +#: ../PLCOpenEditor.py:127 msgid "&Properties" -msgstr "属性" - -#: ../Beremiz.py:310 -#, fuzzy +msgstr "&属性" + +#: ../BeremizIDE.py:219 msgid "&Recent Projects" -msgstr "" -"#-#-#-#-# Beremiz_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭项目\n" -"#-#-#-#-# PLCOpenEditor_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭程序" - -#: ../Beremiz.py:352 -#, fuzzy +msgstr "&最近项目" + +#: ../IDEFrame.py:353 msgid "&Resource" -msgstr "资源" - -#: ../controls/SearchResultPanel.py:237 -#, python-format -msgid "'%s' - %d match in project" -msgstr "" +msgstr "&资源" #: ../controls/SearchResultPanel.py:239 -#, python-format -msgid "'%s' - %d matches in project" -msgstr "" - -#: ../connectors/PYRO/__init__.py:51 -#, python-format -msgid "'%s' is located at %s\n" -msgstr "" - -#: ../controls/SearchResultPanel.py:289 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} 在项目中匹配" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} 在项目中匹配" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' 位于 {a2}\n" + +#: ../controls/SearchResultPanel.py:291 #, python-format msgid "(%d matches)" -msgstr "" - -#: ../PLCOpenEditor.py:508 -#: ../PLCOpenEditor.py:510 -#: ../PLCOpenEditor.py:511 +msgstr "(%d 匹配)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 msgid ", " msgstr "," -#: ../dialogs/PouTransitionDialog.py:96 -#: ../dialogs/PouActionDialog.py:93 -#: ../dialogs/PouDialog.py:113 -#: ../dialogs/SFCTransitionDialog.py:146 +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 #, python-format msgid ", %s" msgstr ", %s" -#: ../PLCOpenEditor.py:506 +#: ../PLCOpenEditor.py:404 msgid ". " msgstr "。" -#: ../ProjectController.py:1268 -msgid "... debugger recovered\n" -msgstr "" - -#: ../IDEFrame.py:1672 -#: ../IDEFrame.py:1714 -#: ../IDEFrame.py:1733 -#: ../dialogs/PouDialog.py:122 -#, python-format -msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1d" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "" +"A POU has an element named \"%s\". This could cause a conflict. Do you wish " +"to continue?" msgstr "一个编程组织单元的成员被命名为\"%s\"。这可能会产生冲突。你希望继续吗?" -#: ../controls/VariablePanel.py:658 -#: ../IDEFrame.py:1684 -#: ../IDEFrame.py:1695 -#: ../dialogs/PouNameDialog.py:51 -#: ../dialogs/PouTransitionDialog.py:103 -#: ../dialogs/SFCStepNameDialog.py:53 -#: ../dialogs/PouActionDialog.py:100 -#: ../dialogs/SFCStepDialog.py:124 +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 #, python-format msgid "A POU named \"%s\" already exists!" msgstr "一个以\"%s\"命名的的编程组织单元已经存在!" -#: ../ConfigTreeNode.py:371 -#, fuzzy, python-format -msgid "A child named \"%s\" already exist -> \"%s\"\n" -msgstr "分支名字 \"%s\" 已经存在 -> \"%s\"\n" - -#: ../dialogs/BrowseLocationsDialog.py:175 -#, fuzzy +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "一个子命名的 \"{a1}\" 已经存在 -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 msgid "A location must be selected!" -msgstr "至少选择一个变量或者表达式!" - -#: ../controls/VariablePanel.py:660 -#: ../IDEFrame.py:1686 -#: ../IDEFrame.py:1697 -#: ../dialogs/SFCStepNameDialog.py:55 -#: ../dialogs/SFCStepDialog.py:126 +msgstr "一个定位必须被选择!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "相同名称的任务已经存在!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 #, python-format msgid "A variable with \"%s\" as name already exists in this pou!" msgstr "一个以\"%s\"命名的变量在这个编程组织单元中已经存在!" -#: ../Beremiz.py:362 -#: ../PLCOpenEditor.py:181 +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "一个变量以 \"%s\" 作为名字已经存在!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 msgid "About" msgstr "关于" -#: ../Beremiz.py:931 -msgid "About Beremiz" -msgstr "关于Beremiz" - -#: ../PLCOpenEditor.py:376 -msgid "About PLCOpenEditor" -msgstr "关于PLCOpen编辑器" - #: ../plcopen/iec_std.csv:22 msgid "Absolute number" msgstr "绝对值" -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCStepDialog.py:69 +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 msgid "Action" -msgstr "行动" - -#: ../editors/Viewer.py:495 -#, fuzzy +msgstr "动作" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 msgid "Action Block" -msgstr "功能块" - -#: ../dialogs/PouActionDialog.py:81 +msgstr "动作控制功能块" + +#: ../dialogs/PouActionDialog.py:82 msgid "Action Name" -msgstr "行动名字" +msgstr "动作名字" #: ../dialogs/PouActionDialog.py:49 msgid "Action Name:" -msgstr "行动名字:" - -#: ../plcopen/plcopen.py:1480 +msgstr "动作名字:" + +#: ../plcopen/plcopen.py:1364 #, python-format msgid "Action with name %s doesn't exist!" msgstr "一个以\"%s\"命名的的行动不存在!" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Actions" -msgstr "行动" - -#: ../dialogs/ActionBlockDialog.py:134 +msgstr "动作" + +#: ../dialogs/ActionBlockDialog.py:133 msgid "Actions:" -msgstr "行动:" - -#: ../canfestival/SlaveEditor.py:54 -#: ../canfestival/NetworkEditor.py:84 -#: ../editors/Viewer.py:527 +msgstr "动作:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "活动" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 msgid "Add" msgstr "添加" -#: ../IDEFrame.py:1925 -#: ../IDEFrame.py:1956 +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 msgid "Add Action" -msgstr "添加行动" - -#: ../features.py:7 +msgstr "添加动作" + +#: ../features.py:32 msgid "Add C code accessing located variables synchronously" -msgstr "" - -#: ../IDEFrame.py:1908 +msgstr "同步的增加C代码访问定位的变量" + +#: ../IDEFrame.py:1876 msgid "Add Configuration" msgstr "添加配置" -#: ../IDEFrame.py:1888 +#: ../IDEFrame.py:1856 msgid "Add DataType" msgstr "添加数据类型" -#: ../editors/Viewer.py:453 +#: ../editors/Viewer.py:572 msgid "Add Divergence Branch" msgstr "添加发散分支" -#: ../dialogs/DiscoveryDialog.py:115 -#, fuzzy +#: ../dialogs/DiscoveryDialog.py:117 msgid "Add IP" -msgstr "添加Pou" - -#: ../IDEFrame.py:1896 -#, fuzzy +msgstr "添加 IP" + +#: ../IDEFrame.py:1864 msgid "Add POU" -msgstr "添加Pou" - -#: ../features.py:8 +msgstr "添加 POU" + +#: ../features.py:33 msgid "Add Python code executed asynchronously" -msgstr "" - -#: ../IDEFrame.py:1936 -#: ../IDEFrame.py:1982 +msgstr "添加异步执行的Python代码" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 msgid "Add Resource" msgstr "添加源" -#: ../IDEFrame.py:1914 -#: ../IDEFrame.py:1953 +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 msgid "Add Transition" msgstr "添加跃迁" -#: ../editors/Viewer.py:442 +#: ../editors/Viewer.py:559 msgid "Add Wire Segment" msgstr "添加布线段" -#: ../editors/SFCViewer.py:359 +#: ../editors/SFCViewer.py:433 msgid "Add a new initial step" msgstr "新建一个初始步骤" -#: ../editors/Viewer.py:2289 -#: ../editors/SFCViewer.py:696 +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 msgid "Add a new jump" msgstr "新建一个跳跃" -#: ../editors/SFCViewer.py:381 +#: ../editors/SFCViewer.py:455 msgid "Add a new step" msgstr "添加一个新步骤" -#: ../features.py:9 +#: ../features.py:34 msgid "Add a simple WxGlade based GUI." -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:138 -#, fuzzy +msgstr "添加一个简单的基于 WxGlade的 GUI." + +#: ../dialogs/ActionBlockDialog.py:137 msgid "Add action" -msgstr "添加行动" - -#: ../editors/DataTypeEditor.py:345 -#, fuzzy +msgstr "添加动作" + +#: ../editors/DataTypeEditor.py:352 msgid "Add element" -msgstr "插入" - -#: ../editors/ResourceEditor.py:251 -#, fuzzy +msgstr "添加元素" + +#: ../editors/ResourceEditor.py:268 msgid "Add instance" msgstr "添加实例" -#: ../canfestival/NetworkEditor.py:86 -#, fuzzy +#: ../canfestival/NetworkEditor.py:103 msgid "Add slave" -msgstr "添加实例" - -#: ../editors/ResourceEditor.py:222 -#, fuzzy +msgstr "添加从站" + +#: ../editors/ResourceEditor.py:239 msgid "Add task" msgstr "添加任务" -#: ../controls/VariablePanel.py:378 -#, fuzzy +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 msgid "Add variable" -msgstr "变量" +msgstr "添加变量" #: ../plcopen/iec_std.csv:33 msgid "Addition" msgstr "加法" -#: ../plcopen/structures.py:250 +#: ../plcopen/definitions.py:49 msgid "Additional function blocks" msgstr "附加功能类型" -#: ../editors/Viewer.py:1395 +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "调整块尺寸" + +#: ../editors/Viewer.py:1686 msgid "Alignment" msgstr "对准" -#: ../controls/VariablePanel.py:75 -#: ../dialogs/BrowseLocationsDialog.py:35 -#: ../dialogs/BrowseLocationsDialog.py:116 +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 msgid "All" msgstr "所有" #: ../editors/FileManagementPanel.py:35 -#, fuzzy msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" -msgstr "ST 文件 (*.st)|*.st|所有文件|*.*" - -#: ../ProjectController.py:1335 +msgstr "所有文件 (*.*)|*.*|CSV 文件 (*.csv)|*.csv" + +#: ../ProjectController.py:1685 msgid "Already connected. Please disconnect\n" msgstr "已经连接。请断开连接\n" -#: ../editors/DataTypeEditor.py:587 +#: ../editors/DataTypeEditor.py:591 #, python-format msgid "An element named \"%s\" already exists in this structure!" msgstr "一个以\"%s\"命名的元素已经在这个结构中存在!" +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "相同名称的实例已经存在!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "应用名称修改到所有伴随相同名称的延续" + #: ../plcopen/iec_std.csv:31 msgid "Arc cosine" msgstr "反余弦" @@ -664,8 +621,8 @@ msgid "Arithmetic" msgstr "运算" -#: ../controls/VariablePanel.py:729 -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 msgid "Array" msgstr "阵列的" @@ -673,49 +630,45 @@ msgid "Assignment" msgstr "分配" -#: ../dialogs/FBDVariableDialog.py:197 +#: ../dialogs/FBDVariableDialog.py:222 msgid "At least a variable or an expression must be selected!" msgstr "至少选择一个变量或者表达式!" -#: ../controls/ProjectPropertiesPanel.py:99 +#: ../controls/ProjectPropertiesPanel.py:100 msgid "Author" msgstr "作者" -#: ../controls/ProjectPropertiesPanel.py:96 +#: ../controls/ProjectPropertiesPanel.py:97 msgid "Author Name (optional):" msgstr "作者姓名(选填):" -#: ../dialogs/FindInPouDialog.py:72 +#: ../dialogs/FindInPouDialog.py:77 msgid "Backward" -msgstr "" +msgstr "反向" #: ../util/Zeroconf.py:599 msgid "Bad domain name (circular) at " -msgstr "" +msgstr "不好域名称(循环的)在" #: ../util/Zeroconf.py:602 msgid "Bad domain name at " -msgstr "" - -#: ../canfestival/config_utils.py:341 -#: ../canfestival/config_utils.py:623 +msgstr "不好的域名称在" + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 #, python-format msgid "Bad location size : %s" msgstr "不好的位置大小:%s" -#: ../editors/DataTypeEditor.py:168 -#: ../editors/DataTypeEditor.py:198 -#: ../editors/DataTypeEditor.py:290 -#: ../dialogs/ArrayTypeDialog.py:55 +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 msgid "Base Type:" msgstr "基类型:" -#: ../controls/VariablePanel.py:699 -#: ../editors/DataTypeEditor.py:617 +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 msgid "Base Types" msgstr "基类型" -#: ../Beremiz.py:486 +#: ../BeremizIDE.py:455 msgid "Beremiz" msgstr "Beremiz" @@ -747,146 +700,169 @@ msgid "Bitwise inverting" msgstr "按位“反向”" -#: ../editors/Viewer.py:465 -#, fuzzy +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 msgid "Block" -msgstr "编辑块" - -#: ../dialogs/FBDBlockDialog.py:38 +msgstr "块" + +#: ../dialogs/FBDBlockDialog.py:60 msgid "Block Properties" msgstr "块属性" -#: ../editors/Viewer.py:434 +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "块名称" + +#: ../editors/Viewer.py:550 msgid "Bottom" msgstr "底部" -#: ../dialogs/BrowseValuesLibraryDialog.py:37 +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "损坏" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 #, python-format msgid "Browse %s values library" -msgstr "" - -#: ../dialogs/BrowseLocationsDialog.py:55 -#, fuzzy +msgstr "浏览 %s 值库" + +#: ../dialogs/BrowseLocationsDialog.py:65 msgid "Browse Locations" -msgstr "位置" - -#: ../ProjectController.py:1484 +msgstr "浏览定位" + +#: ../ProjectController.py:1832 msgid "Build" msgstr "构建" -#: ../ProjectController.py:1051 +#: ../ProjectController.py:1297 msgid "Build directory already clean\n" msgstr "构建目录已经清除\n" -#: ../ProjectController.py:1485 +#: ../ProjectController.py:1833 msgid "Build project into build folder" msgstr "在构建文件夹中构建项目" -#: ../ProjectController.py:910 +#: ../ProjectController.py:1080 msgid "C Build crashed !\n" msgstr "C构建损坏!\n" -#: ../ProjectController.py:907 +#: ../ProjectController.py:1077 msgid "C Build failed.\n" msgstr "C构建失败。\n" -#: ../ProjectController.py:895 +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "C 代码" + +#: ../ProjectController.py:1155 msgid "C code generated successfully.\n" msgstr "C代码生成成功。\n" -#: ../targets/toolchain_gcc.py:132 +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "C编译失败。\n" + +#: ../targets/toolchain_gcc.py:192 #, python-format msgid "C compilation of %s failed.\n" msgstr " %s 的C编译失败。\n" -#: ../features.py:7 -#, fuzzy +#: ../features.py:32 msgid "C extension" msgstr "C扩展" -#: ../features.py:6 +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "C&redits" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "CANOpen 网络" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "CANOpen 从站" + +#: ../features.py:31 msgid "CANopen support" -msgstr "" - -#: ../plcopen/plcopen.py:1722 -#: ../plcopen/plcopen.py:1736 -#: ../plcopen/plcopen.py:1757 -#: ../plcopen/plcopen.py:1773 +msgstr "CANOpen 支持" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 msgid "Can only generate execution order on FBD networks!" msgstr "在功能块网络,只能生成执行命令!" -#: ../controls/VariablePanel.py:256 +#: ../controls/VariablePanel.py:267 msgid "Can only give a location to local or global variables" msgstr "只能影响本地或全局变量的位置" -#: ../PLCOpenEditor.py:357 +#: ../PLCOpenEditor.py:344 #, python-format msgid "Can't generate program to file %s!" msgstr "这个编程生成文件失败 %s!" -#: ../controls/VariablePanel.py:254 +#: ../controls/VariablePanel.py:265 msgid "Can't give a location to a function block instance" msgstr "不能影响功能块实例的位置" -#: ../PLCOpenEditor.py:397 +#: ../PLCOpenEditor.py:389 #, python-format msgid "Can't save project to file %s!" msgstr "这个项目保存为文件失败 %s!" -#: ../controls/VariablePanel.py:298 -#, fuzzy +#: ../controls/VariablePanel.py:313 msgid "Can't set an initial value to a function block instance" -msgstr "不能影响功能块实例的位置" - -#: ../ConfigTreeNode.py:470 -#, python-format -msgid "Cannot create child %s of type %s " -msgstr "无法新建分支 %s 类型 %s " - -#: ../ConfigTreeNode.py:400 +msgstr "不能设置一个初始值到一个功能块实例" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "不能创建类型 {a2} 的子类型 {a1} " + +#: ../ConfigTreeNode.py:454 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "无法找到比 %d 更低的自由的IEC通道\n" -#: ../connectors/PYRO/__init__.py:92 +#: ../connectors/PYRO/__init__.py:131 msgid "Cannot get PLC status - connection failed.\n" msgstr "无法获取PLC的状态 - 连接失败。\n" -#: ../ProjectController.py:715 +#: ../ProjectController.py:943 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "无法打开/解析 VARIABLES.csv!\n" -#: ../canfestival/config_utils.py:371 -#, python-format -msgid "Cannot set bit offset for non bool '%s' variable (ID:%d,Idx:%x,sIdx:%x))" -msgstr "无法设定位抵消非布尔 '%s' variable (ID:%d,Idx:%x,sIdx:%x)) " - -#: ../dialogs/FindInPouDialog.py:81 -#: ../dialogs/SearchInProjectDialog.py:67 +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "" +"Cannot set bit offset for non bool '{a1}' variable " +"(ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "不能设置位偏移对于非布尔 '{a1}' 变量 (ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 msgid "Case sensitive" -msgstr "" - -#: ../editors/Viewer.py:429 +msgstr "区分大小写" + +#: ../editors/Viewer.py:545 msgid "Center" msgstr "中" -#: ../Beremiz_service.py:322 +#: ../Beremiz_service.py:268 msgid "Change IP of interface to bind" msgstr "更改界面的ip用以绑定" -#: ../Beremiz_service.py:321 +#: ../Beremiz_service.py:267 msgid "Change Name" msgstr "更改名字" -#: ../IDEFrame.py:1974 +#: ../IDEFrame.py:1946 msgid "Change POU Type To" msgstr "将POU类型转换为" -#: ../Beremiz_service.py:325 +#: ../Beremiz_service.py:269 msgid "Change Port Number" msgstr "更改端口号" -#: ../Beremiz_service.py:327 +#: ../Beremiz_service.py:270 msgid "Change working directory" msgstr "更改工作目录" @@ -894,124 +870,115 @@ msgid "Character string" msgstr "字符串" -#: ../svgui/svgui.py:92 +#: ../svgui/svgui.py:128 msgid "Choose a SVG file" msgstr "选择一个SVG文件" -#: ../ProjectController.py:353 -#, fuzzy +#: ../ProjectController.py:542 msgid "Choose a directory to save project" -msgstr "选择一个项目" - -#: ../canfestival/canfestival.py:118 -#: ../PLCOpenEditor.py:313 -#: ../PLCOpenEditor.py:347 -#: ../PLCOpenEditor.py:391 +msgstr "选择一个目录保存项目" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 msgid "Choose a file" msgstr "选择一个文件" -#: ../Beremiz.py:831 -#: ../Beremiz.py:866 +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 msgid "Choose a project" msgstr "选择一个项目" -#: ../dialogs/BrowseValuesLibraryDialog.py:42 -#, fuzzy, python-format +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format msgid "Choose a value for %s:" -msgstr "选择一个文件" - -#: ../Beremiz_service.py:373 +msgstr "这对 %s选择一个值:" + +#: ../Beremiz_service.py:325 msgid "Choose a working directory " msgstr "选择一个工作目录" -#: ../ProjectController.py:281 +#: ../ProjectController.py:449 msgid "Chosen folder doesn't contain a program. It's not a valid project!" msgstr "被选中的文件夹未包含一个程序。它不是一个有效项目!" -#: ../ProjectController.py:247 +#: ../ProjectController.py:416 msgid "Chosen folder isn't empty. You can't use it for a new project!" msgstr "被选中的文件夹非空。你不能用它创建一个新项目!" -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Class" msgstr "分类" -#: ../controls/VariablePanel.py:369 +#: ../controls/VariablePanel.py:441 msgid "Class Filter:" msgstr "类过滤器:" -#: ../dialogs/FBDVariableDialog.py:62 +#: ../dialogs/FBDVariableDialog.py:70 msgid "Class:" msgstr "分类:" -#: ../ProjectController.py:1488 +#: ../ProjectController.py:1836 msgid "Clean" msgstr "清除" -#: ../ProjectController.py:1490 +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "清楚记录信息" + +#: ../ProjectController.py:1838 msgid "Clean project build folder" msgstr "清除项目构建目录" -#: ../ProjectController.py:1048 +#: ../ProjectController.py:1294 msgid "Cleaning the build directory\n" msgstr "清除构建目录\n" -#: ../IDEFrame.py:411 -#, fuzzy +#: ../IDEFrame.py:435 msgid "Clear Errors" -msgstr "清除错误\tCTRL+K" - -#: ../editors/Viewer.py:520 +msgstr "清楚错误" + +#: ../editors/Viewer.py:641 msgid "Clear Execution Order" msgstr "清空执行命令" -#: ../editors/GraphicViewer.py:125 -msgid "Clear the graph values" -msgstr "" - -#: ../Beremiz.py:598 -#: ../PLCOpenEditor.py:221 -#, fuzzy +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "关闭" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 msgid "Close Application" -msgstr "" -"#-#-#-#-# Beremiz_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭应用\n" -"#-#-#-#-# PLCOpenEditor_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭应用程序" - -#: ../IDEFrame.py:1089 -#: ../Beremiz.py:319 -#: ../Beremiz.py:552 -#: ../PLCOpenEditor.py:131 -#, fuzzy +msgstr "关闭应用" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 msgid "Close Project" -msgstr "" -"#-#-#-#-# Beremiz_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭项目\n" -"#-#-#-#-# PLCOpenEditor_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭程序" - -#: ../Beremiz.py:317 -#: ../PLCOpenEditor.py:129 -#, fuzzy +msgstr "关闭项目" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 msgid "Close Tab" -msgstr "关闭当前" - -#: ../editors/Viewer.py:481 +msgstr "关闭标签" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 msgid "Coil" -msgstr "" - -#: ../editors/Viewer.py:501 -#: ../editors/LDViewer.py:503 +msgstr "线圈" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 msgid "Comment" msgstr "注释" -#: ../controls/ProjectPropertiesPanel.py:94 +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "社区支持" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "公司名称" + +#: ../controls/ProjectPropertiesPanel.py:95 msgid "Company Name (required):" msgstr "公司名字(必须):" -#: ../controls/ProjectPropertiesPanel.py:95 +#: ../controls/ProjectPropertiesPanel.py:96 msgid "Company URL (optional):" msgstr "公司网址(选填):" @@ -1019,7 +986,7 @@ msgid "Comparison" msgstr "比较" -#: ../ProjectController.py:538 +#: ../ProjectController.py:734 msgid "Compiling IEC Program into C code...\n" msgstr "正在将IEC程序编译成C代码...\n" @@ -1027,73 +994,93 @@ msgid "Concatenation" msgstr "级联" -#: ../dialogs/SearchInProjectDialog.py:47 +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "配置" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "配置变量" + +#: ../dialogs/SearchInProjectDialog.py:40 msgid "Configuration" msgstr "配置" -#: ../PLCControler.py:96 +#: ../PLCControler.py:99 msgid "Configurations" msgstr "配置" -#: ../ProjectController.py:1503 +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "确认或变更变量名称" + +#: ../ProjectController.py:1851 msgid "Connect" msgstr "连接" -#: ../ProjectController.py:1504 +#: ../ProjectController.py:1852 msgid "Connect to the target PLC" msgstr "连接到PLC目标" -#: ../connectors/PYRO/__init__.py:40 -#, python-format -msgid "Connecting to URI : %s\n" -msgstr "连接到URI: %s!\n" - -#: ../editors/Viewer.py:467 -#: ../dialogs/SFCTransitionDialog.py:76 +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "连接到URI: %s" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 msgid "Connection" msgstr "连接" -#: ../dialogs/ConnectionDialog.py:37 +#: ../dialogs/ConnectionDialog.py:53 msgid "Connection Properties" msgstr "连接属性" -#: ../ProjectController.py:1359 -#, fuzzy +#: ../ProjectController.py:1709 msgid "Connection canceled!\n" -msgstr "连接失败 %s!\n" - -#: ../ProjectController.py:1384 +msgstr "取消连接!\n" + +#: ../ProjectController.py:1734 #, python-format msgid "Connection failed to %s!\n" msgstr "连接失败 %s!\n" -#: ../connectors/PYRO/__init__.py:63 -#, fuzzy, python-format +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "失去连接!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format msgid "Connection to '%s' failed.\n" -msgstr " %s 的C编译失败。\n" - -#: ../dialogs/ConnectionDialog.py:56 +msgstr "连接到 '%s' 失败。\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 msgid "Connector" msgstr "连接" -#: ../dialogs/SFCStepDialog.py:58 +#: ../dialogs/SFCStepDialog.py:66 msgid "Connectors:" msgstr "连接:" -#: ../controls/VariablePanel.py:65 +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "控制台" + +#: ../controls/VariablePanel.py:60 msgid "Constant" msgstr "常量" -#: ../editors/Viewer.py:477 -#, fuzzy +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 msgid "Contact" -msgstr "连续" - -#: ../controls/ProjectPropertiesPanel.py:197 +msgstr "连接" + +#: ../controls/ProjectPropertiesPanel.py:198 msgid "Content Description (optional):" msgstr "内容描述(选填):" -#: ../dialogs/ConnectionDialog.py:61 +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 msgid "Continuation" msgstr "连续" @@ -1113,197 +1100,188 @@ msgid "Conversion to time-of-day" msgstr "转换为日期时间" -#: ../IDEFrame.py:348 -#: ../IDEFrame.py:401 -#: ../editors/Viewer.py:536 +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 msgid "Copy" -msgstr "" - -#: ../IDEFrame.py:1961 +msgstr "复制" + +#: ../IDEFrame.py:1933 msgid "Copy POU" -msgstr "" - -#: ../editors/FileManagementPanel.py:283 +msgstr "复制POU" + +#: ../editors/FileManagementPanel.py:65 msgid "Copy file from left folder to right" -msgstr "" - -#: ../editors/FileManagementPanel.py:282 +msgstr "从左侧文件夹中复制文件到右侧" + +#: ../editors/FileManagementPanel.py:64 msgid "Copy file from right folder to left" -msgstr "" +msgstr "从右侧文件夹中复制文件到左侧" #: ../plcopen/iec_std.csv:28 msgid "Cosine" msgstr "余弦" -#: ../ConfigTreeNode.py:582 -#, python-format -msgid "" -"Could not add child \"%s\", type %s :\n" -"%s\n" +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" msgstr "" -"无法添加分支 \"%s\", type %s :\n" -"%s\n" - -#: ../ConfigTreeNode.py:559 -#, fuzzy, python-format -msgid "" -"Couldn't load confnode base parameters %s :\n" -" %s" +"不能增加子 \"{a1}\",类型 {a2} :\n" +"{a3}\n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "不能导入旧 %s 文件。" + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" msgstr "" -"无法下载插件基本参数 %s :\n" -" %s" - -#: ../ConfigTreeNode.py:570 -#, fuzzy, python-format -msgid "" -"Couldn't load confnode parameters %s :\n" -" %s" +"不能加载 confnode 基础参数 {a1} :\n" +" {a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" msgstr "" -"无法下载插件参数 %s :\n" -" %s" - -#: ../PLCControler.py:765 -#: ../PLCControler.py:802 +"不能加载 confnode 参数 {a1} :\n" +" {a2}" + +#: ../PLCControler.py:948 msgid "Couldn't paste non-POU object." -msgstr "" - -#: ../ProjectController.py:1317 +msgstr "不能粘贴 非-POU 目标。" + +#: ../ProjectController.py:1651 msgid "Couldn't start PLC !\n" msgstr "无法开始PLC!\n" -#: ../ProjectController.py:1325 +#: ../ProjectController.py:1659 msgid "Couldn't stop PLC !\n" msgstr "无法停止PLC!\n" -#: ../ProjectController.py:1295 -#, fuzzy +#: ../ProjectController.py:1623 msgid "Couldn't stop debugger.\n" -msgstr "无法开始PLC调试!\n" - -#: ../svgui/svgui.py:22 +msgstr "不能停止调试器。\n" + +#: ../svgui/svgui.py:49 msgid "Create HMI" -msgstr "" - -#: ../dialogs/PouDialog.py:43 +msgstr "新建 HMI" + +#: ../dialogs/PouDialog.py:46 msgid "Create a new POU" msgstr "新建一个POU" #: ../dialogs/PouActionDialog.py:38 msgid "Create a new action" -msgstr "新建一个行动" - -#: ../IDEFrame.py:135 +msgstr "新建一个动作" + +#: ../IDEFrame.py:159 msgid "Create a new action block" -msgstr "新建一个作用块" - -#: ../IDEFrame.py:84 -#: ../IDEFrame.py:114 -#: ../IDEFrame.py:147 +msgstr "新建一个动作控制功能块" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 msgid "Create a new block" msgstr "新建一个块" -#: ../IDEFrame.py:108 +#: ../IDEFrame.py:132 msgid "Create a new branch" msgstr "新建一个支流" -#: ../IDEFrame.py:102 +#: ../IDEFrame.py:126 msgid "Create a new coil" msgstr "新建一个线圈" -#: ../IDEFrame.py:78 -#: ../IDEFrame.py:93 -#: ../IDEFrame.py:123 +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 msgid "Create a new comment" msgstr "新建一个备注" -#: ../IDEFrame.py:87 -#: ../IDEFrame.py:117 -#: ../IDEFrame.py:150 +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 msgid "Create a new connection" msgstr "新建一个连接" -#: ../IDEFrame.py:105 -#: ../IDEFrame.py:156 +#: ../IDEFrame.py:129 ../IDEFrame.py:180 msgid "Create a new contact" msgstr "新建一个接触点" -#: ../IDEFrame.py:138 +#: ../IDEFrame.py:162 msgid "Create a new divergence" msgstr "新建一个发散" -#: ../dialogs/SFCDivergenceDialog.py:36 +#: ../dialogs/SFCDivergenceDialog.py:53 msgid "Create a new divergence or convergence" msgstr "新建一个发散或者收敛" -#: ../IDEFrame.py:126 +#: ../IDEFrame.py:150 msgid "Create a new initial step" msgstr "新建一个初始步骤" -#: ../IDEFrame.py:141 +#: ../IDEFrame.py:165 msgid "Create a new jump" msgstr "新建一个跳跃" -#: ../IDEFrame.py:96 -#: ../IDEFrame.py:153 +#: ../IDEFrame.py:120 ../IDEFrame.py:177 msgid "Create a new power rail" msgstr "新建一个电源导轨" -#: ../IDEFrame.py:99 +#: ../IDEFrame.py:123 msgid "Create a new rung" msgstr "新建一个梯级" -#: ../IDEFrame.py:129 +#: ../IDEFrame.py:153 msgid "Create a new step" msgstr "新建一个步骤" -#: ../IDEFrame.py:132 -#: ../dialogs/PouTransitionDialog.py:42 +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 msgid "Create a new transition" msgstr "新建一个跃迁" -#: ../IDEFrame.py:81 -#: ../IDEFrame.py:111 -#: ../IDEFrame.py:144 +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 msgid "Create a new variable" msgstr "新建一个变量" -#: ../IDEFrame.py:346 -#: ../IDEFrame.py:400 -#: ../editors/Viewer.py:535 +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "关于作者" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "当前工作目录:" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 msgid "Cut" -msgstr "" - -#: ../editors/ResourceEditor.py:71 +msgstr "剪切" + +#: ../editors/ResourceEditor.py:72 msgid "Cyclic" -msgstr "" - -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:58 +msgstr "循环" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 #: ../plcopen/iec_std.csv:60 msgid "DEPRECATED" -msgstr "" - -#: ../canfestival/SlaveEditor.py:50 -#: ../canfestival/NetworkEditor.py:80 +msgstr "DEPRECATED" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 msgid "DS-301 Profile" -msgstr "" - -#: ../canfestival/SlaveEditor.py:51 -#: ../canfestival/NetworkEditor.py:81 +msgstr "DS-301 配置" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 msgid "DS-302 Profile" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:43 +msgstr "DS-302 配置" + +#: ../dialogs/SearchInProjectDialog.py:36 msgid "Data Type" msgstr "数据类型" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Data Types" msgstr "数据类型 " @@ -1311,85 +1289,75 @@ msgid "Data type conversion" msgstr "日期类型转换" -#: ../plcopen/iec_std.csv:44 -#: ../plcopen/iec_std.csv:45 +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 msgid "Date addition" msgstr "日期加法" -#: ../plcopen/iec_std.csv:56 -#: ../plcopen/iec_std.csv:57 -#: ../plcopen/iec_std.csv:58 -#: ../plcopen/iec_std.csv:59 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 msgid "Date and time subtraction" msgstr "日期和时间减法" -#: ../plcopen/iec_std.csv:50 -#: ../plcopen/iec_std.csv:51 +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 msgid "Date subtraction" msgstr "日期减法" -#: ../dialogs/DurationEditorDialog.py:43 +#: ../dialogs/DurationEditorDialog.py:44 msgid "Days:" -msgstr "" - -#: ../ProjectController.py:1405 -msgid "Debug connect matching running PLC\n" -msgstr "" - -#: ../ProjectController.py:1408 -msgid "Debug do not match PLC - stop/transfert/start to re-enable\n" -msgstr "" - -#: ../controls/PouInstanceVariablesPanel.py:52 -#, fuzzy +msgstr "日:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "调试部匹配PLC - 停止/传输/启动 来新启用\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 msgid "Debug instance" -msgstr "删除实例" - -#: ../editors/Viewer.py:3222 -#, fuzzy, python-format +msgstr "调试实例" + +#: ../editors/Viewer.py:448 +#, python-format msgid "Debug: %s" -msgstr "调试" - -#: ../ProjectController.py:1122 -#, fuzzy, python-format +msgstr "调试:%s" + +#: ../ProjectController.py:1412 +#, python-format msgid "Debug: Unknown variable '%s'\n" -msgstr "调试 :未知变量 %s\n" - -#: ../ProjectController.py:1120 +msgstr "调试:未知变量 '%s'\n" + +#: ../ProjectController.py:1410 #, python-format msgid "Debug: Unsupported type to debug '%s'\n" -msgstr "" - -#: ../IDEFrame.py:608 -#, fuzzy +msgstr "调试:不支持的类型进行调试 '%s'\n" + +#: ../IDEFrame.py:639 msgid "Debugger" -msgstr "调试" - -#: ../ProjectController.py:1285 +msgstr "调试器" + +#: ../ProjectController.py:1592 msgid "Debugger disabled\n" msgstr "调试器禁用\n" -#: ../ProjectController.py:1297 -#, fuzzy +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "调试器准备好\n" + +#: ../ProjectController.py:1625 msgid "Debugger stopped.\n" -msgstr "调试器禁用\n" - -#: ../IDEFrame.py:1990 -#: ../Beremiz.py:958 -#: ../editors/Viewer.py:511 +msgstr "调试器停止。\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 msgid "Delete" msgstr "删除" -#: ../editors/Viewer.py:454 +#: ../editors/Viewer.py:573 msgid "Delete Divergence Branch" msgstr "删除发散分支" -#: ../editors/FileManagementPanel.py:371 -#, fuzzy +#: ../editors/FileManagementPanel.py:153 msgid "Delete File" -msgstr "删除项目" - -#: ../editors/Viewer.py:443 +msgstr "删除文件" + +#: ../editors/Viewer.py:560 msgid "Delete Wire Segment" msgstr "删除布线段" @@ -1401,188 +1369,168 @@ msgid "Deletion (within)" msgstr "删除" -#: ../editors/DataTypeEditor.py:146 +#: ../editors/DataTypeEditor.py:153 msgid "Derivation Type:" msgstr "推导类型:" -#: ../plcopen/structures.py:264 -msgid "" -"Derivative\n" -"The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." -msgstr "" -"导数\n" -"导数功能块根据输入XIN的速率的变化而按比例的生产输出XOUT。" - -#: ../controls/VariablePanel.py:360 -#, fuzzy +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "描述" + +#: ../controls/VariablePanel.py:432 msgid "Description:" -msgstr "定位:" - -#: ../editors/DataTypeEditor.py:314 -#: ../dialogs/ArrayTypeDialog.py:61 +msgstr "描述:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 msgid "Dimensions:" msgstr "维数:" -#: ../dialogs/FindInPouDialog.py:61 -#, fuzzy +#: ../dialogs/FindInPouDialog.py:66 msgid "Direction" -msgstr "直接的" - -#: ../dialogs/BrowseLocationsDialog.py:78 -#, fuzzy +msgstr "方向" + +#: ../dialogs/BrowseLocationsDialog.py:91 msgid "Direction:" -msgstr "直接的" - -#: ../editors/DataTypeEditor.py:52 +msgstr "方向:" + +#: ../editors/DataTypeEditor.py:54 msgid "Directly" msgstr "直接的" -#: ../ProjectController.py:1512 +#: ../ProjectController.py:1860 msgid "Disconnect" msgstr "断开" -#: ../ProjectController.py:1514 +#: ../ProjectController.py:1862 msgid "Disconnect from PLC" msgstr "从PLC断开" -#: ../editors/Viewer.py:496 -#, fuzzy +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "已断开" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 msgid "Divergence" -msgstr "选择发散" +msgstr "偏差" #: ../plcopen/iec_std.csv:36 msgid "Division" msgstr "除法" -#: ../editors/FileManagementPanel.py:370 +#: ../editors/FileManagementPanel.py:152 #, python-format msgid "Do you really want to delete the file '%s'?" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#, fuzzy +msgstr "你真的想删除这个文件 '%s' ?" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Documentation" -msgstr "级联" - -#: ../PLCOpenEditor.py:351 +msgstr "文档" + +#: ../PLCOpenEditor.py:338 msgid "Done" msgstr "完成" -#: ../plcopen/structures.py:227 -msgid "" -"Down-counter\n" -"The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." -msgstr "" -"倒计时器\n" -"倒计时器用于当计数到达 0的时候,从当前值开始倒计时。" - -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Duration" msgstr "时间" -#: ../canfestival/canfestival.py:118 -#, fuzzy +#: ../canfestival/canfestival.py:165 msgid "EDS files (*.eds)|*.eds|All files|*.*" -msgstr "ST 文件 (*.st)|*.st|所有文件|*.*" - -#: ../editors/Viewer.py:510 +msgstr "EDS 文件 (*.eds)|*.eds|All files|*.*" + +#: ../editors/Viewer.py:629 msgid "Edit Block" msgstr "编辑块" -#: ../dialogs/LDElementDialog.py:41 +#: ../dialogs/LDElementDialog.py:56 msgid "Edit Coil Values" msgstr "编辑线圈值" -#: ../dialogs/LDElementDialog.py:38 +#: ../dialogs/LDElementDialog.py:54 msgid "Edit Contact Values" msgstr "编辑接触点值" #: ../dialogs/DurationEditorDialog.py:59 -#, fuzzy msgid "Edit Duration" -msgstr "编辑跃迁" - -#: ../dialogs/SFCStepDialog.py:35 +msgstr "编辑期间" + +#: ../dialogs/SFCStepDialog.py:51 msgid "Edit Step" msgstr "编辑步骤" -#: ../wxglade_hmi/wxglade_hmi.py:12 +#: ../wxglade_hmi/wxglade_hmi.py:38 msgid "Edit a WxWidgets GUI with WXGlade" msgstr "用 WXGlade 编辑一个 WxWidgets 用户图形界面" -#: ../dialogs/ActionBlockDialog.py:122 +#: ../dialogs/ActionBlockDialog.py:121 msgid "Edit action block properties" msgstr "编辑行动块属性" -#: ../dialogs/ArrayTypeDialog.py:45 -#, fuzzy +#: ../dialogs/ArrayTypeDialog.py:44 msgid "Edit array type properties" -msgstr "编辑行动块属性" - -#: ../editors/Viewer.py:2112 -#: ../editors/Viewer.py:2114 -#: ../editors/Viewer.py:2630 -#: ../editors/Viewer.py:2632 +msgstr "编辑数组类型属性" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 msgid "Edit comment" msgstr "编辑注释" -#: ../editors/FileManagementPanel.py:284 -#, fuzzy +#: ../editors/FileManagementPanel.py:66 msgid "Edit file" -msgstr "编辑C文件" +msgstr "编辑文件" #: ../controls/CustomEditableListBox.py:39 msgid "Edit item" msgstr "编辑项目" -#: ../editors/Viewer.py:2594 +#: ../editors/Viewer.py:3014 msgid "Edit jump target" msgstr "编辑跳跃目标" -#: ../ProjectController.py:1526 +#: ../ProjectController.py:1874 msgid "Edit raw IEC code added to code generated by PLCGenerator" msgstr "编辑原始的IEC代码添加至PLCGenerator生成的代码" -#: ../editors/SFCViewer.py:725 +#: ../editors/SFCViewer.py:799 msgid "Edit step name" msgstr "编辑步骤名称" -#: ../dialogs/SFCTransitionDialog.py:38 +#: ../dialogs/SFCTransitionDialog.py:52 msgid "Edit transition" msgstr "编辑跃迁" -#: ../IDEFrame.py:580 +#: ../IDEFrame.py:611 msgid "Editor ToolBar" -msgstr "" - -#: ../ProjectController.py:1013 -#, fuzzy +msgstr "编辑工具栏" + +#: ../ProjectController.py:1257 msgid "Editor selection" -msgstr "编辑跃迁" - -#: ../editors/DataTypeEditor.py:341 +msgstr "编辑选择" + +#: ../editors/DataTypeEditor.py:348 msgid "Elements :" msgstr "元素:" -#: ../IDEFrame.py:343 -#, fuzzy +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "空的" + +#: ../IDEFrame.py:365 msgid "Enable Undo/Redo" -msgstr "启用" - -#: ../Beremiz_service.py:380 +msgstr "启用 取消/重做" + +#: ../Beremiz_service.py:333 msgid "Enter a name " msgstr "输入一个名字" -#: ../Beremiz_service.py:365 +#: ../Beremiz_service.py:318 msgid "Enter a port number " msgstr "输入一个端口号" -#: ../Beremiz_service.py:355 +#: ../Beremiz_service.py:309 msgid "Enter the IP of the interface to bind" msgstr "输入界面的ip用以绑定" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Enumerated" msgstr "列举的" @@ -1590,69 +1538,48 @@ msgid "Equal to" msgstr "等于" -#: ../Beremiz_service.py:270 -#: ../Beremiz_service.py:394 -#: ../controls/VariablePanel.py:330 -#: ../controls/VariablePanel.py:678 -#: ../controls/DebugVariablePanel.py:164 -#: ../IDEFrame.py:1083 -#: ../IDEFrame.py:1672 -#: ../IDEFrame.py:1709 -#: ../IDEFrame.py:1714 -#: ../IDEFrame.py:1728 -#: ../IDEFrame.py:1733 -#: ../IDEFrame.py:2422 -#: ../Beremiz.py:1083 -#: ../PLCOpenEditor.py:358 -#: ../PLCOpenEditor.py:363 -#: ../PLCOpenEditor.py:531 -#: ../PLCOpenEditor.py:541 -#: ../editors/TextViewer.py:376 -#: ../editors/DataTypeEditor.py:543 -#: ../editors/DataTypeEditor.py:548 -#: ../editors/DataTypeEditor.py:572 -#: ../editors/DataTypeEditor.py:577 -#: ../editors/DataTypeEditor.py:587 -#: ../editors/DataTypeEditor.py:719 -#: ../editors/DataTypeEditor.py:726 -#: ../editors/Viewer.py:366 -#: ../editors/LDViewer.py:663 -#: ../editors/LDViewer.py:879 -#: ../editors/LDViewer.py:883 -#: ../editors/FileManagementPanel.py:210 -#: ../ProjectController.py:221 -#: ../dialogs/PouNameDialog.py:53 -#: ../dialogs/PouTransitionDialog.py:107 -#: ../dialogs/BrowseLocationsDialog.py:175 -#: ../dialogs/ProjectDialog.py:71 -#: ../dialogs/SFCStepNameDialog.py:59 -#: ../dialogs/ConnectionDialog.py:152 -#: ../dialogs/FBDVariableDialog.py:201 -#: ../dialogs/PouActionDialog.py:104 +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 +#: ../dialogs/PouTransitionDialog.py:107 ../dialogs/BlockPreviewDialog.py:237 +#: ../dialogs/ProjectDialog.py:74 ../dialogs/ArrayTypeDialog.py:97 +#: ../dialogs/ArrayTypeDialog.py:103 ../dialogs/PouNameDialog.py:54 +#: ../dialogs/BrowseLocationsDialog.py:218 #: ../dialogs/BrowseValuesLibraryDialog.py:83 -#: ../dialogs/PouDialog.py:132 -#: ../dialogs/SFCTransitionDialog.py:147 -#: ../dialogs/DurationEditorDialog.py:121 -#: ../dialogs/DurationEditorDialog.py:163 -#: ../dialogs/SearchInProjectDialog.py:157 -#: ../dialogs/SFCStepDialog.py:130 -#: ../dialogs/ArrayTypeDialog.py:97 -#: ../dialogs/ArrayTypeDialog.py:103 -#: ../dialogs/FBDBlockDialog.py:164 -#: ../dialogs/ForceVariableDialog.py:169 +#: ../dialogs/PouActionDialog.py:105 ../dialogs/PouDialog.py:135 +#: ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 ../PLCOpenEditor.py:430 +#: ../PLCOpenEditor.py:440 ../editors/ResourceEditor.py:436 +#: ../editors/Viewer.py:424 ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:882 ../editors/LDViewer.py:886 +#: ../editors/DataTypeEditor.py:550 ../editors/DataTypeEditor.py:555 +#: ../editors/DataTypeEditor.py:574 ../editors/DataTypeEditor.py:743 +#: ../editors/DataTypeEditor.py:750 ../editors/TextViewer.py:389 +#: ../editors/CodeFileEditor.py:762 ../ProjectController.py:372 +#: ../ProjectController.py:512 ../ProjectController.py:519 +#: ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 msgid "Error" msgstr "错误" -#: ../ProjectController.py:587 -msgid "Error : At least one configuration and one resource must be declared in PLC !\n" +#: ../ProjectController.py:789 +msgid "" +"Error : At least one configuration and one resource must be declared in PLC " +"!\n" msgstr "错误:在PLC中,必须申明至少一个配置和一个资源!\n" -#: ../ProjectController.py:579 +#: ../ProjectController.py:781 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "错误:IEC到C编译器返回 %d\n" -#: ../ProjectController.py:520 +#: ../ProjectController.py:712 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" @@ -1661,40 +1588,39 @@ "错误在ST/IL/SFC代码生成器中:\n" "%s\n" -#: ../ConfigTreeNode.py:182 +#: ../ConfigTreeNode.py:216 #, python-format msgid "Error while saving \"%s\"\n" msgstr "存储时有错误 \"%s\"\n" -#: ../canfestival/canfestival.py:122 +#: ../canfestival/canfestival.py:170 msgid "Error: Export slave failed\n" -msgstr "" - -#: ../canfestival/canfestival.py:270 +msgstr "错误:导出从站失败\n" + +#: ../canfestival/canfestival.py:371 msgid "Error: No Master generated\n" msgstr "错误:没有主控生成\n" -#: ../canfestival/canfestival.py:265 +#: ../canfestival/canfestival.py:366 msgid "Error: No PLC built\n" msgstr "错误:没有PLC构建\n" -#: ../ProjectController.py:1378 +#: ../ProjectController.py:1728 #, python-format msgid "Exception while connecting %s!\n" msgstr "连接时存在异常 %s!\n" -#: ../dialogs/FBDBlockDialog.py:95 +#: ../dialogs/FBDBlockDialog.py:120 msgid "Execution Control:" msgstr "执行控制:" -#: ../dialogs/FBDVariableDialog.py:76 -#: ../dialogs/FBDBlockDialog.py:87 +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 msgid "Execution Order:" msgstr "执行命令:" -#: ../features.py:10 +#: ../features.py:35 msgid "Experimental web based HMI" -msgstr "" +msgstr "实验性的WEB基础的HMI" #: ../plcopen/iec_std.csv:38 msgid "Exponent" @@ -1704,192 +1630,171 @@ msgid "Exponentiation" msgstr "幂" -#: ../canfestival/canfestival.py:128 +#: ../canfestival/canfestival.py:176 msgid "Export CanOpen slave to EDS file" -msgstr "" - -#: ../editors/GraphicViewer.py:144 +msgstr "导出 CANOpen 从站到 EDS 文件" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 msgid "Export graph values to clipboard" -msgstr "" - -#: ../canfestival/canfestival.py:127 +msgstr "导出图形值到剪切板" + +#: ../canfestival/canfestival.py:175 msgid "Export slave" -msgstr "" - -#: ../dialogs/FBDVariableDialog.py:69 +msgstr "导出从站" + +#: ../dialogs/FBDVariableDialog.py:90 msgid "Expression:" msgstr "表达式:" -#: ../controls/VariablePanel.py:77 +#: ../controls/VariablePanel.py:72 msgid "External" msgstr "外部的" -#: ../ProjectController.py:591 +#: ../ProjectController.py:802 msgid "Extracting Located Variables...\n" msgstr "正在提取位置变量......\n" -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "FBD" -msgstr "功能区块图" - -#: ../ProjectController.py:1445 +msgstr "功能块图" + +#: ../ProjectController.py:1791 msgid "Failed : Must build before transfer.\n" msgstr "失败:传输之前必须构建。\n" -#: ../editors/Viewer.py:405 -#: ../dialogs/LDElementDialog.py:84 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 msgid "Falling Edge" msgstr "下降沿" -#: ../plcopen/structures.py:217 -msgid "" -"Falling edge detector\n" -"The output produces a single pulse when a falling edge is detected." -msgstr "" -"下降沿检测\n" -"当下降沿被检测到时,输出便产生一个单脉冲。" - -#: ../ProjectController.py:900 +#: ../ProjectController.py:1070 msgid "Fatal : cannot get builder.\n" msgstr "致命错误:无法获取构建者。\n" -#: ../dialogs/DurationEditorDialog.py:160 -#, fuzzy, python-format +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "抓取 %s" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format msgid "Field %s hasn't a valid value!" -msgstr "\"%s\"不是有效值!" - -#: ../dialogs/DurationEditorDialog.py:162 -#, fuzzy, python-format +msgstr "领域 %s 没有一个有效值!" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format msgid "Fields %s haven't a valid value!" -msgstr "\"%s\"不是有效值!" - -#: ../editors/FileManagementPanel.py:209 -#, fuzzy, python-format +msgstr "领域 %s 没有一个有效值!" + +#: ../controls/FolderTree.py:216 +#, python-format msgid "File '%s' already exists!" -msgstr "\"%s\"编程组织单元已经存在!" - -#: ../IDEFrame.py:353 -#: ../dialogs/FindInPouDialog.py:30 -#: ../dialogs/FindInPouDialog.py:99 +msgstr "文件 '%s' 已经存在!" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 msgid "Find" -msgstr "" - -#: ../IDEFrame.py:355 +msgstr "查找" + +#: ../IDEFrame.py:377 msgid "Find Next" -msgstr "" - -#: ../IDEFrame.py:357 +msgstr "查找下一个" + +#: ../IDEFrame.py:379 msgid "Find Previous" -msgstr "" +msgstr "查找前一个" #: ../plcopen/iec_std.csv:90 msgid "Find position" -msgstr "定位" - -#: ../dialogs/FindInPouDialog.py:51 +msgstr "查找位置" + +#: ../dialogs/FindInPouDialog.py:55 msgid "Find:" -msgstr "" - -#: ../connectors/PYRO/__init__.py:125 +msgstr "查找:" + +#: ../connectors/PYRO/__init__.py:163 msgid "Force runtime reload\n" -msgstr "强制重新运行\n" - -#: ../controls/DebugVariablePanel.py:295 -#: ../editors/Viewer.py:1353 +msgstr "强制运行时重新加载\n" + +#: ../editors/Viewer.py:1600 msgid "Force value" -msgstr "" - -#: ../dialogs/ForceVariableDialog.py:152 +msgstr "强制值" + +#: ../dialogs/ForceVariableDialog.py:162 msgid "Forcing Variable Value" -msgstr "" - -#: ../dialogs/PouTransitionDialog.py:97 -#: ../dialogs/ProjectDialog.py:70 -#: ../dialogs/PouActionDialog.py:94 -#: ../dialogs/PouDialog.py:114 -#: ../dialogs/SFCTransitionDialog.py:147 +msgstr "强制变量值" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 #, python-format msgid "Form isn't complete. %s must be filled!" msgstr "形式不完整。%s 必须被填补完整!" -#: ../dialogs/ConnectionDialog.py:142 -#: ../dialogs/FBDBlockDialog.py:154 +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 msgid "Form isn't complete. Name must be filled!" msgstr "形式不完整。%s 名字必须填!" -#: ../dialogs/SearchInProjectDialog.py:145 -#, fuzzy -msgid "Form isn't complete. Pattern to search must be filled!" -msgstr "形式不完整。%s 名字必须填!" - -#: ../dialogs/FBDBlockDialog.py:152 +#: ../dialogs/FBDBlockDialog.py:232 msgid "Form isn't complete. Valid block type must be selected!" msgstr "形式不完整。%s 有效的块类型必须被选择!" -#: ../dialogs/FindInPouDialog.py:67 +#: ../dialogs/FindInPouDialog.py:72 msgid "Forward" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:44 +msgstr "向前的" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 msgid "Function" msgstr "功能" -#: ../IDEFrame.py:329 -#, fuzzy +#: ../IDEFrame.py:349 msgid "Function &Block" -msgstr "功能块" - -#: ../IDEFrame.py:1969 -#: ../dialogs/SearchInProjectDialog.py:45 +msgstr "功能 &块" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 msgid "Function Block" msgstr "功能块" -#: ../controls/VariablePanel.py:741 +#: ../controls/VariablePanel.py:854 msgid "Function Block Types" msgstr "功能块类型" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "Function Blocks" msgstr "功能块" -#: ../editors/Viewer.py:236 +#: ../editors/Viewer.py:249 msgid "Function Blocks can't be used in Functions!" msgstr "功能块不能用于功能中!" -#: ../editors/Viewer.py:238 -msgid "Function Blocks can't be used in Transitions!" -msgstr "功能块不能用于跃迁中" - -#: ../PLCControler.py:2055 +#: ../PLCControler.py:2343 #, python-format msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" msgstr "功能块 \"%s\" 不能用于功能中!" -#: ../PLCControler.py:94 +#: ../PLCControler.py:97 msgid "Functions" msgstr "功能" -#: ../PLCOpenEditor.py:138 -#, fuzzy +#: ../PLCOpenEditor.py:117 msgid "Generate Program" -msgstr "生成程序\tCTRL+G" - -#: ../ProjectController.py:510 +msgstr "生成程序" + +#: ../ProjectController.py:703 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "生成软PLC IEC-61131 ST/IL/SFC 代码......\n" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid "Global" msgstr "全球的" -#: ../editors/GraphicViewer.py:131 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 msgid "Go to current value" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:173 +msgstr "定位当前值" + +#: ../controls/ProjectPropertiesPanel.py:174 msgid "Graphics" msgstr "图形" @@ -1901,115 +1806,110 @@ msgid "Greater than or equal to" msgstr "大于或等于" -#: ../controls/ProjectPropertiesPanel.py:134 +#: ../controls/ProjectPropertiesPanel.py:135 msgid "Grid Resolution:" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:120 +msgstr "栅格分辨率:" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "HTTP 界面端口:" + +#: ../controls/ProjectPropertiesPanel.py:121 msgid "Height:" msgstr "高度:" -#: ../editors/FileManagementPanel.py:303 -#, fuzzy +#: ../editors/FileManagementPanel.py:85 msgid "Home Directory:" -msgstr "直接的" - -#: ../controls/ProjectPropertiesPanel.py:150 +msgstr "主目录" + +#: ../controls/ProjectPropertiesPanel.py:151 msgid "Horizontal:" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:44 +msgstr "水平:" + +#: ../dialogs/DurationEditorDialog.py:45 msgid "Hours:" -msgstr "" - -#: ../plcopen/structures.py:279 -msgid "" -"Hysteresis\n" -"The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." -msgstr "" -"滞后\n" -"滞后功能块提供一个被2个浮点(REAL)的差异所驱动的布尔型滞后输出,2个浮点即输入的XIN1和XIN2。" - -#: ../ProjectController.py:827 -msgid "IEC-61131-3 code generation failed !\n" -msgstr "IEC-61131-3代码生成失败!\n" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +msgstr "小时:" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "IL" -msgstr "指令集" - -#: ../Beremiz_service.py:356 -#: ../Beremiz_service.py:357 +msgstr "指令表编程语言" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "IP" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 msgid "IP is not valid!" -msgstr "Ip无效!" - -#: ../svgui/svgui.py:17 -#: ../svgui/svgui.py:18 +msgstr "IP 无效!" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 msgid "Import SVG" -msgstr "" - -#: ../controls/VariablePanel.py:76 -#: ../dialogs/FBDVariableDialog.py:34 +msgstr "导入 SVG" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 msgid "InOut" msgstr "输入输出" -#: ../controls/VariablePanel.py:263 -#, python-format -msgid "Incompatible data types between \"%s\" and \"%s\"" -msgstr " \"%s\" 和 \"%s\" 数据类型不相容" - -#: ../controls/VariablePanel.py:274 -#, python-format -msgid "Incompatible size of data between \"%s\" and \"%s\"" -msgstr " \"%s\" 和 \"%s\" 数据大小不相容" - -#: ../controls/VariablePanel.py:270 +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "不活动" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr " \"{a1}\" 和 \"{a2}\" 数据类型不相容" + +#: ../controls/VariablePanel.py:282 #, python-format msgid "Incompatible size of data between \"%s\" and \"BOOL\"" -msgstr " \"%s\" 和 \"BOOL\" 数据类型不相容" - -#: ../dialogs/ActionBlockDialog.py:37 +msgstr " \"%s\" 和 \"BOOL\" 数据尺寸不相容" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr " \"{a1}\" 和 \"{a2}\" 数据尺寸不相容" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Indicator" msgstr "指示器" -#: ../editors/Viewer.py:492 -#, fuzzy +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "初始的" + +#: ../editors/Viewer.py:611 msgid "Initial Step" -msgstr "初始值" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 +msgstr "初始的步" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 msgid "Initial Value" msgstr "初始值" -#: ../editors/DataTypeEditor.py:178 -#: ../editors/DataTypeEditor.py:209 -#: ../editors/DataTypeEditor.py:265 -#: ../editors/DataTypeEditor.py:303 +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 msgid "Initial Value:" msgstr "初始值:" -#: ../svgui/svgui.py:21 +#: ../svgui/svgui.py:48 msgid "Inkscape" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:41 -#: ../dialogs/SFCTransitionDialog.py:66 -#: ../dialogs/SFCTransitionDialog.py:137 +msgstr "Inkscape" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 msgid "Inline" msgstr "在线" -#: ../controls/VariablePanel.py:76 -#: ../dialogs/BrowseLocationsDialog.py:36 -#: ../dialogs/FBDVariableDialog.py:33 -#: ../dialogs/SFCStepDialog.py:61 +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Input" msgstr "输入" -#: ../dialogs/FBDBlockDialog.py:78 +#: ../dialogs/FBDBlockDialog.py:96 msgid "Inputs:" msgstr "输入:" @@ -2017,125 +1917,116 @@ msgid "Insertion (into)" msgstr "插入" -#: ../plcopen/plcopen.py:1833 +#: ../plcopen/plcopen.py:1696 #, python-format msgid "Instance with id %d doesn't exist!" msgstr "有id的实例 %d 尚不存在!" -#: ../editors/ResourceEditor.py:247 +#: ../editors/ResourceEditor.py:264 msgid "Instances:" msgstr "实例:" -#: ../plcopen/structures.py:259 -msgid "" -"Integral\n" -"The integral function block integrates the value of input XIN over time." -msgstr "" -"积分\n" -"积分功能随着时间推移而集成输入的XIN的值。" - -#: ../controls/VariablePanel.py:75 +#: ../controls/VariablePanel.py:70 msgid "Interface" msgstr "界面" -#: ../editors/ResourceEditor.py:71 +#: ../editors/ResourceEditor.py:72 msgid "Interrupt" -msgstr "" - -#: ../editors/ResourceEditor.py:67 +msgstr "中断" + +#: ../editors/ResourceEditor.py:68 msgid "Interval" msgstr "区间" -#: ../PLCControler.py:2032 -#: ../PLCControler.py:2070 +#: ../PLCControler.py:2331 msgid "Invalid plcopen element(s)!!!" msgstr "无效的plcopen元素!!!" -#: ../canfestival/config_utils.py:376 -#: ../canfestival/config_utils.py:637 -#, python-format -msgid "Invalid type \"%s\"-> %d != %d for location\"%s\"" -msgstr "无效类型 \"%s\"-> %d != %d 用于位置 \"%s\"" - -#: ../dialogs/ForceVariableDialog.py:167 -#, fuzzy, python-format -msgid "Invalid value \"%s\" for \"%s\" variable!" -msgstr "无效值 \"%s\" 为调试变量" - -#: ../controls/DebugVariablePanel.py:153 -#: ../controls/DebugVariablePanel.py:156 +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "无效类型 \"{a1}\"-> {a2} != {a3} 对于定位 \"{a4}\"" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "无效类型 \"{a1}\"-> {a2} != {a3} 对于定位 \"{a4}\"" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 #, python-format msgid "Invalid value \"%s\" for debug variable" msgstr "无效值 \"%s\" 为调试变量" -#: ../controls/VariablePanel.py:244 -#: ../controls/VariablePanel.py:247 -#, fuzzy, python-format +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format msgid "Invalid value \"%s\" for variable grid element" -msgstr "无效值 \"%s\" 为调试变量" - -#: ../editors/Viewer.py:221 -#: ../editors/Viewer.py:224 +msgstr "无效值 \"%s\" 对于变量网格元素" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 #, python-format msgid "Invalid value \"%s\" for viewer block" msgstr "无效值 \"%s\" 在视窗块" +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "无效值 \"{a1}\" 对于 \"{a2}\" 变量!" + #: ../dialogs/DurationEditorDialog.py:121 msgid "" "Invalid value!\n" "You must fill a numeric value." msgstr "" - -#: ../editors/Viewer.py:497 +"无效值!\n" +"你必须填入一个数字值。" + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 msgid "Jump" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +msgstr "跳转" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "LD" msgstr "梯级图" -#: ../editors/LDViewer.py:215 -#: ../editors/LDViewer.py:231 +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 #, python-format msgid "Ladder element with id %d is on more than one rung." msgstr "有id的梯形元素 %d 不止在一个梯级上。" -#: ../dialogs/PouTransitionDialog.py:86 -#: ../dialogs/PouActionDialog.py:83 -#: ../dialogs/PouDialog.py:102 +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 msgid "Language" msgstr "语言" -#: ../controls/ProjectPropertiesPanel.py:186 +#: ../controls/ProjectPropertiesPanel.py:187 msgid "Language (optional):" msgstr "语言(选填):" -#: ../dialogs/PouTransitionDialog.py:60 -#: ../dialogs/PouActionDialog.py:56 -#: ../dialogs/PouDialog.py:71 +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 msgid "Language:" msgstr "语言:" -#: ../ProjectController.py:1451 +#: ../ProjectController.py:1797 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "最新构建已经与当前目标匹配。正在传输中......\n" -#: ../Beremiz_service.py:324 +#: ../Beremiz_service.py:273 msgid "Launch WX GUI inspector" msgstr "启动 WX GUI 检查员" -#: ../Beremiz_service.py:323 +#: ../Beremiz_service.py:272 msgid "Launch a live Python shell" msgstr "启动一个活的Python Shell" -#: ../editors/Viewer.py:428 +#: ../editors/Viewer.py:544 msgid "Left" msgstr "左" -#: ../dialogs/LDPowerRailDialog.py:55 +#: ../dialogs/LDPowerRailDialog.py:63 msgid "Left PowerRail" msgstr "左电源导轨" @@ -2151,204 +2042,176 @@ msgid "Less than or equal to" msgstr "小于或等于" -#: ../IDEFrame.py:600 +#: ../IDEFrame.py:631 msgid "Library" -msgstr "图书馆" +msgstr "库" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "许可" #: ../plcopen/iec_std.csv:73 msgid "Limitation" msgstr "限制" -#: ../targets/toolchain_gcc.py:142 +#: ../targets/toolchain_gcc.py:202 msgid "Linking :\n" msgstr "链接:\n" -#: ../controls/VariablePanel.py:77 -#: ../dialogs/DiscoveryDialog.py:110 -#, fuzzy +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 msgid "Local" -msgstr "" -"#-#-#-#-# Beremiz_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"本地\n" -"#-#-#-#-# PLCOpenEditor_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"位置" - -#: ../ProjectController.py:1353 -#, fuzzy +msgstr "本地" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "本地入口" + +#: ../ProjectController.py:1703 msgid "Local service discovery failed!\n" -msgstr "服务探索" - -#: ../controls/VariablePanel.py:58 +msgstr "本地服务探索失败!\n" + +#: ../controls/VariablePanel.py:53 msgid "Location" msgstr "位置" -#: ../dialogs/BrowseLocationsDialog.py:61 -#, fuzzy +#: ../dialogs/BrowseLocationsDialog.py:72 msgid "Locations available:" -msgstr "该选项尚未可用!" - -#: ../Beremiz.py:393 -msgid "Log Console" -msgstr "控制台日志" +msgstr "存在的定位:" #: ../plcopen/iec_std.csv:25 msgid "Logarithm to base 10" msgstr "底数10的对数" -#: ../connectors/PYRO/__init__.py:55 +#: ../connectors/PYRO/__init__.py:94 #, python-format msgid "MDNS resolution failure for '%s'\n" -msgstr "" - -#: ../canfestival/SlaveEditor.py:37 -#: ../canfestival/NetworkEditor.py:67 -#, fuzzy +msgstr "MDNS 解析度失败因为 '%s'\n" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 msgid "Map Variable" -msgstr "变量" - -#: ../features.py:6 +msgstr "映射变量" + +#: ../features.py:31 msgid "Map located variables over CANopen" -msgstr "" - -#: ../canfestival/NetworkEditor.py:89 -#, fuzzy +msgstr "通过CANopen映射位置变量" + +#: ../canfestival/NetworkEditor.py:106 msgid "Master" -msgstr "显示主控" - -#: ../ConfigTreeNode.py:480 -#, fuzzy, python-format -msgid "Max count (%d) reached for this confnode of type %s " -msgstr "最大计数 (%d) 到达" +msgstr "主站" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "最大计数 ({a1}) 达到了对于类型 {a2} 这个confnode" #: ../plcopen/iec_std.csv:71 msgid "Maximum" msgstr "最大值" -#: ../editors/DataTypeEditor.py:232 +#: ../editors/DataTypeEditor.py:239 msgid "Maximum:" msgstr "最大值:" -#: ../dialogs/BrowseLocationsDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Memory" -msgstr "" - -#: ../IDEFrame.py:568 -#, fuzzy +msgstr "存储" + +#: ../IDEFrame.py:599 msgid "Menu ToolBar" -msgstr "工具条" - -#: ../dialogs/DurationEditorDialog.py:48 +msgstr "菜单工具栏" + +#: ../dialogs/DurationEditorDialog.py:49 msgid "Microseconds:" -msgstr "" - -#: ../editors/Viewer.py:433 +msgstr "微秒:" + +#: ../editors/Viewer.py:549 msgid "Middle" msgstr "中间" -#: ../dialogs/DurationEditorDialog.py:47 +#: ../dialogs/DurationEditorDialog.py:48 msgid "Milliseconds:" -msgstr "" +msgstr "毫秒:" #: ../plcopen/iec_std.csv:72 msgid "Minimum" msgstr "最小值" -#: ../editors/DataTypeEditor.py:219 +#: ../editors/DataTypeEditor.py:226 msgid "Minimum:" msgstr "最小值:" -#: ../dialogs/DurationEditorDialog.py:45 +#: ../dialogs/DurationEditorDialog.py:46 msgid "Minutes:" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:210 +msgstr "分:" + +#: ../controls/ProjectPropertiesPanel.py:211 msgid "Miscellaneous" msgstr "其他" -#: ../dialogs/LDElementDialog.py:59 +#: ../dialogs/LDElementDialog.py:63 msgid "Modifier:" msgstr "改动:" -#: ../PLCGenerator.py:703 -#: ../PLCGenerator.py:936 -#, python-format -msgid "More than one connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "发现不止一个连接器符合 \"%s\" 延续在 \"%s\" POU中" - -#: ../dialogs/ActionBlockDialog.py:141 -#, fuzzy +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "" +"More than one connector found corresponding to \"{a1}\" continuation in " +"\"{a2}\" POU" +msgstr "多个连接器发现对应在 \"{a2}\" POU中 \"{a1}\" 延续" + +#: ../dialogs/ActionBlockDialog.py:140 msgid "Move action down" -msgstr "下移" - -#: ../dialogs/ActionBlockDialog.py:140 -#, fuzzy +msgstr "下移动作" + +#: ../dialogs/ActionBlockDialog.py:139 msgid "Move action up" -msgstr "上移" - -#: ../controls/DebugVariablePanel.py:185 -#, fuzzy -msgid "Move debug variable down" -msgstr "未找到输出值" - -#: ../controls/DebugVariablePanel.py:184 -#, fuzzy -msgid "Move debug variable up" -msgstr "未找到输出值" +msgstr "上移动作" #: ../controls/CustomEditableListBox.py:43 msgid "Move down" msgstr "下移" -#: ../editors/DataTypeEditor.py:348 -#, fuzzy +#: ../editors/DataTypeEditor.py:355 msgid "Move element down" -msgstr "下移" - -#: ../editors/DataTypeEditor.py:347 -#, fuzzy +msgstr "下移元素" + +#: ../editors/DataTypeEditor.py:354 msgid "Move element up" -msgstr "上移" - -#: ../editors/ResourceEditor.py:254 -#, fuzzy +msgstr "上移元素" + +#: ../editors/ResourceEditor.py:271 msgid "Move instance down" -msgstr "下移" - -#: ../editors/ResourceEditor.py:253 -#, fuzzy +msgstr "下移实例" + +#: ../editors/ResourceEditor.py:270 msgid "Move instance up" -msgstr "上移" - -#: ../editors/ResourceEditor.py:225 -#, fuzzy +msgstr "上移实例" + +#: ../editors/ResourceEditor.py:242 msgid "Move task down" -msgstr "下移" - -#: ../editors/ResourceEditor.py:224 -#, fuzzy +msgstr "下移任务" + +#: ../editors/ResourceEditor.py:241 msgid "Move task up" -msgstr "上移" - -#: ../IDEFrame.py:75 -#: ../IDEFrame.py:90 -#: ../IDEFrame.py:120 -#: ../IDEFrame.py:161 +msgstr "上移任务" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 msgid "Move the view" -msgstr "" +msgstr "移动视图" #: ../controls/CustomEditableListBox.py:42 msgid "Move up" msgstr "上移" -#: ../controls/VariablePanel.py:381 -#, fuzzy +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 msgid "Move variable down" -msgstr "下移" - -#: ../controls/VariablePanel.py:380 -#, fuzzy +msgstr "下移变量" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 msgid "Move variable up" -msgstr "上移" +msgstr "上移变量" #: ../plcopen/iec_std.csv:74 msgid "Multiplexer (select 1 of N)" @@ -2358,28 +2221,26 @@ msgid "Multiplication" msgstr "乘法" -#: ../editors/FileManagementPanel.py:301 -#, fuzzy +#: ../editors/FileManagementPanel.py:83 msgid "My Computer:" -msgstr "编译" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 -#: ../editors/ResourceEditor.py:67 -#: ../editors/ResourceEditor.py:76 +msgstr "我的计算机:" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NAME" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Name" msgstr "名字" -#: ../Beremiz_service.py:381 +#: ../Beremiz_service.py:334 msgid "Name must not be null!" msgstr "名称不能为空!" -#: ../dialogs/ConnectionDialog.py:65 -#: ../dialogs/FBDVariableDialog.py:89 -#: ../dialogs/LDElementDialog.py:88 -#: ../dialogs/SFCStepDialog.py:51 -#: ../dialogs/FBDBlockDialog.py:70 +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 msgid "Name:" msgstr "名字:" @@ -2387,46 +2248,46 @@ msgid "Natural logarithm" msgstr "自然对数" -#: ../editors/Viewer.py:403 -#: ../dialogs/LDElementDialog.py:67 +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 msgid "Negated" msgstr "否定" -#: ../Beremiz.py:307 -#: ../Beremiz.py:342 -#: ../PLCOpenEditor.py:125 -#: ../PLCOpenEditor.py:167 +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "Nevow Web 服务失败。 " + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "Nevow/Athena 导入失败:" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 msgid "New" -msgstr "" +msgstr "新建" #: ../controls/CustomEditableListBox.py:40 msgid "New item" -msgstr "建立项目" - -#: ../editors/Viewer.py:402 +msgstr "新建项目" + +#: ../editors/Viewer.py:518 msgid "No Modifier" msgstr "无改动" -#: ../PLCControler.py:2929 -msgid "No PLC project found" -msgstr "未找到PLC项目" - -#: ../ProjectController.py:1478 +#: ../ProjectController.py:1826 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "没有PLC可传输(构建是否成功?)\n" -#: ../PLCGenerator.py:1321 +#: ../PLCGenerator.py:1631 #, python-format msgid "No body defined in \"%s\" POU" msgstr "在 \"%s\" POU 中没有任何东西被定义" -#: ../PLCGenerator.py:722 -#: ../PLCGenerator.py:945 -#, python-format -msgid "No connector found corresponding to \"%s\" continuation in \"%s\" POU" -msgstr "未发现连接器符合 \"%s\" 连续在 \"%s\" POU中" - -#: ../PLCOpenEditor.py:370 +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "没有连接器发现对应在 \"{a2}\" POU中 \"{a1}\" 延续" + +#: ../PLCOpenEditor.py:357 msgid "" "No documentation available.\n" "Coming soon." @@ -2434,77 +2295,69 @@ "没有文件可用。\n" "稍候" -#: ../PLCGenerator.py:744 +#: ../PLCGenerator.py:829 #, python-format msgid "No informations found for \"%s\" block" -msgstr "" - -#: ../plcopen/structures.py:167 -msgid "No output variable found" -msgstr "未找到输出值" - -#: ../Beremiz_service.py:394 -msgid "No running PLC" -msgstr "没有正在运行的PLC" +msgstr "对于 \"%s\" 块没有信息发现" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "" +"No output {a1} variable found in block {a2} in POU {a3}. Connection must be " +"broken" +msgstr "无输出{a1}变量发现在 {a3} POU的 {a2} 块。 连接必须断开" #: ../controls/SearchResultPanel.py:169 msgid "No search results available." -msgstr "" - -#: ../svgui/svgui.py:98 +msgstr "没有存在的搜索结果。" + +#: ../svgui/svgui.py:134 #, python-format msgid "No such SVG file: %s\n" msgstr "没有这样的SVG文件:%s\n" -#: ../canfestival/config_utils.py:632 -#, python-format -msgid "No such index/subindex (%x,%x) (variable %s)" -msgstr "没有这样的索引/子索引 (%x,%x) (variable %s)" - -#: ../canfestival/config_utils.py:361 -#, python-format -msgid "No such index/subindex (%x,%x) in ID : %d (variable %s)" -msgstr "没有这样的索引/子索引 (%x,%x) in ID : %d (variable %s)" +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "没有如此 标签/子标签 ({a1},{a2}) (变量 {a3})" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "没有如此 标签/子标签 ({a1},{a2}) 在ID : {a3} (变量 {a4})" #: ../dialogs/BrowseValuesLibraryDialog.py:83 msgid "No valid value selected!" -msgstr "" - -#: ../PLCGenerator.py:1319 +msgstr "没有有效的值被选择!" + +#: ../PLCGenerator.py:1629 #, python-format msgid "No variable defined in \"%s\" POU" msgstr "无变量被定义在 \"%s\" POU" -#: ../canfestival/SlaveEditor.py:49 -#: ../canfestival/NetworkEditor.py:79 -#, fuzzy -msgid "Node infos" -msgstr "类型信息:" - -#: ../canfestival/config_utils.py:354 -#, python-format -msgid "Non existing node ID : %d (variable %s)" -msgstr "不存在节点ID:%d (variable %s)" - -#: ../controls/VariablePanel.py:69 -#, fuzzy +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "不存在节点 ID :{a1} (variable {a2})" + +#: ../controls/VariablePanel.py:64 msgid "Non-Retain" -msgstr "保持" - -#: ../dialogs/LDElementDialog.py:62 +msgstr "非保持" + +#: ../dialogs/LDElementDialog.py:75 msgid "Normal" msgstr "正常" -#: ../canfestival/config_utils.py:383 -#, python-format -msgid "Not PDO mappable variable : '%s' (ID:%d,Idx:%x,sIdx:%x))" -msgstr "不是PDO填图变量: '%s' (ID:%d,Idx:%x,sIdx:%x))" +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "不是PDO可映射变量:'{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" #: ../plcopen/iec_std.csv:80 msgid "Not equal to" msgstr "不等于" -#: ../dialogs/SFCDivergenceDialog.py:80 +#: ../dialogs/SFCDivergenceDialog.py:89 msgid "Number of sequences:" msgstr "序列号:" @@ -2512,462 +2365,465 @@ msgid "Numerical" msgstr "数学式" -#: ../plcopen/structures.py:247 -msgid "" -"Off-delay timer\n" -"The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." -msgstr "" -"关闭延迟计时器\n" -"关闭延迟计时器可用于延迟设置一个假性输出,固定期限后一个输入变成假。" - -#: ../plcopen/structures.py:242 -msgid "" -"On-delay timer\n" -"The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." -msgstr "" -"开启延迟计时器\n" -"开启延时计时器可用于延迟设置一个真性输出,固定期限后一个输入成为真。" - -#: ../dialogs/SearchInProjectDialog.py:93 -#, fuzzy +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "在改变中" + +#: ../dialogs/SearchInProjectDialog.py:84 msgid "Only Elements" -msgstr "元素:" - -#: ../Beremiz.py:309 -#: ../Beremiz.py:343 -#: ../PLCOpenEditor.py:127 -#: ../PLCOpenEditor.py:168 +msgstr "唯一元素" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 msgid "Open" -msgstr "" - -#: ../svgui/svgui.py:107 +msgstr "打开" + +#: ../svgui/svgui.py:143 msgid "Open Inkscape" -msgstr "" - -#: ../ProjectController.py:1530 +msgstr "打开 Inkscape" + +#: ../version.py:77 +msgid "" +"Open Source framework for automation, implemented IEC 61131 IDE with " +"constantly growing set of extensions and flexible PLC runtime." +msgstr "开源自动化框架,实现不断增长的扩展集和弹性的PLC运行时的IEC 61131-3 IDE" + +#: ../ProjectController.py:1878 msgid "Open a file explorer to manage project files" -msgstr "" - -#: ../wxglade_hmi/wxglade_hmi.py:109 +msgstr "打开一个文件浏览器来管理项目文件" + +#: ../wxglade_hmi/wxglade_hmi.py:155 msgid "Open wxGlade" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#, fuzzy +msgstr "打开 wxGlade" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Option" -msgstr "行动" - -#: ../dialogs/FindInPouDialog.py:76 -#, fuzzy +msgstr "选项" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 msgid "Options" -msgstr "行动" - -#: ../controls/ProjectPropertiesPanel.py:97 +msgstr "选项" + +#: ../controls/ProjectPropertiesPanel.py:98 msgid "Organization (optional):" msgstr "组织(选填):" -#: ../canfestival/SlaveEditor.py:47 -#: ../canfestival/NetworkEditor.py:77 +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 msgid "Other Profile" -msgstr "" - -#: ../controls/VariablePanel.py:76 -#: ../dialogs/BrowseLocationsDialog.py:37 -#: ../dialogs/FBDVariableDialog.py:35 -#: ../dialogs/SFCStepDialog.py:65 +msgstr "其他配置" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 msgid "Output" msgstr "输出" -#: ../canfestival/SlaveEditor.py:36 -#: ../canfestival/NetworkEditor.py:66 +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 msgid "PDO Receive" -msgstr "" - -#: ../canfestival/SlaveEditor.py:35 -#: ../canfestival/NetworkEditor.py:65 -#, fuzzy +msgstr "PDO 接收" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 msgid "PDO Transmit" -msgstr "添加跃迁" - -#: ../plcopen/structures.py:269 -msgid "" -"PID\n" -"The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." -msgstr "" -"PID\n" -"PID(比例,积分,导数)功能块为闭循环控制提供经典的三阶段控制器。" - -#: ../targets/toolchain_gcc.py:107 +msgstr "PDO 传输" + +#: ../targets/toolchain_gcc.py:167 msgid "PLC :\n" msgstr "PLC:\n" -#: ../ProjectController.py:1096 -#: ../ProjectController.py:1398 -#, python-format -msgid "PLC is %s\n" -msgstr "PLC 是 %s\n" - -#: ../PLCOpenEditor.py:313 -#: ../PLCOpenEditor.py:391 +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "PLC 记录" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "PLC 代码生成失败!\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "PLC 是空的或者已经被启动。" + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "PLC 没有被启动。" + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" +"PLC语法错误在行 {a1}:\n" +"{a2}" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" msgstr "PLCOpen 文件 (*.xml)|*.xml|所有文件|*.*" -#: ../PLCOpenEditor.py:175 -#: ../PLCOpenEditor.py:231 +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 msgid "PLCOpenEditor" msgstr "PLCOpen编辑器" -#: ../dialogs/PouDialog.py:98 +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" +"PLCOpenEditor是Beremiz项目的一部分。\n" +"\n" +"Beremiz是一个" + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "端口" + +#: ../dialogs/PouDialog.py:101 msgid "POU Name" msgstr "POU 名字" -#: ../dialogs/PouDialog.py:56 +#: ../dialogs/PouDialog.py:58 msgid "POU Name:" msgstr "POU 名字:" -#: ../dialogs/PouDialog.py:100 +#: ../dialogs/PouDialog.py:103 msgid "POU Type" msgstr "POU类型" -#: ../dialogs/PouDialog.py:63 +#: ../dialogs/PouDialog.py:65 msgid "POU Type:" msgstr "POU 类型:" -#: ../Beremiz.py:322 -#: ../PLCOpenEditor.py:141 +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "PYRO 连接到 URI : %s\n" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "PYRO使用认证在 '%s' \n" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 msgid "Page Setup" msgstr "页面设置" -#: ../controls/ProjectPropertiesPanel.py:110 +#: ../controls/ProjectPropertiesPanel.py:111 msgid "Page Size (optional):" msgstr "页面大小(选填):" -#: ../PLCOpenEditor.py:476 +#: ../IDEFrame.py:2613 #, python-format msgid "Page: %d" msgstr "页:%d" -#: ../controls/PouInstanceVariablesPanel.py:41 -#, fuzzy +#: ../controls/PouInstanceVariablesPanel.py:124 msgid "Parent instance" -msgstr "删除实例" - -#: ../IDEFrame.py:350 -#: ../IDEFrame.py:402 -#: ../editors/Viewer.py:537 +msgstr "父实例" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 msgid "Paste" -msgstr "" - -#: ../IDEFrame.py:1900 -#, fuzzy +msgstr "粘贴" + +#: ../IDEFrame.py:1868 msgid "Paste POU" -msgstr "请输入POU名" - -#: ../dialogs/SearchInProjectDialog.py:64 +msgstr "粘贴POU" + +#: ../dialogs/SearchInProjectDialog.py:56 msgid "Pattern to search:" -msgstr "" - -#: ../dialogs/LDPowerRailDialog.py:64 +msgstr "检索模式:" + +#: ../dialogs/LDPowerRailDialog.py:74 msgid "Pin number:" msgstr "插脚数:" -#: ../editors/Viewer.py:2289 -#: ../editors/Viewer.py:2594 -#: ../editors/SFCViewer.py:696 +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 msgid "Please choose a target" msgstr "请选择一个目标" -#: ../editors/Viewer.py:2112 -#: ../editors/Viewer.py:2114 -#: ../editors/Viewer.py:2630 -#: ../editors/Viewer.py:2632 +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "请输入一个块名称" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 msgid "Please enter comment text" msgstr "请输入注释文本" -#: ../editors/SFCViewer.py:359 -#: ../editors/SFCViewer.py:381 -#: ../editors/SFCViewer.py:725 +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 msgid "Please enter step name" msgstr "请输入步骤名称" -#: ../dialogs/ForceVariableDialog.py:153 -#, fuzzy, python-format +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "请输入文本" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format msgid "Please enter value for a \"%s\" variable:" -msgstr "请为插件输入一个名字:" - -#: ../Beremiz_service.py:366 +msgstr "请输入值对于一个 \"%s\" 变量:" + +#: ../Beremiz_service.py:319 msgid "Port number must be 0 <= port <= 65535!" msgstr "端口号必须为 0 <= 端口号 <= 65535!" -#: ../Beremiz_service.py:366 +#: ../Beremiz_service.py:319 msgid "Port number must be an integer!" msgstr "端口号必须是整数!" -#: ../editors/GraphicViewer.py:105 -msgid "Position:" -msgstr "定位:" - -#: ../editors/Viewer.py:476 -#, fuzzy +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 msgid "Power Rail" -msgstr "左电源导轨" - -#: ../dialogs/LDPowerRailDialog.py:36 +msgstr "电源导轨" + +#: ../dialogs/LDPowerRailDialog.py:51 msgid "Power Rail Properties" msgstr "电源导轨属性" -#: ../Beremiz.py:324 -#: ../PLCOpenEditor.py:143 +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 msgid "Preview" msgstr "打印预览" -#: ../dialogs/SFCDivergenceDialog.py:93 -#: ../dialogs/LDPowerRailDialog.py:78 -#: ../dialogs/ConnectionDialog.py:78 -#: ../dialogs/FBDVariableDialog.py:97 -#: ../dialogs/SFCTransitionDialog.py:96 -#: ../dialogs/LDElementDialog.py:101 -#: ../dialogs/SFCStepDialog.py:79 -#: ../dialogs/FBDBlockDialog.py:103 +#: ../dialogs/BlockPreviewDialog.py:57 msgid "Preview:" msgstr "预览:" -#: ../Beremiz.py:326 -#: ../Beremiz.py:346 -#: ../PLCOpenEditor.py:145 -#: ../PLCOpenEditor.py:171 +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 msgid "Print" msgstr "打印" -#: ../IDEFrame.py:1155 +#: ../IDEFrame.py:1079 msgid "Print preview" msgstr "打印预览" -#: ../editors/ResourceEditor.py:67 +#: ../editors/ResourceEditor.py:68 msgid "Priority" msgstr "优先" -#: ../dialogs/SFCTransitionDialog.py:83 +#: ../dialogs/SFCTransitionDialog.py:90 msgid "Priority:" msgstr "优先:" -#: ../controls/ProjectPropertiesPanel.py:80 +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "故障启动PLC:错误 %d" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "产品名称" + +#: ../controls/ProjectPropertiesPanel.py:81 msgid "Product Name (required):" msgstr "产品名字(必填):" -#: ../controls/ProjectPropertiesPanel.py:82 +#: ../controls/ProjectPropertiesPanel.py:83 msgid "Product Release (optional):" msgstr "产品发布(选填):" -#: ../controls/ProjectPropertiesPanel.py:81 +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "产品版本" + +#: ../controls/ProjectPropertiesPanel.py:82 msgid "Product Version (required):" msgstr "产品版本(必填):" -#: ../IDEFrame.py:1972 -#: ../dialogs/SearchInProjectDialog.py:46 +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 msgid "Program" msgstr "程序" -#: ../PLCOpenEditor.py:360 +#: ../PLCOpenEditor.py:347 msgid "Program was successfully generated!" msgstr "该编程成功生成文件!" -#: ../PLCControler.py:95 +#: ../PLCControler.py:98 msgid "Programs" msgstr "程序" -#: ../editors/Viewer.py:230 +#: ../editors/Viewer.py:243 msgid "Programs can't be used by other POUs!" msgstr "程序不能被其它POU使用!" -#: ../controls/ProjectPropertiesPanel.py:84 -#: ../IDEFrame.py:553 +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 msgid "Project" msgstr "项目" #: ../controls/SearchResultPanel.py:173 -#, fuzzy, python-format +#, python-format msgid "Project '%s':" -msgstr "项目" - -#: ../ProjectController.py:1529 -#, fuzzy +msgstr "项目 '%s':" + +#: ../ProjectController.py:1877 msgid "Project Files" -msgstr "项目属性" - -#: ../controls/ProjectPropertiesPanel.py:78 +msgstr "项目文件" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "项目名称" + +#: ../controls/ProjectPropertiesPanel.py:79 msgid "Project Name (required):" msgstr "项目名称(必填):" -#: ../controls/ProjectPropertiesPanel.py:79 +#: ../controls/ProjectPropertiesPanel.py:80 msgid "Project Version (optional):" msgstr "项目版本(选填):" -#: ../PLCControler.py:2916 +#: ../PLCControler.py:3164 msgid "" "Project file syntax error:\n" "\n" msgstr "" - -#: ../dialogs/ProjectDialog.py:32 +"项目文件语法错误:\n" +"\n" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 msgid "Project properties" msgstr "项目属性" -#: ../ConfigTreeNode.py:506 -#, fuzzy, python-format -msgid "Project tree layout do not match confnode.xml %s!=%s " -msgstr "项目树型布局与 plugin.xml 不匹配 %s!=%s " - -#: ../PLCControler.py:96 +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "项目树的布局不匹配confnode.xml {a1}!={a2} " + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "扩展名" + +#: ../PLCControler.py:99 msgid "Properties" msgstr "属性" -#: ../plcopen/structures.py:237 -msgid "" -"Pulse timer\n" -"The pulse timer can be used to generate output pulses of a given time duration." -msgstr "" -"脉冲计时器\n" -"脉冲计时器可用于产生给定时间限制的输出的脉冲。" - -#: ../features.py:8 -#, fuzzy +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "在本地网络上发布服务" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "Pyro异常: %s\n" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "Pyro 目标的URL:" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "Pyro端口:" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "Python代码" + +#: ../features.py:33 msgid "Python file" -msgstr "Python代码" - -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "Python文件" + +#: ../dialogs/ActionBlockDialog.py:39 msgid "Qualifier" msgstr "合格验证" -#: ../Beremiz_service.py:328 -#: ../Beremiz.py:329 -#: ../PLCOpenEditor.py:151 +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 msgid "Quit" msgstr "退出" -#: ../plcopen/structures.py:202 -msgid "" -"RS bistable\n" -"The RS bistable is a latch where the Reset dominates." -msgstr "" -"RS双稳\n" -"RS双稳是一个重置支配的锁存器。" - -#: ../plcopen/structures.py:274 -#, fuzzy -msgid "" -"Ramp\n" -"The RAMP function block is modelled on example given in the standard." -msgstr "" -"匝道\n" -"匝道功能块模拟给定标准的例子,但增加了一个' 阻碍 '功能。" - -#: ../editors/GraphicViewer.py:89 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 msgid "Range:" msgstr "范围:" -#: ../ProjectController.py:1525 +#: ../ProjectController.py:1873 msgid "Raw IEC code" msgstr "原始的IEC代码" -#: ../plcopen/structures.py:254 -msgid "" -"Real time clock\n" -"The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." -msgstr "" -"实时时钟\n" -"实时时钟有很多用途,包括时间冲压,设置日期和批量报告日期时间,报警信息等。" - -#: ../Beremiz.py:1039 -#, fuzzy, python-format +#: ../BeremizIDE.py:1047 +#, python-format msgid "Really delete node '%s'?" -msgstr "确定删除插件?" - -#: ../IDEFrame.py:340 -#: ../IDEFrame.py:398 +msgstr "真的删除节点 '%s'吗?" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 msgid "Redo" -msgstr "" - -#: ../dialogs/SFCTransitionDialog.py:57 -#: ../dialogs/SFCTransitionDialog.py:135 +msgstr "重做" + +#: ../dialogs/SFCTransitionDialog.py:75 msgid "Reference" msgstr "参照" -#: ../IDEFrame.py:408 -#: ../dialogs/DiscoveryDialog.py:105 +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 msgid "Refresh" msgstr "刷新" -#: ../dialogs/SearchInProjectDialog.py:73 -#, fuzzy +#: ../dialogs/SearchInProjectDialog.py:66 msgid "Regular expression" -msgstr "表达式:" - -#: ../dialogs/FindInPouDialog.py:91 -#, fuzzy +msgstr "正则表达式" + +#: ../dialogs/FindInPouDialog.py:96 msgid "Regular expressions" -msgstr "表达式:" - -#: ../controls/DebugVariablePanel.py:299 -#: ../editors/Viewer.py:1356 +msgstr "正则表达式" + +#: ../editors/Viewer.py:1603 msgid "Release value" -msgstr "" +msgstr "释放值" #: ../plcopen/iec_std.csv:37 msgid "Remainder (modulo)" msgstr "余数(模)" -#: ../Beremiz.py:1040 +#: ../BeremizIDE.py:1048 #, python-format msgid "Remove %s node" -msgstr "" - -#: ../dialogs/ActionBlockDialog.py:139 -#, fuzzy +msgstr "移除 %s 节点" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "移除数据类型" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "移除POU" + +#: ../dialogs/ActionBlockDialog.py:138 msgid "Remove action" -msgstr "移除这个插件" - -#: ../controls/DebugVariablePanel.py:183 -#, fuzzy -msgid "Remove debug variable" -msgstr "新建一个变量" - -#: ../editors/DataTypeEditor.py:346 -#, fuzzy +msgstr "移除动作" + +#: ../editors/DataTypeEditor.py:353 msgid "Remove element" -msgstr "移除这个插件" - -#: ../editors/FileManagementPanel.py:281 +msgstr "移除元素" + +#: ../editors/FileManagementPanel.py:63 msgid "Remove file from left folder" -msgstr "" - -#: ../editors/ResourceEditor.py:252 -#, fuzzy +msgstr "从左侧目录移除文件" + +#: ../editors/ResourceEditor.py:269 msgid "Remove instance" -msgstr "删除实例" - -#: ../canfestival/NetworkEditor.py:87 -#, fuzzy +msgstr "移除实例" + +#: ../canfestival/NetworkEditor.py:104 msgid "Remove slave" -msgstr "移除这个插件" - -#: ../editors/ResourceEditor.py:223 +msgstr "移除从站" + +#: ../editors/ResourceEditor.py:240 msgid "Remove task" -msgstr "" - -#: ../controls/VariablePanel.py:379 -#, fuzzy +msgstr "移除任务" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 msgid "Remove variable" -msgstr "新建一个变量" - -#: ../IDEFrame.py:1976 +msgstr "移除变量" + +#: ../IDEFrame.py:1948 msgid "Rename" msgstr "重命名" -#: ../editors/FileManagementPanel.py:399 +#: ../editors/FileManagementPanel.py:181 msgid "Replace File" -msgstr "" +msgstr "替换文件" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "通过连接替换Wire" #: ../plcopen/iec_std.csv:89 msgid "Replacement (within)" @@ -2977,55 +2833,42 @@ msgid "Reset" msgstr "重置" -#: ../editors/Viewer.py:521 +#: ../editors/Viewer.py:642 msgid "Reset Execution Order" msgstr "重置执行命令" -#: ../IDEFrame.py:423 +#: ../IDEFrame.py:451 msgid "Reset Perspective" -msgstr "" +msgstr "复位透视图" #: ../controls/SearchResultPanel.py:105 msgid "Reset search result" -msgstr "" - -#: ../editors/GraphicViewer.py:137 -msgid "Reset zoom and offset" -msgstr "" - -#: ../PLCControler.py:96 +msgstr "复位搜索结果" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 msgid "Resources" msgstr "资源" -#: ../controls/VariablePanel.py:67 +#: ../controls/VariablePanel.py:62 msgid "Retain" msgstr "保持" -#: ../controls/VariablePanel.py:352 +#: ../controls/VariablePanel.py:424 msgid "Return Type:" msgstr "返回类型:" -#: ../editors/Viewer.py:430 +#: ../editors/Viewer.py:546 msgid "Right" msgstr "右" -#: ../dialogs/LDPowerRailDialog.py:60 +#: ../dialogs/LDPowerRailDialog.py:64 msgid "Right PowerRail" msgstr "右电源导轨" -#: ../editors/Viewer.py:404 -#: ../dialogs/LDElementDialog.py:80 +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 msgid "Rising Edge" msgstr "上升沿" -#: ../plcopen/structures.py:212 -msgid "" -"Rising edge detector\n" -"The output produces a single pulse when a rising edge is detected." -msgstr "" -"上升沿检测\n" -"当上升沿被检测到时,输出便产生一个单脉冲。" - #: ../plcopen/iec_std.csv:65 msgid "Rotate left" msgstr "循环左移" @@ -3038,149 +2881,138 @@ msgid "Rounding up/down" msgstr "四舍五入" -#: ../ProjectController.py:1493 +#: ../ProjectController.py:1841 msgid "Run" msgstr "运行" -#: ../ProjectController.py:841 -#: ../ProjectController.py:850 -#, fuzzy -msgid "Runtime extensions C code generation failed !\n" -msgstr "插件代码生成失败!\n" - -#: ../canfestival/SlaveEditor.py:34 -#: ../canfestival/NetworkEditor.py:64 +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "运行时IO扩展C代码生成失败!\n" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "运行时库扩展C代码生成失败!\n" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 msgid "SDO Client" -msgstr "" - -#: ../canfestival/SlaveEditor.py:33 -#: ../canfestival/NetworkEditor.py:63 +msgstr "SDO客户端" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 msgid "SDO Server" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:143 -#: ../dialogs/PouDialog.py:36 +msgstr "SDO服务器" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 msgid "SFC" -msgstr "顺序功能流程图" - -#: ../plcopen/structures.py:197 -msgid "" -"SR bistable\n" -"The SR bistable is a latch where the Set dominates." -msgstr "" -"SR双稳态\n" -"SR双稳态是一个设置支配的锁存器。" - -#: ../dialogs/PouTransitionDialog.py:35 -#: ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +msgstr "顺序功能图" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "POU \"{a1}\" 中的SFC跳转 涉及不存在SFC步 \"{a2}\"" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "在POU \"%s\" 中 SFC 移动必须被连接。" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 msgid "ST" -msgstr "结构化文字" - -#: ../PLCOpenEditor.py:347 +msgstr "结构化文本" + +#: ../PLCOpenEditor.py:334 msgid "ST files (*.st)|*.st|All files|*.*" msgstr "ST 文件 (*.st)|*.st|所有文件|*.*" -#: ../svgui/svgui.py:92 -#, fuzzy +#: ../svgui/svgui.py:128 msgid "SVG files (*.svg)|*.svg|All files|*.*" -msgstr "ST 文件 (*.st)|*.st|所有文件|*.*" - -#: ../features.py:10 +msgstr "SVG 文件 (*.svg)|*.svg|All files|*.*" + +#: ../features.py:35 msgid "SVGUI" -msgstr "" - -#: ../Beremiz.py:313 -#: ../Beremiz.py:344 -#: ../PLCOpenEditor.py:134 -#: ../PLCOpenEditor.py:169 -#, fuzzy +msgstr "SVGUI" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 msgid "Save" -msgstr "保存日志" - -#: ../Beremiz.py:345 -#: ../PLCOpenEditor.py:136 -#: ../PLCOpenEditor.py:170 -#, fuzzy +msgstr "保存" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 msgid "Save As..." -msgstr "另存为...\tCTRL+SHIFT+S" - -#: ../Beremiz.py:315 -#, fuzzy +msgstr "另存为..." + +#: ../BeremizIDE.py:224 msgid "Save as" -msgstr "保存日志" - -#: ../dialogs/SearchInProjectDialog.py:76 +msgstr "另存为" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "保存路径和项目路径相同!\n" + +#: ../dialogs/SearchInProjectDialog.py:69 msgid "Scope" -msgstr "" - -#: ../IDEFrame.py:592 -#: ../dialogs/SearchInProjectDialog.py:105 +msgstr "范围" + +#: ../IDEFrame.py:623 msgid "Search" -msgstr "" - -#: ../IDEFrame.py:360 -#: ../IDEFrame.py:404 -#: ../dialogs/SearchInProjectDialog.py:52 -#, fuzzy +msgstr "搜索" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 +#: ../IDEFrame.py:428 msgid "Search in Project" -msgstr "选择一个对象" - -#: ../dialogs/DurationEditorDialog.py:46 +msgstr "在项目中搜索" + +#: ../dialogs/DurationEditorDialog.py:47 msgid "Seconds:" -msgstr "" - -#: ../IDEFrame.py:366 -#, fuzzy +msgstr "秒:" + +#: ../IDEFrame.py:388 msgid "Select All" -msgstr "全部选中\tCTRL+A" - -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:330 -#: ../editors/Viewer.py:277 +msgstr "选择全部" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Select a variable class:" msgstr "选择一个变量种类:" -#: ../ProjectController.py:1013 -#, fuzzy +#: ../ProjectController.py:1257 msgid "Select an editor:" -msgstr "选择一个对象" - -#: ../controls/PouInstanceVariablesPanel.py:197 -#, fuzzy +msgstr "选择一个编辑:" + +#: ../controls/PouInstanceVariablesPanel.py:281 msgid "Select an instance" -msgstr "删除实例" - -#: ../IDEFrame.py:576 +msgstr "选择一个实例" + +#: ../IDEFrame.py:607 msgid "Select an object" msgstr "选择一个对象" +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "选择的目录已经包含其他项目。覆盖?\n" + #: ../plcopen/iec_std.csv:70 msgid "Selection" msgstr "选择" -#: ../dialogs/SFCDivergenceDialog.py:62 +#: ../dialogs/SFCDivergenceDialog.py:65 msgid "Selection Convergence" msgstr "选择收敛" -#: ../dialogs/SFCDivergenceDialog.py:55 +#: ../dialogs/SFCDivergenceDialog.py:64 msgid "Selection Divergence" msgstr "选择发散" -#: ../plcopen/structures.py:207 -msgid "" -"Semaphore\n" -"The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." -msgstr "" -"信号\n" -"信号提供一个机制,使软件元素相互排斥的进入一定资源。" - -#: ../dialogs/DiscoveryDialog.py:84 -#, fuzzy +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "服务探索" + +#: ../dialogs/DiscoveryDialog.py:85 msgid "Services available:" -msgstr "选择一个变量种类:" - -#: ../dialogs/LDElementDialog.py:72 +msgstr "存在的服务:" + +#: ../dialogs/LDElementDialog.py:76 msgid "Set" msgstr "设置" @@ -3192,27 +3024,27 @@ msgid "Shift right" msgstr "右移" -#: ../ProjectController.py:1519 +#: ../ProjectController.py:1867 msgid "Show IEC code generated by PLCGenerator" msgstr "显示由PLCGenerator生成的IEC代码" -#: ../canfestival/canfestival.py:288 +#: ../canfestival/canfestival.py:389 msgid "Show Master" msgstr "显示主控" -#: ../canfestival/canfestival.py:289 +#: ../canfestival/canfestival.py:390 msgid "Show Master generated by config_utils" msgstr "显示由config_utils生成的主控" -#: ../ProjectController.py:1517 +#: ../ProjectController.py:1865 msgid "Show code" msgstr "显示代码" -#: ../dialogs/SFCDivergenceDialog.py:74 +#: ../dialogs/SFCDivergenceDialog.py:67 msgid "Simultaneous Convergence" msgstr "同步收敛" -#: ../dialogs/SFCDivergenceDialog.py:68 +#: ../dialogs/SFCDivergenceDialog.py:66 msgid "Simultaneous Divergence" msgstr "同步发散" @@ -3220,64 +3052,79 @@ msgid "Sine" msgstr "正弦" -#: ../editors/ResourceEditor.py:67 +#: ../editors/ResourceEditor.py:68 msgid "Single" msgstr "单" +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "源代码没有变化,不需要构建.\n" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "" +"Source signal has to be defined for single task '{a1}' in resource " +"'{a2}.{a3}'." +msgstr "源信号必须被定义对于一个单独任务 '{a1}' 在资源 '{a2}.{a3}'." + #: ../plcopen/iec_std.csv:23 msgid "Square root (base 2)" msgstr "平方根(底数2)" -#: ../plcopen/structures.py:193 +#: ../plcopen/definitions.py:48 msgid "Standard function blocks" msgstr "标准功能类型" -#: ../Beremiz_service.py:319 -#: ../ProjectController.py:1495 +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 msgid "Start PLC" msgstr "开始PLC" -#: ../ProjectController.py:819 +#: ../ProjectController.py:1046 #, python-format msgid "Start build in %s\n" msgstr "开始建立 %s\n" -#: ../ProjectController.py:1314 -#, fuzzy +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "已开始" + +#: ../ProjectController.py:1648 msgid "Starting PLC\n" -msgstr "开始PLC" - -#: ../Beremiz.py:403 +msgstr "启动PLC\n" + +#: ../BeremizIDE.py:365 msgid "Status ToolBar" -msgstr "" - -#: ../editors/Viewer.py:493 -#, fuzzy +msgstr "状态工具栏" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 msgid "Step" -msgstr "编辑步骤" - -#: ../ProjectController.py:1498 +msgstr "步" + +#: ../ProjectController.py:1846 msgid "Stop" msgstr "停止" -#: ../Beremiz_service.py:320 +#: ../Beremiz_service.py:264 msgid "Stop PLC" msgstr "停止PLC" -#: ../ProjectController.py:1500 +#: ../ProjectController.py:1848 msgid "Stop Running PLC" msgstr "停止运行PLC" -#: ../ProjectController.py:1292 -#, fuzzy +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "已停止" + +#: ../ProjectController.py:1620 msgid "Stopping debugger...\n" -msgstr "正在停止调试\n" - -#: ../editors/DataTypeEditor.py:52 +msgstr "停止调试器...\n" + +#: ../editors/DataTypeEditor.py:54 msgid "Structure" msgstr "结构的" -#: ../editors/DataTypeEditor.py:52 +#: ../editors/DataTypeEditor.py:54 msgid "Subrange" msgstr "子集的" @@ -3285,53 +3132,83 @@ msgid "Subtraction" msgstr "减法" -#: ../ProjectController.py:915 +#: ../ProjectController.py:1085 msgid "Successfully built.\n" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:154 +msgstr "成功构建.\n" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "切换视图" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 msgid "Syntax error in regular expression of pattern to search!" -msgstr "" +msgstr "在模式搜索正则表达式语法错误!" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "类型" #: ../plcopen/iec_std.csv:29 msgid "Tangent" msgstr "正切" -#: ../editors/ResourceEditor.py:76 +#: ../editors/ResourceEditor.py:83 msgid "Task" msgstr "任务 " -#: ../editors/ResourceEditor.py:218 +#: ../editors/ResourceEditor.py:235 msgid "Tasks:" msgstr "任务:" -#: ../controls/VariablePanel.py:78 +#: ../controls/VariablePanel.py:73 msgid "Temp" msgstr "缓冲" -#: ../editors/FileManagementPanel.py:398 +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" +"关于Beremiz/PLCOpenEditor的最佳问问题点\n" +"是项目的邮件列表: beremiz-devel@lists.sourceforge.net\n" +"\n" +"这主要是社区支持渠道。\n" +"发布必须订阅邮件列表。\n" +"\n" +"你可在这里订阅列表:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + +#: ../editors/FileManagementPanel.py:180 #, python-format msgid "" "The file '%s' already exist.\n" "Do you want to replace it?" msgstr "" - -#: ../editors/LDViewer.py:879 +"文件 '%s' 已经存在。\n" +"你真的要替换它?" + +#: ../editors/LDViewer.py:882 msgid "The group of block must be coherent!" msgstr "块的组必须是连贯的!" -#: ../IDEFrame.py:1091 -#: ../Beremiz.py:555 +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 msgid "There are changes, do you want to save?" msgstr "文件已被改动。你希望保存吗?" -#: ../IDEFrame.py:1709 -#: ../IDEFrame.py:1728 -#, python-format -msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "" +"There is a POU named \"%s\". This could cause a conflict. Do you wish to " +"continue?" msgstr "一个编程组织单元被命名为\"%s\"。这可能会产生冲突。你希望继续吗?" -#: ../IDEFrame.py:1178 +#: ../IDEFrame.py:1102 msgid "" "There was a problem printing.\n" "Perhaps your current printer is not set correctly?" @@ -3339,20 +3216,20 @@ "打印出现问题。\n" "请检查你当前打印机设置。" -#: ../editors/LDViewer.py:888 +#: ../editors/LDViewer.py:891 msgid "This option isn't available yet!" msgstr "该选项尚未可用!" -#: ../editors/GraphicViewer.py:278 -msgid "Tick" -msgstr "" +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "滴答: %d" #: ../plcopen/iec_std.csv:40 msgid "Time" -msgstr "" - -#: ../plcopen/iec_std.csv:40 -#: ../plcopen/iec_std.csv:41 +msgstr "时间" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 msgid "Time addition" msgstr "时间加法" @@ -3360,104 +3237,115 @@ msgid "Time concatenation" msgstr "时间级联" -#: ../plcopen/iec_std.csv:60 -#: ../plcopen/iec_std.csv:61 +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 msgid "Time division" msgstr "时间除法" -#: ../plcopen/iec_std.csv:46 -#: ../plcopen/iec_std.csv:47 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 msgid "Time multiplication" msgstr "时间乘法" -#: ../plcopen/iec_std.csv:48 -#: ../plcopen/iec_std.csv:49 +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 msgid "Time subtraction" msgstr "时间减法" -#: ../plcopen/iec_std.csv:42 -#: ../plcopen/iec_std.csv:43 +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 msgid "Time-of-day addition" msgstr "日期时间加法" -#: ../plcopen/iec_std.csv:52 -#: ../plcopen/iec_std.csv:53 -#: ../plcopen/iec_std.csv:54 -#: ../plcopen/iec_std.csv:55 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 msgid "Time-of-day subtraction" msgstr "日期时间减法" -#: ../editors/Viewer.py:432 +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "触发值" + +#: ../editors/Viewer.py:548 msgid "Top" msgstr "顶部" -#: ../ProjectController.py:1507 +#: ../ProjectController.py:1855 msgid "Transfer" msgstr "传输" -#: ../ProjectController.py:1509 +#: ../ProjectController.py:1857 msgid "Transfer PLC" msgstr "传输PLC" -#: ../ProjectController.py:1474 +#: ../ProjectController.py:1820 msgid "Transfer completed successfully.\n" msgstr "传输成功\n" -#: ../ProjectController.py:1476 +#: ../ProjectController.py:1823 msgid "Transfer failed\n" msgstr "传输失败\n" -#: ../editors/Viewer.py:494 -#, fuzzy +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 +#: ../editors/Viewer.py:2420 msgid "Transition" -msgstr "跃迁" - -#: ../PLCGenerator.py:1212 -#, python-format -msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" -msgstr "" +msgstr "转换" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "" +"Transition \"%s\" body must contain an output variable or coil referring to " +"its name" +msgstr "转换 \"%s\" 体必须包含一个输出变量或圈指的是它的名字" #: ../dialogs/PouTransitionDialog.py:84 msgid "Transition Name" -msgstr "跃迁名字" +msgstr "转换名字" #: ../dialogs/PouTransitionDialog.py:53 msgid "Transition Name:" -msgstr "跃迁名字:" - -#: ../PLCGenerator.py:1301 -#, python-format -msgid "Transition with content \"%s\" not connected to a next step in \"%s\" POU" -msgstr "跃迁的内容 \"%s\" 与后一步骤没有关联在 \"%s\" 中" - -#: ../PLCGenerator.py:1292 -#, python-format -msgid "Transition with content \"%s\" not connected to a previous step in \"%s\" POU" -msgstr "跃迁的内容 \"%s\" 与前一步骤没有关联在 \"%s\" 中" - -#: ../plcopen/plcopen.py:1442 +msgstr "转换名字:" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "转换的内容 \"{a1}\" 没有连接到下一步在 \"{a2}\" POU" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "" +"Transition with content \"{a1}\" not connected to a previous step in " +"\"{a2}\" POU" +msgstr "转换的内容 \"{a1}\" 没有连接到前一步在 \"{a2}\" POU" + +#: ../plcopen/plcopen.py:1323 #, python-format msgid "Transition with name %s doesn't exist!" -msgstr "已命名的跃迁 %s 尚不存在!" - -#: ../PLCControler.py:95 +msgstr "已命名的转换 %s 尚不存在!" + +#: ../PLCControler.py:98 msgid "Transitions" -msgstr "跃迁" - -#: ../editors/ResourceEditor.py:67 +msgstr "转换" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "转换因为" + +#: ../editors/ResourceEditor.py:68 msgid "Triggering" -msgstr "" - -#: ../controls/VariablePanel.py:58 -#: ../controls/VariablePanel.py:59 -#: ../editors/DataTypeEditor.py:48 -#: ../editors/ResourceEditor.py:76 -#: ../dialogs/ActionBlockDialog.py:37 +msgstr "触发" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "扭曲的不可用。" + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 msgid "Type" msgstr "类型" -#: ../canfestival/config_utils.py:335 -#: ../canfestival/config_utils.py:617 +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "类型和派生" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 #, python-format msgid "Type conflict for location \"%s\"" msgstr "位置的冲突类型 \"%s\"" @@ -3466,263 +3354,278 @@ msgid "Type conversion" msgstr "类型转换" -#: ../editors/DataTypeEditor.py:155 +#: ../editors/DataTypeEditor.py:162 msgid "Type infos:" msgstr "类型信息:" -#: ../dialogs/SFCDivergenceDialog.py:51 -#: ../dialogs/LDPowerRailDialog.py:51 -#: ../dialogs/ConnectionDialog.py:52 -#: ../dialogs/SFCTransitionDialog.py:53 -#: ../dialogs/FBDBlockDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "严格类型" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 msgid "Type:" msgstr "类型:" -#: ../canfestival/config_utils.py:455 -#: ../canfestival/config_utils.py:469 +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 #, python-format msgid "Unable to define PDO mapping for node %02x" -msgstr "" - -#: ../targets/Xenomai/__init__.py:14 -#, fuzzy, python-format +msgstr "对于 %02x节点不能定义POU映射" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format msgid "Unable to get Xenomai's %s \n" -msgstr "无法获取Xenomai的CFLAGS\n" - -#: ../PLCGenerator.py:865 -#: ../PLCGenerator.py:924 -#, fuzzy, python-format -msgid "Undefined block type \"%s\" in \"%s\" POU" -msgstr "未定义的pou类型" - -#: ../PLCGenerator.py:240 +msgstr "不能获得 Xenomai 的 %s \n" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "未定义块类型 \"{a1}\" 在 \"{a2}\" POU中" + +#: ../PLCGenerator.py:254 #, python-format msgid "Undefined pou type \"%s\"" msgstr "未定义的pou类型" -#: ../IDEFrame.py:338 -#: ../IDEFrame.py:397 +#: ../IDEFrame.py:360 ../IDEFrame.py:421 msgid "Undo" -msgstr "" - -#: ../ProjectController.py:254 +msgstr "撤销" + +#: ../ProjectController.py:423 msgid "Unknown" -msgstr "" - -#: ../editors/Viewer.py:336 +msgstr "未知" + +#: ../editors/Viewer.py:394 #, python-format msgid "Unknown variable \"%s\" for this POU!" msgstr "未知的变量 \"%s\" 这个POU!" -#: ../ProjectController.py:251 -#: ../ProjectController.py:252 -#, fuzzy +#: ../ProjectController.py:420 ../ProjectController.py:421 msgid "Unnamed" -msgstr "未命名%d" - -#: ../PLCControler.py:305 +msgstr "未命名" + +#: ../PLCControler.py:638 #, python-format msgid "Unnamed%d" msgstr "未命名%d" -#: ../controls/VariablePanel.py:272 +#: ../controls/VariablePanel.py:284 #, python-format msgid "Unrecognized data size \"%s\"" msgstr "无法识别数据大小 \"%s\"" -#: ../plcopen/structures.py:222 -msgid "" -"Up-counter\n" -"The up-counter can be used to signal when a count has reached a maximum value." -msgstr "" -"顺计时器\n" -"当计数到达最大值时,顺计时器给出信号。" - -#: ../plcopen/structures.py:232 -msgid "" -"Up-down counter\n" -"The 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." -msgstr "" -"顺逆计数器\n" -"顺逆计数器有两个输入:CU和CD。可用于顺计时和倒计时的输入。" - -#: ../controls/VariablePanel.py:709 -#: ../editors/DataTypeEditor.py:623 +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 msgid "User Data Types" msgstr "用户数据类型" -#: ../canfestival/SlaveEditor.py:38 -#: ../canfestival/NetworkEditor.py:68 -#, fuzzy +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 msgid "User Type" -msgstr "用户数据类型" - -#: ../PLCControler.py:94 +msgstr "用户类型" + +#: ../PLCControler.py:97 msgid "User-defined POUs" msgstr "用户 - 定义POUs" -#: ../controls/DebugVariablePanel.py:40 -#: ../dialogs/ActionBlockDialog.py:37 +#: ../dialogs/ActionBlockDialog.py:39 msgid "Value" msgstr "值" -#: ../editors/GraphicViewer.py:278 -msgid "Values" -msgstr "值" - -#: ../editors/DataTypeEditor.py:252 +#: ../editors/DataTypeEditor.py:259 msgid "Values:" msgstr "值:" -#: ../controls/DebugVariablePanel.py:40 -#: ../editors/Viewer.py:466 -#: ../dialogs/ActionBlockDialog.py:41 +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 msgid "Variable" msgstr "变量" -#: ../dialogs/FBDVariableDialog.py:47 +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "变量Drop" + +#: ../dialogs/FBDVariableDialog.py:64 msgid "Variable Properties" msgstr "变量属性" -#: ../controls/VariablePanel.py:277 -#: ../editors/TextViewer.py:330 -#: ../editors/Viewer.py:277 +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 msgid "Variable class" msgstr "变量种类" -#: ../editors/TextViewer.py:374 -#: ../editors/Viewer.py:338 +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 msgid "Variable don't belong to this POU!" msgstr "变量不属于这个POU!" -#: ../controls/VariablePanel.py:77 +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "变量:" + +#: ../controls/VariablePanel.py:72 msgid "Variables" msgstr "变量" -#: ../controls/ProjectPropertiesPanel.py:151 -#, fuzzy +#: ../controls/ProjectPropertiesPanel.py:152 msgid "Vertical:" -msgstr "数学式" - -#: ../wxglade_hmi/wxglade_hmi.py:11 +msgstr "垂直的:" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "WAMP客户端启动失败。" + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "WAMP 连接到 URL : %s\n" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "WAMP连接超时" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "WAMP 连接到 '%s' 失败。\n" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "WAMP导入失败:" + +#: ../wxglade_hmi/wxglade_hmi.py:37 msgid "WXGLADE GUI" msgstr "WXGLADE 用户图形界面" -#: ../ProjectController.py:1276 -msgid "Waiting debugger to recover...\n" -msgstr "" - -#: ../editors/LDViewer.py:888 -#: ../dialogs/PouDialog.py:126 +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 msgid "Warning" msgstr "警告" -#: ../ProjectController.py:515 +#: ../ProjectController.py:707 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "警告在ST/IL/SFC代码生成器中:\n" -#: ../dialogs/SearchInProjectDialog.py:85 -#, fuzzy +#: ../dialogs/SearchInProjectDialog.py:78 msgid "Whole Project" -msgstr "" -"#-#-#-#-# Beremiz_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭项目\n" -"#-#-#-#-# PLCOpenEditor_zh_CN.po (PACKAGE VERSION) #-#-#-#-#\n" -"关闭程序" - -#: ../controls/ProjectPropertiesPanel.py:119 +msgstr "整个项目" + +#: ../controls/ProjectPropertiesPanel.py:120 msgid "Width:" msgstr "宽度:" -#: ../dialogs/FindInPouDialog.py:86 +#: ../dialogs/FindInPouDialog.py:91 msgid "Wrap search" -msgstr "" - -#: ../features.py:9 +msgstr "包搜索" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "出自" + +#: ../features.py:34 msgid "WxGlade GUI" -msgstr "" - -#: ../svgui/svgui.py:106 +msgstr "WxGlade GUI" + +#: ../svgui/svgui.py:142 msgid "" "You don't have write permissions.\n" "Open Inkscape anyway ?" msgstr "" - -#: ../wxglade_hmi/wxglade_hmi.py:108 +"你没有写入的许可。\n" +"无论如何都打开Inkscape?" + +#: ../wxglade_hmi/wxglade_hmi.py:154 msgid "" "You don't have write permissions.\n" "Open wxGlade anyway ?" msgstr "" - -#: ../ProjectController.py:220 +"你没有写入的许可。\n" +"无论如何都打开wxGlade?" + +#: ../ProjectController.py:371 msgid "" "You must have permission to work on the project\n" "Work on a project copy ?" msgstr "" - -#: ../editors/LDViewer.py:883 -msgid "You must select the block or group of blocks around which a branch should be added!" +"你必须有在此项目上工作的许可\n" +"在项目的一个拷贝上工作?" + +#: ../editors/LDViewer.py:886 +msgid "" +"You must select the block or group of blocks around which a branch should be" +" added!" msgstr "你必须选择一个块或块的组围绕着需被添加的分支!" -#: ../editors/LDViewer.py:663 +#: ../editors/LDViewer.py:666 msgid "You must select the wire where a contact should be added!" msgstr "你必须选择一条线连接需被添加的接触点!" -#: ../dialogs/PouNameDialog.py:45 -#: ../dialogs/SFCStepNameDialog.py:47 -#: ../dialogs/SFCStepDialog.py:118 +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 msgid "You must type a name!" msgstr "你必须输入一个名字!" -#: ../dialogs/ForceVariableDialog.py:165 -#, fuzzy +#: ../dialogs/ForceVariableDialog.py:193 msgid "You must type a value!" -msgstr "你必须输入一个名字!" - -#: ../IDEFrame.py:414 +msgstr "你必须输入一个值!" + +#: ../IDEFrame.py:438 msgid "Zoom" msgstr "显示比例" -#: ../editors/GraphicViewer.py:97 -#, fuzzy -msgid "Zoom:" -msgstr "显示比例" - -#: ../PLCOpenEditor.py:356 +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "日" + +#: ../PLCOpenEditor.py:343 #, python-format msgid "error: %s\n" msgstr "错误:%s\n" -#: ../util/ProcessLogger.py:161 -#, python-format -msgid "exited with status %s (pid %s)\n" -msgstr "退出并保持现状 %s (pid %s)\n" - -#: ../PLCOpenEditor.py:508 -#: ../PLCOpenEditor.py:510 +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "退出伴随状态 {a1} (pid {a2})\n" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 msgid "file : " msgstr "文件:" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "function" msgstr "功能" -#: ../PLCOpenEditor.py:511 +#: ../PLCOpenEditor.py:409 msgid "function : " msgstr "功能:" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:32 msgid "functionBlock" msgstr "功能块" -#: ../PLCOpenEditor.py:511 +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "小时" + +#: ../PLCOpenEditor.py:409 msgid "line : " msgstr "在线:" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "毫秒" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "分" + +#: ../dialogs/PouDialog.py:32 msgid "program" msgstr "程序" +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "秒" + #: ../plcopen/iec_std.csv:84 msgid "string from the middle" msgstr "从中间取字符串" @@ -3735,11 +3638,29 @@ msgid "string right of" msgstr "从右取字符串" -#: ../PLCOpenEditor.py:354 +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "更新信息不存在" + +#: ../PLCOpenEditor.py:341 #, python-format msgid "warning: %s\n" msgstr "警告:%s\n" +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "{a1} \"{a2}\" 不能被粘贴作为一个 {a3}." + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" +"{a1} XML文件没有遵循XSD schema在行 %{a2}:\n" +"{a3}" + #: Extra XSD strings msgid "CanFestivalSlaveNode" msgstr "CanFestival从节点" @@ -3771,18 +3692,36 @@ msgid "CAN_Driver" msgstr "CAN_驱动" -msgid "Debug_mode" -msgstr "调试_模式" - -msgid "CExtension" -msgstr "C扩展" +msgid "Generic" +msgstr "一般的" + +msgid "Command" +msgstr "命令" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "XenoConfig" + +msgid "Compiler" +msgstr "编译" msgid "CFLAGS" msgstr "CFLAGS" +msgid "Linker" +msgstr "链接 " + msgid "LDFLAGS" msgstr "LDFLAGS" +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + msgid "BaseParams" msgstr "基本参照 " @@ -3792,412 +3731,221 @@ msgid "Enabled" msgstr "启用" -msgid "Linux" -msgstr "Linux" - -msgid "Compiler" -msgstr "编译" - -msgid "Linker" -msgstr "链接 " - -msgid "Win32" -msgstr "Win32" - -msgid "Xenomai" -msgstr "Xenomai" - -msgid "XenoConfig" -msgstr "XenoConfig" - msgid "BeremizRoot" msgstr "Beremiz根" msgid "TargetType" msgstr "目标类型" -#, fuzzy msgid "Libraries" -msgstr "图书馆" +msgstr "库" msgid "URI_location" msgstr "URI_位置" -#, fuzzy msgid "Disable_Extensions" -msgstr "C扩展" - -#~ msgid "#EXCEPTION : " -#~ msgstr "#异常:" - -#~ msgid "A child with IEC channel %d already exist -> %d\n" -#~ msgstr "一个IEC通道的分支 %d 已经存在 -> %d\n" - -#~ msgid "Add Plugin" -#~ msgstr "添加插件" - -#~ msgid "Add a sub plugin" -#~ msgstr "添加一个子插件" - -#~ msgid "Append " -#~ msgstr "追加" - -#~ msgid "Beremiz\tF1" -#~ msgstr "Beremiz\tF1" - -#~ msgid "Broken" -#~ msgstr "损坏" - -#~ msgid "Build\tCTRL+R" -#~ msgstr "建立\tCTRL+R" - -#~ msgid "Can't find module for target %s!\n" -#~ msgstr "无法为目标找到模型 %s!\n" - -#~ msgid "Cancel" -#~ msgstr "取消" - -#~ msgid "Cannot compare latest build to target. Please build.\n" -#~ msgstr "无法与目标比较最新的建立。\n" - -#~ msgid "Debug Thread couldn't be killed" -#~ msgstr "调试线程不能结束" - -#~ msgid "Debug data not coherent %d != %d\n" -#~ msgstr "调试不和谐的数据 %d != %d\n" - -#~ msgid "Debug error idx : %d, expected_idx %d, type : %s" -#~ msgstr "调试错误 idx : %d, expected_idx %d, 类型 : %s" - -#~ msgid "Delete Plugin" -#~ msgstr "删除插件" - -#~ msgid "Delete this plugin" -#~ msgstr "删除这个插件" - -#~ msgid "Dirty" -#~ msgstr "变质" - -#~ msgid "Disconnected" -#~ msgstr "已断开" - -#~ msgid "Do you want to continue?" -#~ msgstr "你希望继续吗?" - -#~ msgid "ERROR" -#~ msgstr "错误" - -#~ msgid "Edit CanOpen Network with NetworkEdit" -#~ msgstr "用网络编辑器编辑CanOpen网络" - -#~ msgid "Edit PLC" -#~ msgstr "编辑PLC" - -#~ msgid "Edit PLC\tCTRL+R" -#~ msgstr "编辑PLC\tCTRL+R" - -#~ msgid "Edit PLC program with PLCOpenEditor" -#~ msgstr "使用PLCOpen编辑器编辑PLC程序" - -#~ msgid "Edit network" -#~ msgstr "编辑网络" - -#~ msgid "Empty" -#~ msgstr "空的" - -#~ msgid "Enable/Disable this plugin" -#~ msgstr "激活/禁用这个插件" - -#~ msgid "Generating plugins C code\n" -#~ msgstr "生成C代码插件\n" - -#~ msgid "Latest build does not match with target, please transfer.\n" -#~ msgstr "最新构建与目标不匹配,请传输。\n" - -#~ msgid "Latest build matches target, no transfer needed.\n" -#~ msgstr "最新构建与目标匹配,不需要传输。\n" - -#~ msgid "My IP is :" -#~ msgstr "我的IP是:" - -#~ msgid "New\tCTRL+N" -#~ msgstr "新建\tCTRL+N" - -#~ msgid "NewPLC (%s)" -#~ msgstr "新的PLC(%s)" - -#~ msgid "OK" -#~ msgstr "确定" - -#~ msgid "Open\tCTRL+O" -#~ msgstr "打开\tCTRL+O" - -#~ msgid "Please stop PLC to close" -#~ msgstr "请停止PLC以便关闭" - -#~ msgid "Plugin : " -#~ msgstr "插件:" - -#~ msgid "Problem %s PLC" -#~ msgstr "问题 %s PLC" - -#~ msgid "Project not created" -#~ msgstr "项目未创建" - -#~ msgid "Publish service on local network" -#~ msgstr "在本地网络发布服务" - -#~ msgid "PythonThreadProc interrupted" -#~ msgstr "Python线程处理被中断" - -#~ msgid "Question" -#~ msgstr "问题" - -#~ msgid "Quit\tCTRL+Q" -#~ msgstr "退出\tCTRL+Q" - -#~ msgid "Save\tCTRL+S" -#~ msgstr "保存\tCTRL+S" - -#~ msgid "Save changes ?" -#~ msgstr "保存修改?" - -#~ msgid "Simulate" -#~ msgstr "模拟" - -#~ msgid "Start PLC (debug mode)" -#~ msgstr "开始PLC(调试模式)" - -#~ msgid "Started" -#~ msgstr "已开始" - -#~ msgid "Starting PLC (debug mode)\n" -#~ msgstr "正在开始PLC(调试模式)\n" - -#~ msgid "Stopped" -#~ msgstr "已停止" - -#~ msgid "The daemon runs on port :" -#~ msgstr "守护进程在端口运行:" - -#~ msgid "The object's uri is :" -#~ msgstr "对象的uri是:" - -#~ msgid "The working directory :" -#~ msgstr "工作目录:" - -#~ msgid "Unable to get Xenomai's LDFLAGS\n" -#~ msgstr "无法获取Xenomai的LDFLAGS\n" - -#~ msgid "Write Python runtime code, for use with python_eval FBs" -#~ msgstr "编辑Python运行时间代码,与python_eval FBs一起使用" - -#~ msgid "Wrong URI, please check it !\n" -#~ msgstr "错误的URI,请检查!\n" - -#~ msgid "You are about to overwrite that file\n" -#~ msgstr "你即将要覆盖该文件\n" - -#~ msgid "loading" -#~ msgstr "载入" - -#~ msgid "starting" -#~ msgstr "正在开始" - -#~ msgid "Enable_Plugins" -#~ msgstr "启用_插件" - -#~ msgid "Rtai" -#~ msgstr "Rtai" - -#~ msgid "rtai_config" -#~ msgstr "rtai_config" - -#~ msgid "" -#~ "\n" -#~ "An error has occurred.\n" -#~ "\n" -#~ "Click OK to save an error report.\n" -#~ "\n" -#~ "Please contact LOLITech at:\n" -#~ "+33 (0)3 29 57 60 42\n" -#~ "bugs_PLCOpenEditor@lolitech.fr\n" -#~ "\n" -#~ "\n" -#~ "Error:\n" -#~ msgstr "" -#~ "\n" -#~ "一个错误发生了。\n" -#~ "\n" -#~ "点击确定以保存一个错误报告。\n" -#~ "\n" -#~ "edouard.tisserant@gmail.com\n" -#~ "\n" -#~ "\n" -#~ "错误:\n" - -#~ msgid " (Debug)" -#~ msgstr " (调试)" - -#~ msgid "Add a new data type" -#~ msgstr "添加一个新的数据类型" - -#~ msgid "Add new configuration" -#~ msgstr "添加新配置" - -#~ msgid "Add new resource" -#~ msgstr "添加新源" - -#~ msgid "Block Types" -#~ msgstr "块类型" - -#~ msgid "CSV Log" -#~ msgstr "逗号分隔值文件日志" - -#~ msgid "Close\tCTRL+Q" -#~ msgstr "关闭\tCTRL+Q" - -#~ msgid "Copy\tCTRL+C" -#~ msgstr "复制\tCTRL+C" - -#~ msgid "Create a new POU from" -#~ msgstr "新建一个POU从" - -#~ msgid "Cut\tCTRL+X" -#~ msgstr "剪切\tCTRL+X" - -#~ msgid "Delete Task" -#~ msgstr "删除任务" - -#~ msgid "Graphic Panel" -#~ msgstr "图形面板" - -#~ msgid "Instances" -#~ msgstr "实例" - -#~ msgid "Invalid value \"%s\" for location" -#~ msgstr "因地点而无效\"%s\"" - -#~ msgid "No" -#~ msgstr "否" - -#~ msgid "PLCOpenEditor\tF1" -#~ msgstr "PLCOpen编辑器\tF1" - -#~ msgid "Paste\tCTRL+V" -#~ msgstr "粘贴\tCTRL+V" - -#~ msgid "Please enter configuration name" -#~ msgstr "请输入配置名" - -#~ msgid "Please enter data type name" -#~ msgstr "请输入数据类型名" - -#~ msgid "Please enter resource name" -#~ msgstr "请输入源名" - -#~ msgid "Please enter text" -#~ msgstr "请输入文本" - -#~ msgid "Redo\tCTRL+Y" -#~ msgstr "重做\tCTRL+Y" - -#~ msgid "Refresh\tCTRL+R" -#~ msgstr "重新载入\tCTRL+R" - -#~ msgid "Scaling:" -#~ msgstr "比例:" - -#~ msgid "Types" -#~ msgstr "类型" - -#~ msgid "Undo\tCTRL+Z" -#~ msgstr "撤消\tCTRL+Z" - -#~ msgid "X Scale:" -#~ msgstr "X 坐标:" - -#~ msgid "Y Scale:" -#~ msgstr "Y 坐标:" - -#~ msgid "Yes" -#~ msgstr "是" - -#~ msgid "#define %s beremiz%s\n" -#~ msgstr "#定义 %s beremiz%s\n" - -#~ msgid "/* Beremiz c_ext plugin user variables definition */\n" -#~ msgstr "/* Beremiz c_ext 插件的用户变量定义 */\n" - -#~ msgid "/* Beremiz plugin functions */\n" -#~ msgstr "/* Beremiz插件功能 */\n" - -#~ msgid "" -#~ "/* Code generated by Beremiz c_ext plugin */\n" -#~ "\n" -#~ msgstr "" -#~ "/* 代码由Beremiz c_ext插件生成 */\n" -#~ "\n" - -#~ msgid "/* User includes */\n" -#~ msgstr "/* 用户包含 */\n" - -#~ msgid "/* User internal user variables and routines */\n" -#~ msgstr "/* 用户内部用户变量和例程 */\n" - -#~ msgid "/* User variables reference */\n" -#~ msgstr "/* 用户变量参照 */\n" - -#~ msgid "Choose a XML file" -#~ msgstr "选择一个XML文件" - -#~ msgid "No corresponding output variable found on SVGUI Block \"%s\"" -#~ msgstr "没有相应的输出变量" - -#~ msgid "No such XML file: %s\n" -#~ msgstr "没有这样的XML文件:%s\n" - -#~ msgid "Shortcuts created." -#~ msgstr "快捷方式已被建立。" - -#~ msgid "\n" -#~ msgstr "\n" - -#~ msgid "A pou with \"%s\" for name exists!" -#~ msgstr "一个以\"%s\"命名的的编程组织单元已经存在!" - -#~ msgid "" -#~ "A variable is defined with \"%s\" as name. It can generate a conflict. Do " -#~ "you wish to continue?" -#~ msgstr "一个变量被定义 \"%s\" 为名称。它会导致冲突。你希望继续吗?" - -#~ msgid "A variable with \"%s\" as name exists in this pou!" -#~ msgstr "一个以\"%s\"命名的变量在这个编程组织单元中已经存在!" - -#~ msgid "A variable with \"%s\" as name exists!" -#~ msgstr "一个以\"%s\"命名的变量已经存在!" - -#~ msgid "Create A New POU From" -#~ msgstr "新建一个POU从" - -#~ msgid "Create a new project" -#~ msgstr "新建一个项目" - -#~ msgid "Printing" -#~ msgstr "打印" - -#~ msgid "" -#~ "Ratio monitor\n" -#~ "The 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." -#~ msgstr "" -#~ "比监视器\n" -#~ "比监视器功能块检查一个步骤值PV1总是被比较于(被输入的比定义)第二个步骤" -#~ "值。" - -#~ msgid "ValueError" -#~ msgstr "值错误" - -#~ msgid "You can't paste the element in buffer here!" -#~ msgstr "你不能在这缓冲区中粘贴元素!" +msgstr "禁止_扩展" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)s" + +msgid "variables" +msgstr "变量" + +msgid "variable" +msgstr "变量" + +msgid "name" +msgstr "名字" + +msgid "type" +msgstr "类型" + +msgid "class" +msgstr "类" + +msgid "initial" +msgstr "初始的" + +msgid "desc" +msgstr "降序(desc)" + +msgid "onchange" +msgstr "事件(onchange)" + +msgid "opts" +msgstr "挑选(opts)" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "0 - 当前时间, 1 - 从PDT下载时间" + +msgid "Preset datetime" +msgstr "当前日期" + +msgid "Copy of IN" +msgstr "IN的复制" + +msgid "Datetime, current or relative to PDT" +msgstr "日期时间,当前的或者PDT相对的" + +msgid "" +"The real time clock has many uses including time stamping, setting dates and" +" times of day in batch reports, in alarm messages and so on." +msgstr "实时时钟大量用于包含时间戳,设置在批处理报告,报警信息等中的日期和日中的时间," + +msgid "1 = integrate, 0 = hold" +msgstr "1 = 积分, 0 = 保持" + +msgid "Overriding reset" +msgstr "压倒一切的复位" + +msgid "Input variable" +msgstr "输入变量" + +msgid "Initial value" +msgstr "初始值" + +msgid "Sampling period" +msgstr "采样周期" + +msgid "NOT R1" +msgstr "NOT R1" + +msgid "Integrated output" +msgstr "积分输出" + +msgid "" +"The integral function block integrates the value of input XIN over time." +msgstr "积分功能块集成了一段时间输入XIN的值" + +msgid "0 = reset" +msgstr "0 = 复位" + +msgid "Input to be differentiated" +msgstr "微分输入" + +msgid "Differentiated output" +msgstr "微分输出" + +msgid "" +"The derivative function block produces an output XOUT proportional to the " +"rate of change of the input XIN." +msgstr "微分功能块处理一个输出XOUT 比例于输入XIN的变化率." + +msgid "0 - manual , 1 - automatic" +msgstr "0 - 手动, 1 - 自动" + +msgid "Process variable" +msgstr "过程变量" + +msgid "Set point" +msgstr "设置点" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "手动输出调节 - 通常来自中转站" + +msgid "Proportionality constant" +msgstr "比例常数" + +msgid "Reset time" +msgstr "复位时间" + +msgid "Derivative time constant" +msgstr "微分时间常数" + +msgid "PV - SP" +msgstr "PV - SP" + +msgid "FB for integral term" +msgstr "积分项的FB" + +msgid "FB for derivative term" +msgstr "微分项的FB" + +msgid "" +"The PID (proportional, Integral, Derivative) function block provides the " +"classical three term controller for closed loop control." +msgstr "PID(比例,积分,微分)功能块提供经典的三项控制器,实现闭环控制。" + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "0 - 追踪X0, 1 - 斜坡/追踪X1" + +msgid "Ramp duration" +msgstr "斜坡时间" + +msgid "BUSY = 1 during ramping period" +msgstr "BUSY = 1 在加速期" + +msgid "Elapsed time of ramp" +msgstr "斜坡经过时间" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "(RAMP)斜坡功能块是根据标准中给出的示例建模的。" + +msgid "" +"The hysteresis function block provides a hysteresis boolean output driven by" +" the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "迟滞功能块提供一个两个差异的浮点(实数)输入XIN1和XIN2驱动的迟滞布尔输出。" + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "SR双稳态是一个置位有限锁存器" + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "RS双稳态是一个复位优先锁存器" + +msgid "" +"The semaphore provides a mechanism to allow software elements mutually " +"exclusive access to certain ressources." +msgstr "信号量提供一种机制,允许软件元素互斥排他的访问某一资源。" + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "当检测到一个上升沿时,输出产生一个单独的脉冲。" + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "当检测到下降沿时,输出产生一个单独的脉冲。" + +msgid "" +"The up-counter can be used to signal when a count has reached a maximum " +"value." +msgstr "当计数值达到最大值时,加计数器能被用来产生信号。" + +msgid "" +"The down-counter can be used to signal when a count has reached zero, on " +"counting down from a preset value." +msgstr "当计数值从一个预设值减到0,减计数器能被用来产生信号。" + +msgid "" +"The 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." +msgstr "加减计数器有2个输入CU和CD,能被用来同时在一个输入上加计数在另一个上减计数。" + +msgid "first input parameter" +msgstr "第一个输入参数" + +msgid "second input parameter" +msgstr "第二个输入参数" + +msgid "first output parameter" +msgstr "第一个输出参数" + +msgid "second output parameter" +msgstr "第二个输出参数" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "内部状态:0-复位, 1-计数,2-设置" + +msgid "" +"The pulse timer can be used to generate output pulses of a given time " +"duration." +msgstr "脉冲定时器能够被用来产生给定持续时间的输出脉冲" + +msgid "" +"The on-delay timer can be used to delay setting an output true, for fixed " +"period after an input becomes true." +msgstr "接通延迟定时器能被用来延迟设置一个输出为TRUE,当输入为TRUE时,经过固定的时间," + +msgid "" +"The off-delay timer can be used to delay setting an output false, for fixed " +"period after input goes false." +msgstr "断开延迟定时器能被用来延迟设置一个输出为FALSE,当输入到FALSE后经过一个固定的时间。" diff -r c1298e7ffe3a -r 8391c11477f4 i18n/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/Makefile Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,19 @@ +all: + @echo "Please specify target 'source_list', 'template' or 'locales'" + +template: + @echo To generate translation template message.pot file: + python mki18n.py -p --domain=Beremiz + +locales: + @echo "Generate .mo files for all languages:" + python mki18n.py -m --moTarget=../locale --domain=Beremiz + + +source_list: + echo @To "Generate list with source files: app.fil:" + find .. -name "*.py" -exec grep -q '_(' {} ';' -print -o -name "*XSD*" -print -o -name "*.csv" -print | grep -v '/build/' | grep -v '/pyjs/' > app.fil + echo "../plcopen/Additional_Function_Blocks.xml" >> app.fil + echo "../plcopen/Standard_Function_Blocks.xml" >> app.fil + +.PHONY: all, template, locales, source_list diff -r c1298e7ffe3a -r 8391c11477f4 i18n/README --- a/i18n/README Fri Mar 24 12:07:47 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ -To generate message.pot file: - - python mki18n.py -p --domain=Beremiz - -To generate .mo files for all languages: - - python mki18n.py -m --moTarget=../locale --domain=Beremiz - - -To generate app.fil: - - find .. -name "*.py" -exec grep -q '_(' {} ';' -print -o -name "*XSD*" -print -o -name "*.csv" -print | grep -v '/build/' | grep -v '/pyjs/' > app.fil diff -r c1298e7ffe3a -r 8391c11477f4 i18n/app.fil --- a/i18n/app.fil Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/app.fil Tue Jan 30 16:06:58 2018 +0100 @@ -2,6 +2,7 @@ ../canfestival/SlaveEditor.py ../canfestival/config_utils.py ../canfestival/NetworkEditor.py +../BeremizIDE.py ../dialogs/LDElementDialog.py ../dialogs/ForceVariableDialog.py ../dialogs/SearchInProjectDialog.py @@ -42,7 +43,6 @@ ../targets/Xenomai/__init__.py ../targets/Xenomai/XSD ../targets/XSD_toolchain_gcc -../targets/PLC/XSD ../targets/__init__.py ../targets/toolchain_gcc.py ../targets/toolchain_makefile.py @@ -75,6 +75,7 @@ ../controls/SearchResultPanel.py ../controls/PouInstanceVariablesPanel.py ../controls/CustomTable.py +../controls/EnhancedStatusBar.py ../controls/LibraryPanel.py ../controls/CustomToolTip.py ../controls/LogViewer.py @@ -91,6 +92,7 @@ ../controls/CustomStyledTextCtrl.py ../controls/VariablePanel.py ../controls/CustomGrid.py +../controls/CustomIntCtrl.py ../controls/CustomTree.py ../controls/CustomEditableListBox.py ../CodeFileTreeNode.py @@ -102,7 +104,7 @@ ../version.py ../POULibrary.py ../util/MiniTextControler.py -../util/Zeroconf.py +../util/ExceptionHandler.py ../util/misc.py ../util/ProcessLogger.py ../features.py @@ -117,4 +119,4 @@ ../PLCGenerator.py ../Beremiz_service.py ../plcopen/Additional_Function_Blocks.xml -../plcopen/Standard_Function_Blocks.xml \ No newline at end of file +../plcopen/Standard_Function_Blocks.xml diff -r c1298e7ffe3a -r 8391c11477f4 i18n/messages.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/i18n/messages.po Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,3898 @@ +# English translations for Beremiz package. +# Copyright (C) 2017 THE Beremiz'S COPYRIGHT HOLDER +# This file is distributed under the same license as the Beremiz package. +# Automatically generated, 2017. +# +msgid "" +msgstr "" +"Project-Id-Version: Beremiz\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-05 13:02+0300\n" +"PO-Revision-Date: 2017-07-05 13:02+0300\n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../BeremizIDE.py:1095 ../PLCOpenEditor.py:418 +#, python-format +msgid "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" +msgstr "" +"\n" +"An unhandled exception (bug) occured. Bug report saved at :\n" +"(%s)\n" +"\n" +"Please be kind enough to send this file to:\n" +"beremiz-devel@lists.sourceforge.net\n" +"\n" +"You should now restart program.\n" +"\n" +"Traceback:\n" + +#: ../controls/VariablePanel.py:72 +msgid " External" +msgstr " External" + +#: ../controls/VariablePanel.py:71 +msgid " InOut" +msgstr " InOut" + +#: ../controls/VariablePanel.py:71 +msgid " Input" +msgstr " Input" + +#: ../controls/VariablePanel.py:72 +msgid " Local" +msgstr " Local" + +#: ../controls/VariablePanel.py:71 +msgid " Output" +msgstr " Output" + +#: ../controls/VariablePanel.py:73 +msgid " Temp" +msgstr " Temp" + +#: ../dialogs/PouTransitionDialog.py:94 ../dialogs/ProjectDialog.py:69 +#: ../dialogs/PouActionDialog.py:92 ../dialogs/PouDialog.py:114 +#, python-format +msgid " and %s" +msgstr " and %s" + +#: ../ProjectController.py:1151 +msgid " generation failed !\n" +msgstr " generation failed !\n" + +#: ../plcopen/plcopen.py:886 +#, python-format +msgid "\"%s\" Data Type doesn't exist !!!" +msgstr "\"%s\" Data Type doesn't exist !!!" + +#: ../plcopen/plcopen.py:904 +#, python-format +msgid "\"%s\" POU already exists !!!" +msgstr "\"%s\" POU already exists !!!" + +#: ../plcopen/plcopen.py:925 +#, python-format +msgid "\"%s\" POU doesn't exist !!!" +msgstr "\"%s\" POU doesn't exist !!!" + +#: ../editors/Viewer.py:247 +#, python-format +msgid "\"%s\" can't use itself!" +msgstr "\"%s\" can't use itself!" + +#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#, python-format +msgid "\"%s\" config already exists!" +msgstr "\"%s\" config already exists!" + +#: ../plcopen/plcopen.py:472 +#, python-format +msgid "\"%s\" configuration already exists !!!" +msgstr "\"%s\" configuration already exists !!!" + +#: ../IDEFrame.py:1605 +#, python-format +msgid "\"%s\" data type already exists!" +msgstr "\"%s\" data type already exists!" + +#: ../dialogs/PouTransitionDialog.py:105 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:103 ../editors/Viewer.py:263 +#: ../editors/Viewer.py:331 ../editors/Viewer.py:355 ../editors/Viewer.py:375 +#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 +#: ../controls/VariablePanel.py:396 +#, python-format +msgid "\"%s\" element for this pou already exists!" +msgstr "\"%s\" element for this pou already exists!" + +#: ../BeremizIDE.py:897 +#, python-format +msgid "\"%s\" folder is not a valid Beremiz project\n" +msgstr "\"%s\" folder is not a valid Beremiz project\n" + +#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:101 +#: ../dialogs/BlockPreviewDialog.py:208 ../dialogs/PouNameDialog.py:50 +#: ../dialogs/PouActionDialog.py:99 ../dialogs/PouDialog.py:121 +#: ../editors/ResourceEditor.py:449 ../editors/ResourceEditor.py:484 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:587 +#: ../editors/CodeFileEditor.py:776 ../controls/VariablePanel.py:773 +#: ../IDEFrame.py:1596 +#, python-format +msgid "\"%s\" is a keyword. It can't be used!" +msgstr "\"%s\" is a keyword. It can't be used!" + +#: ../plcopen/plcopen.py:2417 +#, python-format +msgid "\"%s\" is an invalid value!" +msgstr "\"%s\" is an invalid value!" + +#: ../PLCOpenEditor.py:349 ../PLCOpenEditor.py:391 +#, python-format +msgid "\"%s\" is not a valid folder!" +msgstr "\"%s\" is not a valid folder!" + +#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:99 +#: ../dialogs/BlockPreviewDialog.py:204 ../dialogs/PouNameDialog.py:48 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:119 +#: ../editors/ResourceEditor.py:447 ../editors/ResourceEditor.py:482 +#: ../editors/DataTypeEditor.py:585 ../editors/CodeFileEditor.py:774 +#: ../controls/VariablePanel.py:771 ../IDEFrame.py:1594 +#, python-format +msgid "\"%s\" is not a valid identifier!" +msgstr "\"%s\" is not a valid identifier!" + +#: ../IDEFrame.py:2410 +#, python-format +msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" +msgstr "\"%s\" is used by one or more POUs. Do you wish to continue?" + +#: ../dialogs/BlockPreviewDialog.py:212 ../dialogs/PouDialog.py:123 +#: ../editors/Viewer.py:261 ../editors/Viewer.py:316 ../editors/Viewer.py:346 +#: ../editors/Viewer.py:368 ../editors/TextViewer.py:270 +#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 +#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 +#: ../IDEFrame.py:1614 +#, python-format +msgid "\"%s\" pou already exists!" +msgstr "\"%s\" pou already exists!" + +#: ../dialogs/SFCStepNameDialog.py:58 +#, python-format +msgid "\"%s\" step already exists!" +msgstr "\"%s\" step already exists!" + +#: ../editors/DataTypeEditor.py:550 +#, python-format +msgid "\"%s\" value already defined!" +msgstr "\"%s\" value already defined!" + +#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:743 +#, python-format +msgid "\"%s\" value isn't a valid array dimension!" +msgstr "\"%s\" value isn't a valid array dimension!" + +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:750 +#, python-format +msgid "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." +msgstr "" +"\"%s\" value isn't a valid array dimension!\n" +"Right value must be greater than left value." + +#: ../PLCGenerator.py:1101 +#, python-brace-format +msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" +msgstr "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" + +#: ../editors/Viewer.py:251 +#, python-brace-format +msgid "\"{a1}\" is already used by \"{a2}\"!" +msgstr "\"{a1}\" is already used by \"{a2}\"!" + +#: ../plcopen/plcopen.py:496 +#, python-brace-format +msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" + +#: ../plcopen/plcopen.py:514 +#, python-brace-format +msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" +msgstr "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#, python-format +msgid "%03gms" +msgstr "%03gms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 +#, python-format +msgid "%dd" +msgstr "%dd" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dh" +msgstr "%dh" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 +#, python-format +msgid "%dm" +msgstr "%dm" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 +#, python-format +msgid "%dms" +msgstr "%dms" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 +#, python-format +msgid "%ds" +msgstr "%ds" + +#: ../PLCControler.py:1533 +#, python-format +msgid "%s Data Types" +msgstr "%s Data Types" + +#: ../PLCControler.py:1516 +#, python-format +msgid "%s POUs" +msgstr "%s POUs" + +#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#, python-format +msgid "%s Profile" +msgstr "%s Profile" + +#: ../plcopen/plcopen.py:1650 ../plcopen/plcopen.py:1657 +#: ../plcopen/plcopen.py:1669 ../plcopen/plcopen.py:1677 +#: ../plcopen/plcopen.py:1687 +#, python-format +msgid "%s body don't have instances!" +msgstr "%s body don't have instances!" + +#: ../plcopen/plcopen.py:1705 ../plcopen/plcopen.py:1712 +#: ../plcopen/plcopen.py:1719 +#, python-format +msgid "%s body don't have text!" +msgstr "%s body don't have text!" + +#: ../IDEFrame.py:386 +msgid "&Add Element" +msgstr "&Add Element" + +#: ../dialogs/AboutDialog.py:73 ../dialogs/AboutDialog.py:121 +#: ../dialogs/AboutDialog.py:158 +msgid "&Close" +msgstr "&Close" + +#: ../IDEFrame.py:356 +msgid "&Configuration" +msgstr "&Configuration" + +#: ../IDEFrame.py:345 +msgid "&Data Type" +msgstr "&Data Type" + +#: ../IDEFrame.py:390 +msgid "&Delete" +msgstr "&Delete" + +#: ../IDEFrame.py:337 +msgid "&Display" +msgstr "&Display" + +#: ../IDEFrame.py:336 +msgid "&Edit" +msgstr "&Edit" + +#: ../IDEFrame.py:335 +msgid "&File" +msgstr "&File" + +#: ../IDEFrame.py:347 +msgid "&Function" +msgstr "&Function" + +#: ../IDEFrame.py:338 +msgid "&Help" +msgstr "&Help" + +#: ../dialogs/AboutDialog.py:72 +msgid "&License" +msgstr "&License" + +#: ../IDEFrame.py:351 +msgid "&Program" +msgstr "&Program" + +#: ../PLCOpenEditor.py:127 +msgid "&Properties" +msgstr "&Properties" + +#: ../BeremizIDE.py:219 +msgid "&Recent Projects" +msgstr "&Recent Projects" + +#: ../IDEFrame.py:353 +msgid "&Resource" +msgstr "&Resource" + +#: ../controls/SearchResultPanel.py:239 +#, python-brace-format +msgid "'{a1}' - {a2} match in project" +msgstr "'{a1}' - {a2} match in project" + +#: ../controls/SearchResultPanel.py:241 +#, python-brace-format +msgid "'{a1}' - {a2} matches in project" +msgstr "'{a1}' - {a2} matches in project" + +#: ../connectors/PYRO/__init__.py:90 +#, python-brace-format +msgid "'{a1}' is located at {a2}\n" +msgstr "'{a1}' is located at {a2}\n" + +#: ../controls/SearchResultPanel.py:291 +#, python-format +msgid "(%d matches)" +msgstr "(%d matches)" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 ../PLCOpenEditor.py:409 +msgid ", " +msgstr ", " + +#: ../dialogs/PouTransitionDialog.py:96 ../dialogs/PouActionDialog.py:94 +#: ../dialogs/PouDialog.py:116 +#, python-format +msgid ", %s" +msgstr ", %s" + +#: ../PLCOpenEditor.py:404 +msgid ". " +msgstr ". " + +#: ../controls/LogViewer.py:279 +msgid "1d" +msgstr "1d" + +#: ../controls/LogViewer.py:280 +msgid "1h" +msgstr "1h" + +#: ../controls/LogViewer.py:281 +msgid "1m" +msgstr "1m" + +#: ../controls/LogViewer.py:282 +msgid "1s" +msgstr "1s" + +#: ../dialogs/PouDialog.py:125 ../IDEFrame.py:1617 ../IDEFrame.py:1663 +#: ../IDEFrame.py:1682 +#, python-format +msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" +msgstr "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" + +#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:103 +#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:101 +#: ../controls/VariablePanel.py:775 ../IDEFrame.py:1631 ../IDEFrame.py:1644 +#, python-format +msgid "A POU named \"%s\" already exists!" +msgstr "A POU named \"%s\" already exists!" + +#: ../ConfigTreeNode.py:424 +#, python-brace-format +msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" +msgstr "A child named \"{a1}\" already exists -> \"{a2}\"\n" + +#: ../dialogs/BrowseLocationsDialog.py:218 +msgid "A location must be selected!" +msgstr "A location must be selected!" + +#: ../editors/ResourceEditor.py:451 +msgid "A task with the same name already exists!" +msgstr "A task with the same name already exists!" + +#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:777 +#: ../IDEFrame.py:1633 ../IDEFrame.py:1646 +#, python-format +msgid "A variable with \"%s\" as name already exists in this pou!" +msgstr "A variable with \"%s\" as name already exists in this pou!" + +#: ../editors/CodeFileEditor.py:780 +#, python-format +msgid "A variable with \"%s\" as name already exists!" +msgstr "A variable with \"%s\" as name already exists!" + +#: ../BeremizIDE.py:283 ../dialogs/AboutDialog.py:48 ../PLCOpenEditor.py:168 +msgid "About" +msgstr "About" + +#: ../plcopen/iec_std.csv:22 +msgid "Absolute number" +msgstr "Absolute number" + +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:43 +msgid "Action" +msgstr "Action" + +#: ../editors/Viewer.py:614 ../editors/Viewer.py:2394 +msgid "Action Block" +msgstr "Action Block" + +#: ../dialogs/PouActionDialog.py:82 +msgid "Action Name" +msgstr "Action Name" + +#: ../dialogs/PouActionDialog.py:49 +msgid "Action Name:" +msgstr "Action Name:" + +#: ../plcopen/plcopen.py:1364 +#, python-format +msgid "Action with name %s doesn't exist!" +msgstr "Action with name %s doesn't exist!" + +#: ../PLCControler.py:98 +msgid "Actions" +msgstr "Actions" + +#: ../dialogs/ActionBlockDialog.py:133 +msgid "Actions:" +msgstr "Actions:" + +#: ../editors/Viewer.py:431 +msgid "Active" +msgstr "Active" + +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 +#: ../BeremizIDE.py:965 ../editors/Viewer.py:647 +msgid "Add" +msgstr "Add" + +#: ../IDEFrame.py:1893 ../IDEFrame.py:1928 +msgid "Add Action" +msgstr "Add Action" + +#: ../features.py:32 +msgid "Add C code accessing located variables synchronously" +msgstr "Add C code accessing located variables synchronously" + +#: ../IDEFrame.py:1876 +msgid "Add Configuration" +msgstr "Add Configuration" + +#: ../IDEFrame.py:1856 +msgid "Add DataType" +msgstr "Add DataType" + +#: ../editors/Viewer.py:572 +msgid "Add Divergence Branch" +msgstr "Add Divergence Branch" + +#: ../dialogs/DiscoveryDialog.py:117 +msgid "Add IP" +msgstr "Add IP" + +#: ../IDEFrame.py:1864 +msgid "Add POU" +msgstr "Add POU" + +#: ../features.py:33 +msgid "Add Python code executed asynchronously" +msgstr "Add Python code executed asynchronously" + +#: ../IDEFrame.py:1904 ../IDEFrame.py:1954 +msgid "Add Resource" +msgstr "Add Resource" + +#: ../IDEFrame.py:1882 ../IDEFrame.py:1925 +msgid "Add Transition" +msgstr "Add Transition" + +#: ../editors/Viewer.py:559 +msgid "Add Wire Segment" +msgstr "Add Wire Segment" + +#: ../editors/SFCViewer.py:433 +msgid "Add a new initial step" +msgstr "Add a new initial step" + +#: ../editors/Viewer.py:2757 ../editors/SFCViewer.py:770 +msgid "Add a new jump" +msgstr "Add a new jump" + +#: ../editors/SFCViewer.py:455 +msgid "Add a new step" +msgstr "Add a new step" + +#: ../features.py:34 +msgid "Add a simple WxGlade based GUI." +msgstr "Add a simple WxGlade based GUI." + +#: ../dialogs/ActionBlockDialog.py:137 +msgid "Add action" +msgstr "Add action" + +#: ../editors/DataTypeEditor.py:352 +msgid "Add element" +msgstr "Add element" + +#: ../editors/ResourceEditor.py:268 +msgid "Add instance" +msgstr "Add instance" + +#: ../canfestival/NetworkEditor.py:103 +msgid "Add slave" +msgstr "Add slave" + +#: ../editors/ResourceEditor.py:239 +msgid "Add task" +msgstr "Add task" + +#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +msgid "Add variable" +msgstr "Add variable" + +#: ../plcopen/iec_std.csv:33 +msgid "Addition" +msgstr "Addition" + +#: ../plcopen/definitions.py:49 +msgid "Additional function blocks" +msgstr "Additional function blocks" + +#: ../editors/Viewer.py:630 +msgid "Adjust Block Size" +msgstr "Adjust Block Size" + +#: ../editors/Viewer.py:1686 +msgid "Alignment" +msgstr "Alignment" + +#: ../dialogs/BrowseLocationsDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:141 +#: ../dialogs/BrowseLocationsDialog.py:144 ../controls/LogViewer.py:298 +#: ../controls/VariablePanel.py:70 +msgid "All" +msgstr "All" + +#: ../editors/FileManagementPanel.py:35 +msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" +msgstr "All files (*.*)|*.*|CSV files (*.csv)|*.csv" + +#: ../ProjectController.py:1685 +msgid "Already connected. Please disconnect\n" +msgstr "Already connected. Please disconnect\n" + +#: ../editors/DataTypeEditor.py:591 +#, python-format +msgid "An element named \"%s\" already exists in this structure!" +msgstr "An element named \"%s\" already exists in this structure!" + +#: ../editors/ResourceEditor.py:486 +msgid "An instance with the same name already exists!" +msgstr "An instance with the same name already exists!" + +#: ../dialogs/ConnectionDialog.py:100 +msgid "Apply name modification to all continuations with the same name" +msgstr "Apply name modification to all continuations with the same name" + +#: ../plcopen/iec_std.csv:31 +msgid "Arc cosine" +msgstr "Arc cosine" + +#: ../plcopen/iec_std.csv:30 +msgid "Arc sine" +msgstr "Arc sine" + +#: ../plcopen/iec_std.csv:32 +msgid "Arc tangent" +msgstr "Arc tangent" + +#: ../plcopen/iec_std.csv:33 +msgid "Arithmetic" +msgstr "Arithmetic" + +#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:633 +#: ../controls/VariablePanel.py:858 +msgid "Array" +msgstr "Array" + +#: ../plcopen/iec_std.csv:39 +msgid "Assignment" +msgstr "Assignment" + +#: ../dialogs/FBDVariableDialog.py:222 +msgid "At least a variable or an expression must be selected!" +msgstr "At least a variable or an expression must be selected!" + +#: ../controls/ProjectPropertiesPanel.py:100 +msgid "Author" +msgstr "Author" + +#: ../controls/ProjectPropertiesPanel.py:97 +msgid "Author Name (optional):" +msgstr "Author Name (optional):" + +#: ../dialogs/FindInPouDialog.py:77 +msgid "Backward" +msgstr "Backward" + +#: ../util/Zeroconf.py:599 +msgid "Bad domain name (circular) at " +msgstr "Bad domain name (circular) at " + +#: ../util/Zeroconf.py:602 +msgid "Bad domain name at " +msgstr "Bad domain name at " + +#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#, python-format +msgid "Bad location size : %s" +msgstr "Bad location size : %s" + +#: ../dialogs/ArrayTypeDialog.py:54 ../editors/DataTypeEditor.py:175 +#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +msgid "Base Type:" +msgstr "Base Type:" + +#: ../editors/DataTypeEditor.py:623 ../controls/VariablePanel.py:816 +msgid "Base Types" +msgstr "Base Types" + +#: ../BeremizIDE.py:455 +msgid "Beremiz" +msgstr "Beremiz" + +#: ../plcopen/iec_std.csv:70 +msgid "Binary selection (1 of 2)" +msgstr "Binary selection (1 of 2)" + +#: ../plcopen/iec_std.csv:62 +msgid "Bit-shift" +msgstr "Bit-shift" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise" +msgstr "Bitwise" + +#: ../plcopen/iec_std.csv:66 +msgid "Bitwise AND" +msgstr "Bitwise AND" + +#: ../plcopen/iec_std.csv:67 +msgid "Bitwise OR" +msgstr "Bitwise OR" + +#: ../plcopen/iec_std.csv:68 +msgid "Bitwise XOR" +msgstr "Bitwise XOR" + +#: ../plcopen/iec_std.csv:69 +msgid "Bitwise inverting" +msgstr "Bitwise inverting" + +#: ../editors/Viewer.py:584 ../editors/Viewer.py:2407 +msgid "Block" +msgstr "Block" + +#: ../dialogs/FBDBlockDialog.py:60 +msgid "Block Properties" +msgstr "Block Properties" + +#: ../editors/TextViewer.py:262 +msgid "Block name" +msgstr "Block name" + +#: ../editors/Viewer.py:550 +msgid "Bottom" +msgstr "Bottom" + +#: ../ProjectController.py:1363 +msgid "Broken" +msgstr "Broken" + +#: ../dialogs/BrowseValuesLibraryDialog.py:38 +#, python-format +msgid "Browse %s values library" +msgstr "Browse %s values library" + +#: ../dialogs/BrowseLocationsDialog.py:65 +msgid "Browse Locations" +msgstr "Browse Locations" + +#: ../ProjectController.py:1832 +msgid "Build" +msgstr "Build" + +#: ../ProjectController.py:1297 +msgid "Build directory already clean\n" +msgstr "Build directory already clean\n" + +#: ../ProjectController.py:1833 +msgid "Build project into build folder" +msgstr "Build project into build folder" + +#: ../ProjectController.py:1080 +msgid "C Build crashed !\n" +msgstr "C Build crashed !\n" + +#: ../ProjectController.py:1077 +msgid "C Build failed.\n" +msgstr "C Build failed.\n" + +#: ../c_ext/CFileEditor.py:63 +msgid "C code" +msgstr "C code" + +#: ../ProjectController.py:1155 +msgid "C code generated successfully.\n" +msgstr "C code generated successfully.\n" + +#: ../targets/toolchain_makefile.py:122 +msgid "C compilation failed.\n" +msgstr "C compilation failed.\n" + +#: ../targets/toolchain_gcc.py:192 +#, python-format +msgid "C compilation of %s failed.\n" +msgstr "C compilation of %s failed.\n" + +#: ../features.py:32 +msgid "C extension" +msgstr "C extension" + +#: ../dialogs/AboutDialog.py:71 +msgid "C&redits" +msgstr "C&redits" + +#: ../canfestival/NetworkEditor.py:52 +msgid "CANOpen network" +msgstr "CANOpen network" + +#: ../canfestival/SlaveEditor.py:44 +msgid "CANOpen slave" +msgstr "CANOpen slave" + +#: ../features.py:31 +msgid "CANopen support" +msgstr "CANopen support" + +#: ../plcopen/plcopen.py:1589 ../plcopen/plcopen.py:1603 +#: ../plcopen/plcopen.py:1627 ../plcopen/plcopen.py:1643 +msgid "Can only generate execution order on FBD networks!" +msgstr "Can only generate execution order on FBD networks!" + +#: ../controls/VariablePanel.py:267 +msgid "Can only give a location to local or global variables" +msgstr "Can only give a location to local or global variables" + +#: ../PLCOpenEditor.py:344 +#, python-format +msgid "Can't generate program to file %s!" +msgstr "Can't generate program to file %s!" + +#: ../controls/VariablePanel.py:265 +msgid "Can't give a location to a function block instance" +msgstr "Can't give a location to a function block instance" + +#: ../PLCOpenEditor.py:389 +#, python-format +msgid "Can't save project to file %s!" +msgstr "Can't save project to file %s!" + +#: ../controls/VariablePanel.py:313 +msgid "Can't set an initial value to a function block instance" +msgstr "Can't set an initial value to a function block instance" + +#: ../ConfigTreeNode.py:529 +#, python-brace-format +msgid "Cannot create child {a1} of type {a2} " +msgstr "Cannot create child {a1} of type {a2} " + +#: ../ConfigTreeNode.py:454 +#, python-format +msgid "Cannot find lower free IEC channel than %d\n" +msgstr "Cannot find lower free IEC channel than %d\n" + +#: ../connectors/PYRO/__init__.py:131 +msgid "Cannot get PLC status - connection failed.\n" +msgstr "Cannot get PLC status - connection failed.\n" + +#: ../ProjectController.py:943 +msgid "Cannot open/parse VARIABLES.csv!\n" +msgstr "Cannot open/parse VARIABLES.csv!\n" + +#: ../canfestival/config_utils.py:374 +#, python-brace-format +msgid "Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:86 +msgid "Case sensitive" +msgstr "Case sensitive" + +#: ../editors/Viewer.py:545 +msgid "Center" +msgstr "Center" + +#: ../Beremiz_service.py:268 +msgid "Change IP of interface to bind" +msgstr "Change IP of interface to bind" + +#: ../Beremiz_service.py:267 +msgid "Change Name" +msgstr "Change Name" + +#: ../IDEFrame.py:1946 +msgid "Change POU Type To" +msgstr "Change POU Type To" + +#: ../Beremiz_service.py:269 +msgid "Change Port Number" +msgstr "Change Port Number" + +#: ../Beremiz_service.py:270 +msgid "Change working directory" +msgstr "Change working directory" + +#: ../plcopen/iec_std.csv:81 +msgid "Character string" +msgstr "Character string" + +#: ../svgui/svgui.py:128 +msgid "Choose a SVG file" +msgstr "Choose a SVG file" + +#: ../ProjectController.py:542 +msgid "Choose a directory to save project" +msgstr "Choose a directory to save project" + +#: ../canfestival/canfestival.py:162 ../PLCOpenEditor.py:302 +#: ../PLCOpenEditor.py:334 ../PLCOpenEditor.py:383 +msgid "Choose a file" +msgstr "Choose a file" + +#: ../BeremizIDE.py:833 ../BeremizIDE.py:869 +msgid "Choose a project" +msgstr "Choose a project" + +#: ../dialogs/BrowseValuesLibraryDialog.py:41 +#, python-format +msgid "Choose a value for %s:" +msgstr "Choose a value for %s:" + +#: ../Beremiz_service.py:325 +msgid "Choose a working directory " +msgstr "Choose a working directory " + +#: ../ProjectController.py:449 +msgid "Chosen folder doesn't contain a program. It's not a valid project!" +msgstr "Chosen folder doesn't contain a program. It's not a valid project!" + +#: ../ProjectController.py:416 +msgid "Chosen folder isn't empty. You can't use it for a new project!" +msgstr "Chosen folder isn't empty. You can't use it for a new project!" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Class" +msgstr "Class" + +#: ../controls/VariablePanel.py:441 +msgid "Class Filter:" +msgstr "Class Filter:" + +#: ../dialogs/FBDVariableDialog.py:70 +msgid "Class:" +msgstr "Class:" + +#: ../ProjectController.py:1836 +msgid "Clean" +msgstr "Clean" + +#: ../controls/LogViewer.py:318 +msgid "Clean log messages" +msgstr "Clean log messages" + +#: ../ProjectController.py:1838 +msgid "Clean project build folder" +msgstr "Clean project build folder" + +#: ../ProjectController.py:1294 +msgid "Cleaning the build directory\n" +msgstr "Cleaning the build directory\n" + +#: ../IDEFrame.py:435 +msgid "Clear Errors" +msgstr "Clear Errors" + +#: ../editors/Viewer.py:641 +msgid "Clear Execution Order" +msgstr "Clear Execution Order" + +#: ../dialogs/SearchInProjectDialog.py:103 ../dialogs/FindInPouDialog.py:109 +msgid "Close" +msgstr "Close" + +#: ../BeremizIDE.py:595 ../PLCOpenEditor.py:209 +msgid "Close Application" +msgstr "Close Application" + +#: ../BeremizIDE.py:228 ../BeremizIDE.py:539 ../PLCOpenEditor.py:110 +#: ../IDEFrame.py:1013 +msgid "Close Project" +msgstr "Close Project" + +#: ../BeremizIDE.py:226 ../PLCOpenEditor.py:108 +msgid "Close Tab" +msgstr "Close Tab" + +#: ../editors/Viewer.py:600 ../editors/Viewer.py:2415 +msgid "Coil" +msgstr "Coil" + +#: ../editors/Viewer.py:620 ../editors/LDViewer.py:506 +msgid "Comment" +msgstr "Comment" + +#: ../BeremizIDE.py:276 ../BeremizIDE.py:279 ../PLCOpenEditor.py:161 +#: ../PLCOpenEditor.py:164 +msgid "Community support" +msgstr "Community support" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Company Name" +msgstr "Company Name" + +#: ../controls/ProjectPropertiesPanel.py:95 +msgid "Company Name (required):" +msgstr "Company Name (required):" + +#: ../controls/ProjectPropertiesPanel.py:96 +msgid "Company URL (optional):" +msgstr "Company URL (optional):" + +#: ../plcopen/iec_std.csv:75 +msgid "Comparison" +msgstr "Comparison" + +#: ../ProjectController.py:734 +msgid "Compiling IEC Program into C code...\n" +msgstr "Compiling IEC Program into C code...\n" + +#: ../plcopen/iec_std.csv:85 +msgid "Concatenation" +msgstr "Concatenation" + +#: ../editors/ConfTreeNodeEditor.py:230 +msgid "Config" +msgstr "Config" + +#: ../editors/ProjectNodeEditor.py:36 +msgid "Config variables" +msgstr "Config variables" + +#: ../dialogs/SearchInProjectDialog.py:40 +msgid "Configuration" +msgstr "Configuration" + +#: ../PLCControler.py:99 +msgid "Configurations" +msgstr "Configurations" + +#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 +#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 +#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +msgid "Confirm or change variable name" +msgstr "Confirm or change variable name" + +#: ../ProjectController.py:1851 +msgid "Connect" +msgstr "Connect" + +#: ../ProjectController.py:1852 +msgid "Connect to the target PLC" +msgstr "Connect to the target PLC" + +#: ../ProjectController.py:1354 +#, python-format +msgid "Connected to URI: %s" +msgstr "Connected to URI: %s" + +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:586 +#: ../editors/Viewer.py:2408 +msgid "Connection" +msgstr "Connection" + +#: ../dialogs/ConnectionDialog.py:53 +msgid "Connection Properties" +msgstr "Connection Properties" + +#: ../ProjectController.py:1709 +msgid "Connection canceled!\n" +msgstr "Connection canceled!\n" + +#: ../ProjectController.py:1734 +#, python-format +msgid "Connection failed to %s!\n" +msgstr "Connection failed to %s!\n" + +#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +msgid "Connection lost!\n" +msgstr "Connection lost!\n" + +#: ../connectors/PYRO/__init__.py:102 +#, python-format +msgid "Connection to '%s' failed.\n" +msgstr "Connection to '%s' failed.\n" + +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1643 +msgid "Connector" +msgstr "Connector" + +#: ../dialogs/SFCStepDialog.py:66 +msgid "Connectors:" +msgstr "Connectors:" + +#: ../BeremizIDE.py:350 +msgid "Console" +msgstr "Console" + +#: ../controls/VariablePanel.py:60 +msgid "Constant" +msgstr "Constant" + +#: ../editors/Viewer.py:596 ../editors/Viewer.py:2411 +msgid "Contact" +msgstr "Contact" + +#: ../controls/ProjectPropertiesPanel.py:198 +msgid "Content Description (optional):" +msgstr "Content Description (optional):" + +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1644 +msgid "Continuation" +msgstr "Continuation" + +#: ../plcopen/iec_std.csv:18 +msgid "Conversion from BCD" +msgstr "Conversion from BCD" + +#: ../plcopen/iec_std.csv:19 +msgid "Conversion to BCD" +msgstr "Conversion to BCD" + +#: ../plcopen/iec_std.csv:21 +msgid "Conversion to date" +msgstr "Conversion to date" + +#: ../plcopen/iec_std.csv:20 +msgid "Conversion to time-of-day" +msgstr "Conversion to time-of-day" + +#: ../editors/Viewer.py:656 ../controls/LogViewer.py:704 ../IDEFrame.py:370 +#: ../IDEFrame.py:425 +msgid "Copy" +msgstr "Copy" + +#: ../IDEFrame.py:1933 +msgid "Copy POU" +msgstr "Copy POU" + +#: ../editors/FileManagementPanel.py:65 +msgid "Copy file from left folder to right" +msgstr "Copy file from left folder to right" + +#: ../editors/FileManagementPanel.py:64 +msgid "Copy file from right folder to left" +msgstr "Copy file from right folder to left" + +#: ../plcopen/iec_std.csv:28 +msgid "Cosine" +msgstr "Cosine" + +#: ../ConfigTreeNode.py:656 +#, python-brace-format +msgid "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" +msgstr "" +"Could not add child \"{a1}\", type {a2} :\n" +"{a3}\n" + +#: ../py_ext/PythonFileCTNMixin.py:78 +#, python-format +msgid "Couldn't import old %s file." +msgstr "Couldn't import old %s file." + +#: ../ConfigTreeNode.py:626 +#, python-brace-format +msgid "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" +msgstr "" +"Couldn't load confnode base parameters {a1} :\n" +" {a2}" + +#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#, python-brace-format +msgid "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" +msgstr "" +"Couldn't load confnode parameters {a1} :\n" +" {a2}" + +#: ../PLCControler.py:948 +msgid "Couldn't paste non-POU object." +msgstr "Couldn't paste non-POU object." + +#: ../ProjectController.py:1651 +msgid "Couldn't start PLC !\n" +msgstr "Couldn't start PLC !\n" + +#: ../ProjectController.py:1659 +msgid "Couldn't stop PLC !\n" +msgstr "Couldn't stop PLC !\n" + +#: ../ProjectController.py:1623 +msgid "Couldn't stop debugger.\n" +msgstr "Couldn't stop debugger.\n" + +#: ../svgui/svgui.py:49 +msgid "Create HMI" +msgstr "Create HMI" + +#: ../dialogs/PouDialog.py:46 +msgid "Create a new POU" +msgstr "Create a new POU" + +#: ../dialogs/PouActionDialog.py:38 +msgid "Create a new action" +msgstr "Create a new action" + +#: ../IDEFrame.py:159 +msgid "Create a new action block" +msgstr "Create a new action block" + +#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +msgid "Create a new block" +msgstr "Create a new block" + +#: ../IDEFrame.py:132 +msgid "Create a new branch" +msgstr "Create a new branch" + +#: ../IDEFrame.py:126 +msgid "Create a new coil" +msgstr "Create a new coil" + +#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +msgid "Create a new comment" +msgstr "Create a new comment" + +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +msgid "Create a new connection" +msgstr "Create a new connection" + +#: ../IDEFrame.py:129 ../IDEFrame.py:180 +msgid "Create a new contact" +msgstr "Create a new contact" + +#: ../IDEFrame.py:162 +msgid "Create a new divergence" +msgstr "Create a new divergence" + +#: ../dialogs/SFCDivergenceDialog.py:53 +msgid "Create a new divergence or convergence" +msgstr "Create a new divergence or convergence" + +#: ../IDEFrame.py:150 +msgid "Create a new initial step" +msgstr "Create a new initial step" + +#: ../IDEFrame.py:165 +msgid "Create a new jump" +msgstr "Create a new jump" + +#: ../IDEFrame.py:120 ../IDEFrame.py:177 +msgid "Create a new power rail" +msgstr "Create a new power rail" + +#: ../IDEFrame.py:123 +msgid "Create a new rung" +msgstr "Create a new rung" + +#: ../IDEFrame.py:153 +msgid "Create a new step" +msgstr "Create a new step" + +#: ../dialogs/PouTransitionDialog.py:42 ../IDEFrame.py:156 +msgid "Create a new transition" +msgstr "Create a new transition" + +#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +msgid "Create a new variable" +msgstr "Create a new variable" + +#: ../dialogs/AboutDialog.py:113 +msgid "Credits" +msgstr "Credits" + +#: ../Beremiz_service.py:434 +msgid "Current working directory :" +msgstr "Current working directory :" + +#: ../editors/Viewer.py:655 ../IDEFrame.py:368 ../IDEFrame.py:424 +msgid "Cut" +msgstr "Cut" + +#: ../editors/ResourceEditor.py:72 +msgid "Cyclic" +msgstr "Cyclic" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:44 +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:50 +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:54 +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:58 +#: ../plcopen/iec_std.csv:60 +msgid "DEPRECATED" +msgstr "DEPRECATED" + +#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +msgid "DS-301 Profile" +msgstr "DS-301 Profile" + +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +msgid "DS-302 Profile" +msgstr "DS-302 Profile" + +#: ../dialogs/SearchInProjectDialog.py:36 +msgid "Data Type" +msgstr "Data Type" + +#: ../PLCControler.py:98 +msgid "Data Types" +msgstr "Data Types" + +#: ../plcopen/iec_std.csv:16 +msgid "Data type conversion" +msgstr "Data type conversion" + +#: ../plcopen/iec_std.csv:44 ../plcopen/iec_std.csv:45 +msgid "Date addition" +msgstr "Date addition" + +#: ../plcopen/iec_std.csv:56 ../plcopen/iec_std.csv:57 +#: ../plcopen/iec_std.csv:58 ../plcopen/iec_std.csv:59 +msgid "Date and time subtraction" +msgstr "Date and time subtraction" + +#: ../plcopen/iec_std.csv:50 ../plcopen/iec_std.csv:51 +msgid "Date subtraction" +msgstr "Date subtraction" + +#: ../dialogs/DurationEditorDialog.py:44 +msgid "Days:" +msgstr "Days:" + +#: ../ProjectController.py:1756 +msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" +msgstr "Debug does not match PLC - stop/transfert/start to re-enable\n" + +#: ../controls/PouInstanceVariablesPanel.py:134 +msgid "Debug instance" +msgstr "Debug instance" + +#: ../editors/Viewer.py:448 +#, python-format +msgid "Debug: %s" +msgstr "Debug: %s" + +#: ../ProjectController.py:1412 +#, python-format +msgid "Debug: Unknown variable '%s'\n" +msgstr "Debug: Unknown variable '%s'\n" + +#: ../ProjectController.py:1410 +#, python-format +msgid "Debug: Unsupported type to debug '%s'\n" +msgstr "Debug: Unsupported type to debug '%s'\n" + +#: ../IDEFrame.py:639 +msgid "Debugger" +msgstr "Debugger" + +#: ../ProjectController.py:1592 +msgid "Debugger disabled\n" +msgstr "Debugger disabled\n" + +#: ../ProjectController.py:1753 +msgid "Debugger ready\n" +msgstr "Debugger ready\n" + +#: ../ProjectController.py:1625 +msgid "Debugger stopped.\n" +msgstr "Debugger stopped.\n" + +#: ../BeremizIDE.py:968 ../editors/Viewer.py:631 ../IDEFrame.py:1962 +msgid "Delete" +msgstr "Delete" + +#: ../editors/Viewer.py:573 +msgid "Delete Divergence Branch" +msgstr "Delete Divergence Branch" + +#: ../editors/FileManagementPanel.py:153 +msgid "Delete File" +msgstr "Delete File" + +#: ../editors/Viewer.py:560 +msgid "Delete Wire Segment" +msgstr "Delete Wire Segment" + +#: ../controls/CustomEditableListBox.py:41 +msgid "Delete item" +msgstr "Delete item" + +#: ../plcopen/iec_std.csv:88 +msgid "Deletion (within)" +msgstr "Deletion (within)" + +#: ../editors/DataTypeEditor.py:153 +msgid "Derivation Type:" +msgstr "Derivation Type:" + +#: ../editors/CodeFileEditor.py:739 +msgid "Description" +msgstr "Description" + +#: ../controls/VariablePanel.py:432 +msgid "Description:" +msgstr "Description:" + +#: ../dialogs/ArrayTypeDialog.py:60 ../editors/DataTypeEditor.py:321 +msgid "Dimensions:" +msgstr "Dimensions:" + +#: ../dialogs/FindInPouDialog.py:66 +msgid "Direction" +msgstr "Direction" + +#: ../dialogs/BrowseLocationsDialog.py:91 +msgid "Direction:" +msgstr "Direction:" + +#: ../editors/DataTypeEditor.py:54 +msgid "Directly" +msgstr "Directly" + +#: ../ProjectController.py:1860 +msgid "Disconnect" +msgstr "Disconnect" + +#: ../ProjectController.py:1862 +msgid "Disconnect from PLC" +msgstr "Disconnect from PLC" + +#: ../ProjectController.py:1364 +msgid "Disconnected" +msgstr "Disconnected" + +#: ../editors/Viewer.py:615 ../editors/Viewer.py:2403 +msgid "Divergence" +msgstr "Divergence" + +#: ../plcopen/iec_std.csv:36 +msgid "Division" +msgstr "Division" + +#: ../editors/FileManagementPanel.py:152 +#, python-format +msgid "Do you really want to delete the file '%s'?" +msgstr "Do you really want to delete the file '%s'?" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Documentation" +msgstr "Documentation" + +#: ../PLCOpenEditor.py:338 +msgid "Done" +msgstr "Done" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Duration" +msgstr "Duration" + +#: ../canfestival/canfestival.py:165 +msgid "EDS files (*.eds)|*.eds|All files|*.*" +msgstr "EDS files (*.eds)|*.eds|All files|*.*" + +#: ../editors/Viewer.py:629 +msgid "Edit Block" +msgstr "Edit Block" + +#: ../dialogs/LDElementDialog.py:56 +msgid "Edit Coil Values" +msgstr "Edit Coil Values" + +#: ../dialogs/LDElementDialog.py:54 +msgid "Edit Contact Values" +msgstr "Edit Contact Values" + +#: ../dialogs/DurationEditorDialog.py:59 +msgid "Edit Duration" +msgstr "Edit Duration" + +#: ../dialogs/SFCStepDialog.py:51 +msgid "Edit Step" +msgstr "Edit Step" + +#: ../wxglade_hmi/wxglade_hmi.py:38 +msgid "Edit a WxWidgets GUI with WXGlade" +msgstr "Edit a WxWidgets GUI with WXGlade" + +#: ../dialogs/ActionBlockDialog.py:121 +msgid "Edit action block properties" +msgstr "Edit action block properties" + +#: ../dialogs/ArrayTypeDialog.py:44 +msgid "Edit array type properties" +msgstr "Edit array type properties" + +#: ../editors/Viewer.py:2626 ../editors/Viewer.py:3055 +msgid "Edit comment" +msgstr "Edit comment" + +#: ../editors/FileManagementPanel.py:66 +msgid "Edit file" +msgstr "Edit file" + +#: ../controls/CustomEditableListBox.py:39 +msgid "Edit item" +msgstr "Edit item" + +#: ../editors/Viewer.py:3014 +msgid "Edit jump target" +msgstr "Edit jump target" + +#: ../ProjectController.py:1874 +msgid "Edit raw IEC code added to code generated by PLCGenerator" +msgstr "Edit raw IEC code added to code generated by PLCGenerator" + +#: ../editors/SFCViewer.py:799 +msgid "Edit step name" +msgstr "Edit step name" + +#: ../dialogs/SFCTransitionDialog.py:52 +msgid "Edit transition" +msgstr "Edit transition" + +#: ../IDEFrame.py:611 +msgid "Editor ToolBar" +msgstr "Editor ToolBar" + +#: ../ProjectController.py:1257 +msgid "Editor selection" +msgstr "Editor selection" + +#: ../editors/DataTypeEditor.py:348 +msgid "Elements :" +msgstr "Elements :" + +#: ../ProjectController.py:1362 +msgid "Empty" +msgstr "Empty" + +#: ../IDEFrame.py:365 +msgid "Enable Undo/Redo" +msgstr "Enable Undo/Redo" + +#: ../Beremiz_service.py:333 +msgid "Enter a name " +msgstr "Enter a name " + +#: ../Beremiz_service.py:318 +msgid "Enter a port number " +msgstr "Enter a port number " + +#: ../Beremiz_service.py:309 +msgid "Enter the IP of the interface to bind" +msgstr "Enter the IP of the interface to bind" + +#: ../editors/DataTypeEditor.py:54 +msgid "Enumerated" +msgstr "Enumerated" + +#: ../plcopen/iec_std.csv:77 +msgid "Equal to" +msgstr "Equal to" + +#: ../BeremizIDE.py:1107 ../dialogs/ForceVariableDialog.py:197 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 +#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:167 ../dialogs/PouTransitionDialog.py:107 +#: ../dialogs/BlockPreviewDialog.py:237 ../dialogs/ProjectDialog.py:74 +#: ../dialogs/ArrayTypeDialog.py:97 ../dialogs/ArrayTypeDialog.py:103 +#: ../dialogs/PouNameDialog.py:54 ../dialogs/BrowseLocationsDialog.py:218 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 ../dialogs/PouActionDialog.py:105 +#: ../dialogs/PouDialog.py:135 ../PLCOpenEditor.py:345 ../PLCOpenEditor.py:350 +#: ../PLCOpenEditor.py:430 ../PLCOpenEditor.py:440 +#: ../editors/ResourceEditor.py:436 ../editors/Viewer.py:424 +#: ../editors/LDViewer.py:666 ../editors/LDViewer.py:882 +#: ../editors/LDViewer.py:886 ../editors/DataTypeEditor.py:550 +#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:574 +#: ../editors/DataTypeEditor.py:743 ../editors/DataTypeEditor.py:750 +#: ../editors/TextViewer.py:389 ../editors/CodeFileEditor.py:762 +#: ../ProjectController.py:372 ../ProjectController.py:512 +#: ../ProjectController.py:519 ../controls/FolderTree.py:217 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 +#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:759 +#: ../IDEFrame.py:1007 ../IDEFrame.py:1617 ../IDEFrame.py:1658 +#: ../IDEFrame.py:1663 ../IDEFrame.py:1677 ../IDEFrame.py:1682 +#: ../Beremiz_service.py:213 +msgid "Error" +msgstr "Error" + +#: ../ProjectController.py:789 +msgid "Error : At least one configuration and one resource must be declared in PLC !\n" +msgstr "Error : At least one configuration and one resource must be declared in PLC !\n" + +#: ../ProjectController.py:781 +#, python-format +msgid "Error : IEC to C compiler returned %d\n" +msgstr "Error : IEC to C compiler returned %d\n" + +#: ../ProjectController.py:712 +#, python-format +msgid "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" +msgstr "" +"Error in ST/IL/SFC code generator :\n" +"%s\n" + +#: ../ConfigTreeNode.py:216 +#, python-format +msgid "Error while saving \"%s\"\n" +msgstr "Error while saving \"%s\"\n" + +#: ../canfestival/canfestival.py:170 +msgid "Error: Export slave failed\n" +msgstr "Error: Export slave failed\n" + +#: ../canfestival/canfestival.py:371 +msgid "Error: No Master generated\n" +msgstr "Error: No Master generated\n" + +#: ../canfestival/canfestival.py:366 +msgid "Error: No PLC built\n" +msgstr "Error: No PLC built\n" + +#: ../ProjectController.py:1728 +#, python-format +msgid "Exception while connecting %s!\n" +msgstr "Exception while connecting %s!\n" + +#: ../dialogs/FBDBlockDialog.py:120 +msgid "Execution Control:" +msgstr "Execution Control:" + +#: ../dialogs/FBDVariableDialog.py:80 ../dialogs/FBDBlockDialog.py:108 +msgid "Execution Order:" +msgstr "Execution Order:" + +#: ../features.py:35 +msgid "Experimental web based HMI" +msgstr "Experimental web based HMI" + +#: ../plcopen/iec_std.csv:38 +msgid "Exponent" +msgstr "Exponent" + +#: ../plcopen/iec_std.csv:26 +msgid "Exponentiation" +msgstr "Exponentiation" + +#: ../canfestival/canfestival.py:176 +msgid "Export CanOpen slave to EDS file" +msgstr "Export CanOpen slave to EDS file" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:243 +msgid "Export graph values to clipboard" +msgstr "Export graph values to clipboard" + +#: ../canfestival/canfestival.py:175 +msgid "Export slave" +msgstr "Export slave" + +#: ../dialogs/FBDVariableDialog.py:90 +msgid "Expression:" +msgstr "Expression:" + +#: ../controls/VariablePanel.py:72 +msgid "External" +msgstr "External" + +#: ../ProjectController.py:802 +msgid "Extracting Located Variables...\n" +msgstr "Extracting Located Variables...\n" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "FBD" +msgstr "FBD" + +#: ../ProjectController.py:1791 +msgid "Failed : Must build before transfer.\n" +msgstr "Failed : Must build before transfer.\n" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:521 +msgid "Falling Edge" +msgstr "Falling Edge" + +#: ../ProjectController.py:1070 +msgid "Fatal : cannot get builder.\n" +msgstr "Fatal : cannot get builder.\n" + +#: ../Beremiz.py:156 +#, python-format +msgid "Fetching %s" +msgstr "Fetching %s" + +#: ../dialogs/DurationEditorDialog.py:164 +#, python-format +msgid "Field %s hasn't a valid value!" +msgstr "Field %s hasn't a valid value!" + +#: ../dialogs/DurationEditorDialog.py:166 +#, python-format +msgid "Fields %s haven't a valid value!" +msgstr "Fields %s haven't a valid value!" + +#: ../controls/FolderTree.py:216 +#, python-format +msgid "File '%s' already exists!" +msgstr "File '%s' already exists!" + +#: ../dialogs/SearchInProjectDialog.py:98 ../dialogs/FindInPouDialog.py:37 +#: ../dialogs/FindInPouDialog.py:104 ../IDEFrame.py:375 +msgid "Find" +msgstr "Find" + +#: ../IDEFrame.py:377 +msgid "Find Next" +msgstr "Find Next" + +#: ../IDEFrame.py:379 +msgid "Find Previous" +msgstr "Find Previous" + +#: ../plcopen/iec_std.csv:90 +msgid "Find position" +msgstr "Find position" + +#: ../dialogs/FindInPouDialog.py:55 +msgid "Find:" +msgstr "Find:" + +#: ../connectors/PYRO/__init__.py:163 +msgid "Force runtime reload\n" +msgstr "Force runtime reload\n" + +#: ../editors/Viewer.py:1600 +msgid "Force value" +msgstr "Force value" + +#: ../dialogs/ForceVariableDialog.py:162 +msgid "Forcing Variable Value" +msgstr "Forcing Variable Value" + +#: ../dialogs/SFCTransitionDialog.py:182 ../dialogs/PouTransitionDialog.py:97 +#: ../dialogs/ProjectDialog.py:73 ../dialogs/PouActionDialog.py:95 +#: ../dialogs/PouDialog.py:117 +#, python-format +msgid "Form isn't complete. %s must be filled!" +msgstr "Form isn't complete. %s must be filled!" + +#: ../dialogs/SFCStepDialog.py:147 ../dialogs/FBDBlockDialog.py:236 +#: ../dialogs/ConnectionDialog.py:163 +msgid "Form isn't complete. Name must be filled!" +msgstr "Form isn't complete. Name must be filled!" + +#: ../dialogs/FBDBlockDialog.py:232 +msgid "Form isn't complete. Valid block type must be selected!" +msgstr "Form isn't complete. Valid block type must be selected!" + +#: ../dialogs/FindInPouDialog.py:72 +msgid "Forward" +msgstr "Forward" + +#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1749 +msgid "Function" +msgstr "Function" + +#: ../IDEFrame.py:349 +msgid "Function &Block" +msgstr "Function &Block" + +#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1748 +#: ../IDEFrame.py:1941 +msgid "Function Block" +msgstr "Function Block" + +#: ../controls/VariablePanel.py:854 +msgid "Function Block Types" +msgstr "Function Block Types" + +#: ../PLCControler.py:97 +msgid "Function Blocks" +msgstr "Function Blocks" + +#: ../editors/Viewer.py:249 +msgid "Function Blocks can't be used in Functions!" +msgstr "Function Blocks can't be used in Functions!" + +#: ../PLCControler.py:2343 +#, python-format +msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" +msgstr "FunctionBlock \"%s\" can't be pasted in a Function!!!" + +#: ../PLCControler.py:97 +msgid "Functions" +msgstr "Functions" + +#: ../PLCOpenEditor.py:117 +msgid "Generate Program" +msgstr "Generate Program" + +#: ../ProjectController.py:703 +msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" +msgstr "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" + +#: ../controls/VariablePanel.py:73 +msgid "Global" +msgstr "Global" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:242 +msgid "Go to current value" +msgstr "Go to current value" + +#: ../controls/ProjectPropertiesPanel.py:174 +msgid "Graphics" +msgstr "Graphics" + +#: ../plcopen/iec_std.csv:75 +msgid "Greater than" +msgstr "Greater than" + +#: ../plcopen/iec_std.csv:76 +msgid "Greater than or equal to" +msgstr "Greater than or equal to" + +#: ../controls/ProjectPropertiesPanel.py:135 +msgid "Grid Resolution:" +msgstr "Grid Resolution:" + +#: ../runtime/NevowServer.py:182 +msgid "HTTP interface port :" +msgstr "HTTP interface port :" + +#: ../controls/ProjectPropertiesPanel.py:121 +msgid "Height:" +msgstr "Height:" + +#: ../editors/FileManagementPanel.py:85 +msgid "Home Directory:" +msgstr "Home Directory:" + +#: ../controls/ProjectPropertiesPanel.py:151 +msgid "Horizontal:" +msgstr "Horizontal:" + +#: ../dialogs/DurationEditorDialog.py:45 +msgid "Hours:" +msgstr "Hours:" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "IL" +msgstr "IL" + +#: ../dialogs/DiscoveryDialog.py:94 +msgid "IP" +msgstr "IP" + +#: ../Beremiz_service.py:310 ../Beremiz_service.py:311 +msgid "IP is not valid!" +msgstr "IP is not valid!" + +#: ../svgui/svgui.py:44 ../svgui/svgui.py:45 +msgid "Import SVG" +msgstr "Import SVG" + +#: ../dialogs/FBDVariableDialog.py:39 ../editors/Viewer.py:1629 +#: ../controls/VariablePanel.py:71 +msgid "InOut" +msgstr "InOut" + +#: ../editors/Viewer.py:431 +msgid "Inactive" +msgstr "Inactive" + +#: ../controls/VariablePanel.py:276 +#, python-brace-format +msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" +msgstr "Incompatible data types between \"{a1}\" and \"{a2}\"" + +#: ../controls/VariablePanel.py:282 +#, python-format +msgid "Incompatible size of data between \"%s\" and \"BOOL\"" +msgstr "Incompatible size of data between \"%s\" and \"BOOL\"" + +#: ../controls/VariablePanel.py:286 +#, python-brace-format +msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" +msgstr "Incompatible size of data between \"{a1}\" and \"{a2}\"" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Indicator" +msgstr "Indicator" + +#: ../editors/CodeFileEditor.py:739 +msgid "Initial" +msgstr "Initial" + +#: ../editors/Viewer.py:611 +msgid "Initial Step" +msgstr "Initial Step" + +#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:54 +msgid "Initial Value" +msgstr "Initial Value" + +#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 +#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +msgid "Initial Value:" +msgstr "Initial Value:" + +#: ../svgui/svgui.py:48 +msgid "Inkscape" +msgstr "Inkscape" + +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:43 +msgid "Inline" +msgstr "Inline" + +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:38 +#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1627 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Input" +msgstr "Input" + +#: ../dialogs/FBDBlockDialog.py:96 +msgid "Inputs:" +msgstr "Inputs:" + +#: ../plcopen/iec_std.csv:87 +msgid "Insertion (into)" +msgstr "Insertion (into)" + +#: ../plcopen/plcopen.py:1696 +#, python-format +msgid "Instance with id %d doesn't exist!" +msgstr "Instance with id %d doesn't exist!" + +#: ../editors/ResourceEditor.py:264 +msgid "Instances:" +msgstr "Instances:" + +#: ../controls/VariablePanel.py:70 +msgid "Interface" +msgstr "Interface" + +#: ../editors/ResourceEditor.py:72 +msgid "Interrupt" +msgstr "Interrupt" + +#: ../editors/ResourceEditor.py:68 +msgid "Interval" +msgstr "Interval" + +#: ../PLCControler.py:2331 +msgid "Invalid plcopen element(s)!!!" +msgstr "Invalid plcopen element(s)!!!" + +#: ../canfestival/config_utils.py:381 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" +msgstr "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" + +#: ../canfestival/config_utils.py:645 +#, python-brace-format +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#, python-format +msgid "Invalid value \"%s\" for debug variable" +msgstr "Invalid value \"%s\" for debug variable" + +#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#, python-format +msgid "Invalid value \"%s\" for variable grid element" +msgstr "Invalid value \"%s\" for variable grid element" + +#: ../editors/Viewer.py:234 ../editors/Viewer.py:237 +#, python-format +msgid "Invalid value \"%s\" for viewer block" +msgstr "Invalid value \"%s\" for viewer block" + +#: ../dialogs/ForceVariableDialog.py:195 +#, python-brace-format +msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" +msgstr "Invalid value \"{a1}\" for \"{a2}\" variable!" + +#: ../dialogs/DurationEditorDialog.py:121 +msgid "" +"Invalid value!\n" +"You must fill a numeric value." +msgstr "" +"Invalid value!\n" +"You must fill a numeric value." + +#: ../editors/Viewer.py:616 ../editors/Viewer.py:2392 +msgid "Jump" +msgstr "Jump" + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "LD" +msgstr "LD" + +#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#, python-format +msgid "Ladder element with id %d is on more than one rung." +msgstr "Ladder element with id %d is on more than one rung." + +#: ../dialogs/PouTransitionDialog.py:86 ../dialogs/PouActionDialog.py:84 +#: ../dialogs/PouDialog.py:105 +msgid "Language" +msgstr "Language" + +#: ../controls/ProjectPropertiesPanel.py:187 +msgid "Language (optional):" +msgstr "Language (optional):" + +#: ../dialogs/PouTransitionDialog.py:60 ../dialogs/PouActionDialog.py:56 +#: ../dialogs/PouDialog.py:73 +msgid "Language:" +msgstr "Language:" + +#: ../ProjectController.py:1797 +msgid "Latest build already matches current target. Transfering anyway...\n" +msgstr "Latest build already matches current target. Transfering anyway...\n" + +#: ../Beremiz_service.py:273 +msgid "Launch WX GUI inspector" +msgstr "Launch WX GUI inspector" + +#: ../Beremiz_service.py:272 +msgid "Launch a live Python shell" +msgstr "Launch a live Python shell" + +#: ../editors/Viewer.py:544 +msgid "Left" +msgstr "Left" + +#: ../dialogs/LDPowerRailDialog.py:63 +msgid "Left PowerRail" +msgstr "Left PowerRail" + +#: ../plcopen/iec_std.csv:81 +msgid "Length of string" +msgstr "Length of string" + +#: ../plcopen/iec_std.csv:78 +msgid "Less than" +msgstr "Less than" + +#: ../plcopen/iec_std.csv:79 +msgid "Less than or equal to" +msgstr "Less than or equal to" + +#: ../IDEFrame.py:631 +msgid "Library" +msgstr "Library" + +#: ../dialogs/AboutDialog.py:151 +msgid "License" +msgstr "License" + +#: ../plcopen/iec_std.csv:73 +msgid "Limitation" +msgstr "Limitation" + +#: ../targets/toolchain_gcc.py:202 +msgid "Linking :\n" +msgstr "Linking :\n" + +#: ../dialogs/DiscoveryDialog.py:112 ../controls/VariablePanel.py:72 +msgid "Local" +msgstr "Local" + +#: ../canfestival/canfestival.py:348 +msgid "Local entries" +msgstr "Local entries" + +#: ../ProjectController.py:1703 +msgid "Local service discovery failed!\n" +msgstr "Local service discovery failed!\n" + +#: ../controls/VariablePanel.py:53 +msgid "Location" +msgstr "Location" + +#: ../dialogs/BrowseLocationsDialog.py:72 +msgid "Locations available:" +msgstr "Locations available:" + +#: ../plcopen/iec_std.csv:25 +msgid "Logarithm to base 10" +msgstr "Logarithm to base 10" + +#: ../connectors/PYRO/__init__.py:94 +#, python-format +msgid "MDNS resolution failure for '%s'\n" +msgstr "MDNS resolution failure for '%s'\n" + +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +msgid "Map Variable" +msgstr "Map Variable" + +#: ../features.py:31 +msgid "Map located variables over CANopen" +msgstr "Map located variables over CANopen" + +#: ../canfestival/NetworkEditor.py:106 +msgid "Master" +msgstr "Master" + +#: ../ConfigTreeNode.py:539 +#, python-brace-format +msgid "Max count ({a1}) reached for this confnode of type {a2} " +msgstr "Max count ({a1}) reached for this confnode of type {a2} " + +#: ../plcopen/iec_std.csv:71 +msgid "Maximum" +msgstr "Maximum" + +#: ../editors/DataTypeEditor.py:239 +msgid "Maximum:" +msgstr "Maximum:" + +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:290 +#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Memory" +msgstr "Memory" + +#: ../IDEFrame.py:599 +msgid "Menu ToolBar" +msgstr "Menu ToolBar" + +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "Microseconds:" + +#: ../editors/Viewer.py:549 +msgid "Middle" +msgstr "Middle" + +#: ../dialogs/DurationEditorDialog.py:48 +msgid "Milliseconds:" +msgstr "Milliseconds:" + +#: ../plcopen/iec_std.csv:72 +msgid "Minimum" +msgstr "Minimum" + +#: ../editors/DataTypeEditor.py:226 +msgid "Minimum:" +msgstr "Minimum:" + +#: ../dialogs/DurationEditorDialog.py:46 +msgid "Minutes:" +msgstr "Minutes:" + +#: ../controls/ProjectPropertiesPanel.py:211 +msgid "Miscellaneous" +msgstr "Miscellaneous" + +#: ../dialogs/LDElementDialog.py:63 +msgid "Modifier:" +msgstr "Modifier:" + +#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#, python-brace-format +msgid "More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" + +#: ../dialogs/ActionBlockDialog.py:140 +msgid "Move action down" +msgstr "Move action down" + +#: ../dialogs/ActionBlockDialog.py:139 +msgid "Move action up" +msgstr "Move action up" + +#: ../controls/CustomEditableListBox.py:43 +msgid "Move down" +msgstr "Move down" + +#: ../editors/DataTypeEditor.py:355 +msgid "Move element down" +msgstr "Move element down" + +#: ../editors/DataTypeEditor.py:354 +msgid "Move element up" +msgstr "Move element up" + +#: ../editors/ResourceEditor.py:271 +msgid "Move instance down" +msgstr "Move instance down" + +#: ../editors/ResourceEditor.py:270 +msgid "Move instance up" +msgstr "Move instance up" + +#: ../editors/ResourceEditor.py:242 +msgid "Move task down" +msgstr "Move task down" + +#: ../editors/ResourceEditor.py:241 +msgid "Move task up" +msgstr "Move task up" + +#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 +msgid "Move the view" +msgstr "Move the view" + +#: ../controls/CustomEditableListBox.py:42 +msgid "Move up" +msgstr "Move up" + +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +msgid "Move variable down" +msgstr "Move variable down" + +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +msgid "Move variable up" +msgstr "Move variable up" + +#: ../plcopen/iec_std.csv:74 +msgid "Multiplexer (select 1 of N)" +msgstr "Multiplexer (select 1 of N)" + +#: ../plcopen/iec_std.csv:34 +msgid "Multiplication" +msgstr "Multiplication" + +#: ../editors/FileManagementPanel.py:83 +msgid "My Computer:" +msgstr "My Computer:" + +#: ../dialogs/DiscoveryDialog.py:92 +msgid "NAME" +msgstr "NAME" + +#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Name" +msgstr "Name" + +#: ../Beremiz_service.py:334 +msgid "Name must not be null!" +msgstr "Name must not be null!" + +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:86 +#: ../dialogs/ConnectionDialog.py:76 +msgid "Name:" +msgstr "Name:" + +#: ../plcopen/iec_std.csv:24 +msgid "Natural logarithm" +msgstr "Natural logarithm" + +#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:519 +msgid "Negated" +msgstr "Negated" + +#: ../Beremiz_service.py:580 +msgid "Nevow Web service failed. " +msgstr "Nevow Web service failed. " + +#: ../Beremiz_service.py:556 +msgid "Nevow/Athena import failed :" +msgstr "Nevow/Athena import failed :" + +#: ../BeremizIDE.py:216 ../BeremizIDE.py:251 ../PLCOpenEditor.py:104 +#: ../PLCOpenEditor.py:146 +msgid "New" +msgstr "New" + +#: ../controls/CustomEditableListBox.py:40 +msgid "New item" +msgstr "New item" + +#: ../editors/Viewer.py:518 +msgid "No Modifier" +msgstr "No Modifier" + +#: ../ProjectController.py:1826 +msgid "No PLC to transfer (did build succeed ?)\n" +msgstr "No PLC to transfer (did build succeed ?)\n" + +#: ../PLCGenerator.py:1631 +#, python-format +msgid "No body defined in \"%s\" POU" +msgstr "No body defined in \"%s\" POU" + +#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#, python-brace-format +msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" +msgstr "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" + +#: ../PLCOpenEditor.py:357 +msgid "" +"No documentation available.\n" +"Coming soon." +msgstr "" +"No documentation available.\n" +"Coming soon." + +#: ../PLCGenerator.py:829 +#, python-format +msgid "No informations found for \"%s\" block" +msgstr "No informations found for \"%s\" block" + +#: ../PLCGenerator.py:1194 +#, python-brace-format +msgid "No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken" +msgstr "No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken" + +#: ../controls/SearchResultPanel.py:169 +msgid "No search results available." +msgstr "No search results available." + +#: ../svgui/svgui.py:134 +#, python-format +msgid "No such SVG file: %s\n" +msgstr "No such SVG file: %s\n" + +#: ../canfestival/config_utils.py:639 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) (variable {a3})" +msgstr "No such index/subindex ({a1},{a2}) (variable {a3})" + +#: ../canfestival/config_utils.py:362 +#, python-brace-format +msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" +msgstr "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" + +#: ../dialogs/BrowseValuesLibraryDialog.py:83 +msgid "No valid value selected!" +msgstr "No valid value selected!" + +#: ../PLCGenerator.py:1629 +#, python-format +msgid "No variable defined in \"%s\" POU" +msgstr "No variable defined in \"%s\" POU" + +#: ../canfestival/config_utils.py:355 +#, python-brace-format +msgid "Non existing node ID : {a1} (variable {a2})" +msgstr "Non existing node ID : {a1} (variable {a2})" + +#: ../controls/VariablePanel.py:64 +msgid "Non-Retain" +msgstr "Non-Retain" + +#: ../dialogs/LDElementDialog.py:75 +msgid "Normal" +msgstr "Normal" + +#: ../canfestival/config_utils.py:389 +#, python-brace-format +msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" +msgstr "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" + +#: ../plcopen/iec_std.csv:80 +msgid "Not equal to" +msgstr "Not equal to" + +#: ../dialogs/SFCDivergenceDialog.py:89 +msgid "Number of sequences:" +msgstr "Number of sequences:" + +#: ../plcopen/iec_std.csv:22 +msgid "Numerical" +msgstr "Numerical" + +#: ../editors/CodeFileEditor.py:739 +msgid "OnChange" +msgstr "OnChange" + +#: ../dialogs/SearchInProjectDialog.py:84 +msgid "Only Elements" +msgstr "Only Elements" + +#: ../BeremizIDE.py:218 ../BeremizIDE.py:252 ../PLCOpenEditor.py:106 +#: ../PLCOpenEditor.py:147 +msgid "Open" +msgstr "Open" + +#: ../svgui/svgui.py:143 +msgid "Open Inkscape" +msgstr "Open Inkscape" + +#: ../version.py:77 +msgid "Open Source framework for automation, implemented IEC 61131 IDE with constantly growing set of extensions and flexible PLC runtime." +msgstr "Open Source framework for automation, implemented IEC 61131 IDE with constantly growing set of extensions and flexible PLC runtime." + +#: ../ProjectController.py:1878 +msgid "Open a file explorer to manage project files" +msgstr "Open a file explorer to manage project files" + +#: ../wxglade_hmi/wxglade_hmi.py:155 +msgid "Open wxGlade" +msgstr "Open wxGlade" + +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Option" +msgstr "Option" + +#: ../dialogs/FindInPouDialog.py:81 ../editors/CodeFileEditor.py:739 +msgid "Options" +msgstr "Options" + +#: ../controls/ProjectPropertiesPanel.py:98 +msgid "Organization (optional):" +msgstr "Organization (optional):" + +#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +msgid "Other Profile" +msgstr "Other Profile" + +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:40 +#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:290 +#: ../editors/Viewer.py:1628 ../editors/TextViewer.py:307 +#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +msgid "Output" +msgstr "Output" + +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +msgid "PDO Receive" +msgstr "PDO Receive" + +#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +msgid "PDO Transmit" +msgstr "PDO Transmit" + +#: ../targets/toolchain_gcc.py:167 +msgid "PLC :\n" +msgstr "PLC :\n" + +#: ../BeremizIDE.py:355 +msgid "PLC Log" +msgstr "PLC Log" + +#: ../ProjectController.py:1054 +msgid "PLC code generation failed !\n" +msgstr "PLC code generation failed !\n" + +#: ../Beremiz_service.py:297 +msgid "PLC is empty or already started." +msgstr "PLC is empty or already started." + +#: ../Beremiz_service.py:304 +msgid "PLC is not started." +msgstr "PLC is not started." + +#: ../PLCOpenEditor.py:206 ../PLCOpenEditor.py:319 +#, python-brace-format +msgid "" +"PLC syntax error at line {a1}:\n" +"{a2}" +msgstr "" +"PLC syntax error at line {a1}:\n" +"{a2}" + +#: ../PLCOpenEditor.py:302 ../PLCOpenEditor.py:383 +msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" +msgstr "PLCOpen files (*.xml)|*.xml|All files|*.*" + +#: ../PLCOpenEditor.py:154 ../PLCOpenEditor.py:219 +msgid "PLCOpenEditor" +msgstr "PLCOpenEditor" + +#: ../PLCOpenEditor.py:365 +msgid "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " +msgstr "" +"PLCOpenEditor is part of Beremiz project.\n" +"\n" +"Beremiz is an " + +#: ../dialogs/DiscoveryDialog.py:95 +msgid "PORT" +msgstr "PORT" + +#: ../dialogs/PouDialog.py:101 +msgid "POU Name" +msgstr "POU Name" + +#: ../dialogs/PouDialog.py:58 +msgid "POU Name:" +msgstr "POU Name:" + +#: ../dialogs/PouDialog.py:103 +msgid "POU Type" +msgstr "POU Type" + +#: ../dialogs/PouDialog.py:65 +msgid "POU Type:" +msgstr "POU Type:" + +#: ../connectors/PYRO/__init__.py:45 +#, python-format +msgid "PYRO connecting to URI : %s\n" +msgstr "PYRO connecting to URI : %s\n" + +#: ../connectors/PYRO/__init__.py:61 +#, python-format +msgid "PYRO using certificates in '%s' \n" +msgstr "PYRO using certificates in '%s' \n" + +#: ../BeremizIDE.py:231 ../PLCOpenEditor.py:120 +msgid "Page Setup" +msgstr "Page Setup" + +#: ../controls/ProjectPropertiesPanel.py:111 +msgid "Page Size (optional):" +msgstr "Page Size (optional):" + +#: ../IDEFrame.py:2613 +#, python-format +msgid "Page: %d" +msgstr "Page: %d" + +#: ../controls/PouInstanceVariablesPanel.py:124 +msgid "Parent instance" +msgstr "Parent instance" + +#: ../editors/Viewer.py:657 ../IDEFrame.py:372 ../IDEFrame.py:426 +msgid "Paste" +msgstr "Paste" + +#: ../IDEFrame.py:1868 +msgid "Paste POU" +msgstr "Paste POU" + +#: ../dialogs/SearchInProjectDialog.py:56 +msgid "Pattern to search:" +msgstr "Pattern to search:" + +#: ../dialogs/LDPowerRailDialog.py:74 +msgid "Pin number:" +msgstr "Pin number:" + +#: ../editors/Viewer.py:2757 ../editors/Viewer.py:3014 +#: ../editors/SFCViewer.py:770 +msgid "Please choose a target" +msgstr "Please choose a target" + +#: ../editors/TextViewer.py:262 +msgid "Please enter a block name" +msgstr "Please enter a block name" + +#: ../editors/Viewer.py:2627 ../editors/Viewer.py:3056 +msgid "Please enter comment text" +msgstr "Please enter comment text" + +#: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 +#: ../editors/SFCViewer.py:799 +msgid "Please enter step name" +msgstr "Please enter step name" + +#: ../Beremiz_service.py:196 +msgid "Please enter text" +msgstr "Please enter text" + +#: ../dialogs/ForceVariableDialog.py:163 +#, python-format +msgid "Please enter value for a \"%s\" variable:" +msgstr "Please enter value for a \"%s\" variable:" + +#: ../Beremiz_service.py:319 +msgid "Port number must be 0 <= port <= 65535!" +msgstr "Port number must be 0 <= port <= 65535!" + +#: ../Beremiz_service.py:319 +msgid "Port number must be an integer!" +msgstr "Port number must be an integer!" + +#: ../editors/Viewer.py:595 ../editors/Viewer.py:2416 +msgid "Power Rail" +msgstr "Power Rail" + +#: ../dialogs/LDPowerRailDialog.py:51 +msgid "Power Rail Properties" +msgstr "Power Rail Properties" + +#: ../BeremizIDE.py:233 ../PLCOpenEditor.py:122 +msgid "Preview" +msgstr "Preview" + +#: ../dialogs/BlockPreviewDialog.py:57 +msgid "Preview:" +msgstr "Preview:" + +#: ../BeremizIDE.py:235 ../BeremizIDE.py:255 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:150 +msgid "Print" +msgstr "Print" + +#: ../IDEFrame.py:1079 +msgid "Print preview" +msgstr "Print preview" + +#: ../editors/ResourceEditor.py:68 +msgid "Priority" +msgstr "Priority" + +#: ../dialogs/SFCTransitionDialog.py:90 +msgid "Priority:" +msgstr "Priority:" + +#: ../runtime/PLCObject.py:369 +#, python-format +msgid "Problem starting PLC : error %d" +msgstr "Problem starting PLC : error %d" + +#: ../dialogs/ProjectDialog.py:58 +msgid "Product Name" +msgstr "Product Name" + +#: ../controls/ProjectPropertiesPanel.py:81 +msgid "Product Name (required):" +msgstr "Product Name (required):" + +#: ../controls/ProjectPropertiesPanel.py:83 +msgid "Product Release (optional):" +msgstr "Product Release (optional):" + +#: ../dialogs/ProjectDialog.py:59 +msgid "Product Version" +msgstr "Product Version" + +#: ../controls/ProjectPropertiesPanel.py:82 +msgid "Product Version (required):" +msgstr "Product Version (required):" + +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1747 +#: ../IDEFrame.py:1944 +msgid "Program" +msgstr "Program" + +#: ../PLCOpenEditor.py:347 +msgid "Program was successfully generated!" +msgstr "Program was successfully generated!" + +#: ../PLCControler.py:98 +msgid "Programs" +msgstr "Programs" + +#: ../editors/Viewer.py:243 +msgid "Programs can't be used by other POUs!" +msgstr "Programs can't be used by other POUs!" + +#: ../controls/ProjectPropertiesPanel.py:85 ../IDEFrame.py:584 +msgid "Project" +msgstr "Project" + +#: ../controls/SearchResultPanel.py:173 +#, python-format +msgid "Project '%s':" +msgstr "Project '%s':" + +#: ../ProjectController.py:1877 +msgid "Project Files" +msgstr "Project Files" + +#: ../dialogs/ProjectDialog.py:57 +msgid "Project Name" +msgstr "Project Name" + +#: ../controls/ProjectPropertiesPanel.py:79 +msgid "Project Name (required):" +msgstr "Project Name (required):" + +#: ../controls/ProjectPropertiesPanel.py:80 +msgid "Project Version (optional):" +msgstr "Project Version (optional):" + +#: ../PLCControler.py:3164 +msgid "" +"Project file syntax error:\n" +"\n" +msgstr "" +"Project file syntax error:\n" +"\n" + +#: ../dialogs/ProjectDialog.py:33 ../editors/ProjectNodeEditor.py:37 +msgid "Project properties" +msgstr "Project properties" + +#: ../ConfigTreeNode.py:566 +#, python-brace-format +msgid "Project tree layout do not match confnode.xml {a1}!={a2} " +msgstr "Project tree layout do not match confnode.xml {a1}!={a2} " + +#: ../dialogs/ConnectionDialog.py:98 +msgid "Propagate Name" +msgstr "Propagate Name" + +#: ../PLCControler.py:99 +msgid "Properties" +msgstr "Properties" + +#: ../Beremiz_service.py:442 +msgid "Publishing service on local network" +msgstr "Publishing service on local network" + +#: ../connectors/PYRO/__init__.py:118 +#, python-format +msgid "Pyro exception: %s\n" +msgstr "Pyro exception: %s\n" + +#: ../Beremiz_service.py:429 +msgid "Pyro object's uri :" +msgstr "Pyro object's uri :" + +#: ../Beremiz_service.py:428 +msgid "Pyro port :" +msgstr "Pyro port :" + +#: ../py_ext/PythonEditor.py:81 +msgid "Python code" +msgstr "Python code" + +#: ../features.py:33 +msgid "Python file" +msgstr "Python file" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Qualifier" +msgstr "Qualifier" + +#: ../BeremizIDE.py:238 ../PLCOpenEditor.py:130 ../Beremiz_service.py:275 +msgid "Quit" +msgstr "Quit" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +msgid "Range:" +msgstr "Range:" + +#: ../ProjectController.py:1873 +msgid "Raw IEC code" +msgstr "Raw IEC code" + +#: ../BeremizIDE.py:1047 +#, python-format +msgid "Really delete node '%s'?" +msgstr "Really delete node '%s'?" + +#: ../IDEFrame.py:362 ../IDEFrame.py:422 +msgid "Redo" +msgstr "Redo" + +#: ../dialogs/SFCTransitionDialog.py:75 +msgid "Reference" +msgstr "Reference" + +#: ../dialogs/DiscoveryDialog.py:107 ../IDEFrame.py:432 +msgid "Refresh" +msgstr "Refresh" + +#: ../dialogs/SearchInProjectDialog.py:66 +msgid "Regular expression" +msgstr "Regular expression" + +#: ../dialogs/FindInPouDialog.py:96 +msgid "Regular expressions" +msgstr "Regular expressions" + +#: ../editors/Viewer.py:1603 +msgid "Release value" +msgstr "Release value" + +#: ../plcopen/iec_std.csv:37 +msgid "Remainder (modulo)" +msgstr "Remainder (modulo)" + +#: ../BeremizIDE.py:1048 +#, python-format +msgid "Remove %s node" +msgstr "Remove %s node" + +#: ../IDEFrame.py:2419 +msgid "Remove Datatype" +msgstr "Remove Datatype" + +#: ../IDEFrame.py:2424 +msgid "Remove Pou" +msgstr "Remove Pou" + +#: ../dialogs/ActionBlockDialog.py:138 +msgid "Remove action" +msgstr "Remove action" + +#: ../editors/DataTypeEditor.py:353 +msgid "Remove element" +msgstr "Remove element" + +#: ../editors/FileManagementPanel.py:63 +msgid "Remove file from left folder" +msgstr "Remove file from left folder" + +#: ../editors/ResourceEditor.py:269 +msgid "Remove instance" +msgstr "Remove instance" + +#: ../canfestival/NetworkEditor.py:104 +msgid "Remove slave" +msgstr "Remove slave" + +#: ../editors/ResourceEditor.py:240 +msgid "Remove task" +msgstr "Remove task" + +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +msgid "Remove variable" +msgstr "Remove variable" + +#: ../IDEFrame.py:1948 +msgid "Rename" +msgstr "Rename" + +#: ../editors/FileManagementPanel.py:181 +msgid "Replace File" +msgstr "Replace File" + +#: ../editors/Viewer.py:561 +msgid "Replace Wire by connections" +msgstr "Replace Wire by connections" + +#: ../plcopen/iec_std.csv:89 +msgid "Replacement (within)" +msgstr "Replacement (within)" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Reset" +msgstr "Reset" + +#: ../editors/Viewer.py:642 +msgid "Reset Execution Order" +msgstr "Reset Execution Order" + +#: ../IDEFrame.py:451 +msgid "Reset Perspective" +msgstr "Reset Perspective" + +#: ../controls/SearchResultPanel.py:105 +msgid "Reset search result" +msgstr "Reset search result" + +#: ../BeremizIDE.py:979 ../PLCControler.py:99 +msgid "Resources" +msgstr "Resources" + +#: ../controls/VariablePanel.py:62 +msgid "Retain" +msgstr "Retain" + +#: ../controls/VariablePanel.py:424 +msgid "Return Type:" +msgstr "Return Type:" + +#: ../editors/Viewer.py:546 +msgid "Right" +msgstr "Right" + +#: ../dialogs/LDPowerRailDialog.py:64 +msgid "Right PowerRail" +msgstr "Right PowerRail" + +#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:520 +msgid "Rising Edge" +msgstr "Rising Edge" + +#: ../plcopen/iec_std.csv:65 +msgid "Rotate left" +msgstr "Rotate left" + +#: ../plcopen/iec_std.csv:64 +msgid "Rotate right" +msgstr "Rotate right" + +#: ../plcopen/iec_std.csv:17 +msgid "Rounding up/down" +msgstr "Rounding up/down" + +#: ../ProjectController.py:1841 +msgid "Run" +msgstr "Run" + +#: ../ProjectController.py:1099 +msgid "Runtime IO extensions C code generation failed !\n" +msgstr "Runtime IO extensions C code generation failed !\n" + +#: ../ProjectController.py:1108 +msgid "Runtime library extensions C code generation failed !\n" +msgstr "Runtime library extensions C code generation failed !\n" + +#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +msgid "SDO Client" +msgstr "SDO Client" + +#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +msgid "SDO Server" +msgstr "SDO Server" + +#: ../dialogs/PouDialog.py:37 ../controls/ProjectPropertiesPanel.py:144 +msgid "SFC" +msgstr "SFC" + +#: ../PLCGenerator.py:1392 +#, python-brace-format +msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" +msgstr "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" + +#: ../PLCGenerator.py:773 +#, python-format +msgid "SFC transition in POU \"%s\" must be connected." +msgstr "SFC transition in POU \"%s\" must be connected." + +#: ../dialogs/PouTransitionDialog.py:36 ../dialogs/PouActionDialog.py:32 +#: ../dialogs/PouDialog.py:37 +msgid "ST" +msgstr "ST" + +#: ../PLCOpenEditor.py:334 +msgid "ST files (*.st)|*.st|All files|*.*" +msgstr "ST files (*.st)|*.st|All files|*.*" + +#: ../svgui/svgui.py:128 +msgid "SVG files (*.svg)|*.svg|All files|*.*" +msgstr "SVG files (*.svg)|*.svg|All files|*.*" + +#: ../features.py:35 +msgid "SVGUI" +msgstr "SVGUI" + +#: ../BeremizIDE.py:222 ../BeremizIDE.py:253 ../PLCOpenEditor.py:113 +#: ../PLCOpenEditor.py:148 +msgid "Save" +msgstr "Save" + +#: ../BeremizIDE.py:254 ../PLCOpenEditor.py:115 ../PLCOpenEditor.py:149 +msgid "Save As..." +msgstr "Save As..." + +#: ../BeremizIDE.py:224 +msgid "Save as" +msgstr "Save as" + +#: ../ProjectController.py:511 +msgid "Save path is the same as path of a project! \n" +msgstr "Save path is the same as path of a project! \n" + +#: ../dialogs/SearchInProjectDialog.py:69 +msgid "Scope" +msgstr "Scope" + +#: ../IDEFrame.py:623 +msgid "Search" +msgstr "Search" + +#: ../dialogs/SearchInProjectDialog.py:45 ../IDEFrame.py:382 ../IDEFrame.py:428 +msgid "Search in Project" +msgstr "Search in Project" + +#: ../dialogs/DurationEditorDialog.py:47 +msgid "Seconds:" +msgstr "Seconds:" + +#: ../IDEFrame.py:388 +msgid "Select All" +msgstr "Select All" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Select a variable class:" +msgstr "Select a variable class:" + +#: ../ProjectController.py:1257 +msgid "Select an editor:" +msgstr "Select an editor:" + +#: ../controls/PouInstanceVariablesPanel.py:281 +msgid "Select an instance" +msgstr "Select an instance" + +#: ../IDEFrame.py:607 +msgid "Select an object" +msgstr "Select an object" + +#: ../ProjectController.py:518 +msgid "Selected directory already contains another project. Overwrite? \n" +msgstr "Selected directory already contains another project. Overwrite? \n" + +#: ../plcopen/iec_std.csv:70 +msgid "Selection" +msgstr "Selection" + +#: ../dialogs/SFCDivergenceDialog.py:65 +msgid "Selection Convergence" +msgstr "Selection Convergence" + +#: ../dialogs/SFCDivergenceDialog.py:64 +msgid "Selection Divergence" +msgstr "Selection Divergence" + +#: ../dialogs/DiscoveryDialog.py:82 +msgid "Service Discovery" +msgstr "Service Discovery" + +#: ../dialogs/DiscoveryDialog.py:85 +msgid "Services available:" +msgstr "Services available:" + +#: ../dialogs/LDElementDialog.py:76 +msgid "Set" +msgstr "Set" + +#: ../plcopen/iec_std.csv:62 +msgid "Shift left" +msgstr "Shift left" + +#: ../plcopen/iec_std.csv:63 +msgid "Shift right" +msgstr "Shift right" + +#: ../ProjectController.py:1867 +msgid "Show IEC code generated by PLCGenerator" +msgstr "Show IEC code generated by PLCGenerator" + +#: ../canfestival/canfestival.py:389 +msgid "Show Master" +msgstr "Show Master" + +#: ../canfestival/canfestival.py:390 +msgid "Show Master generated by config_utils" +msgstr "Show Master generated by config_utils" + +#: ../ProjectController.py:1865 +msgid "Show code" +msgstr "Show code" + +#: ../dialogs/SFCDivergenceDialog.py:67 +msgid "Simultaneous Convergence" +msgstr "Simultaneous Convergence" + +#: ../dialogs/SFCDivergenceDialog.py:66 +msgid "Simultaneous Divergence" +msgstr "Simultaneous Divergence" + +#: ../plcopen/iec_std.csv:27 +msgid "Sine" +msgstr "Sine" + +#: ../editors/ResourceEditor.py:68 +msgid "Single" +msgstr "Single" + +#: ../targets/toolchain_makefile.py:126 +msgid "Source didn't change, no build.\n" +msgstr "Source didn't change, no build.\n" + +#: ../PLCGenerator.py:397 +#, python-brace-format +msgid "Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'." +msgstr "Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'." + +#: ../plcopen/iec_std.csv:23 +msgid "Square root (base 2)" +msgstr "Square root (base 2)" + +#: ../plcopen/definitions.py:48 +msgid "Standard function blocks" +msgstr "Standard function blocks" + +#: ../ProjectController.py:1843 ../Beremiz_service.py:263 +msgid "Start PLC" +msgstr "Start PLC" + +#: ../ProjectController.py:1046 +#, python-format +msgid "Start build in %s\n" +msgstr "Start build in %s\n" + +#: ../ProjectController.py:1360 +msgid "Started" +msgstr "Started" + +#: ../ProjectController.py:1648 +msgid "Starting PLC\n" +msgstr "Starting PLC\n" + +#: ../BeremizIDE.py:365 +msgid "Status ToolBar" +msgstr "Status ToolBar" + +#: ../editors/Viewer.py:612 ../editors/Viewer.py:2391 +msgid "Step" +msgstr "Step" + +#: ../ProjectController.py:1846 +msgid "Stop" +msgstr "Stop" + +#: ../Beremiz_service.py:264 +msgid "Stop PLC" +msgstr "Stop PLC" + +#: ../ProjectController.py:1848 +msgid "Stop Running PLC" +msgstr "Stop Running PLC" + +#: ../ProjectController.py:1361 +msgid "Stopped" +msgstr "Stopped" + +#: ../ProjectController.py:1620 +msgid "Stopping debugger...\n" +msgstr "Stopping debugger...\n" + +#: ../editors/DataTypeEditor.py:54 +msgid "Structure" +msgstr "Structure" + +#: ../editors/DataTypeEditor.py:54 +msgid "Subrange" +msgstr "Subrange" + +#: ../plcopen/iec_std.csv:35 +msgid "Subtraction" +msgstr "Subtraction" + +#: ../ProjectController.py:1085 +msgid "Successfully built.\n" +msgstr "Successfully built.\n" + +#: ../IDEFrame.py:447 +msgid "Switch perspective" +msgstr "Switch perspective" + +#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:115 +msgid "Syntax error in regular expression of pattern to search!" +msgstr "Syntax error in regular expression of pattern to search!" + +#: ../dialogs/DiscoveryDialog.py:93 +msgid "TYPE" +msgstr "TYPE" + +#: ../plcopen/iec_std.csv:29 +msgid "Tangent" +msgstr "Tangent" + +#: ../editors/ResourceEditor.py:83 +msgid "Task" +msgstr "Task" + +#: ../editors/ResourceEditor.py:235 +msgid "Tasks:" +msgstr "Tasks:" + +#: ../controls/VariablePanel.py:73 +msgid "Temp" +msgstr "Temp" + +#: ../version.py:30 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + +#: ../editors/FileManagementPanel.py:180 +#, python-format +msgid "" +"The file '%s' already exist.\n" +"Do you want to replace it?" +msgstr "" +"The file '%s' already exist.\n" +"Do you want to replace it?" + +#: ../editors/LDViewer.py:882 +msgid "The group of block must be coherent!" +msgstr "The group of block must be coherent!" + +#: ../BeremizIDE.py:542 ../IDEFrame.py:1015 +msgid "There are changes, do you want to save?" +msgstr "There are changes, do you want to save?" + +#: ../IDEFrame.py:1658 ../IDEFrame.py:1677 +#, python-format +msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" +msgstr "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" + +#: ../IDEFrame.py:1102 +msgid "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" +msgstr "" +"There was a problem printing.\n" +"Perhaps your current printer is not set correctly?" + +#: ../editors/LDViewer.py:891 +msgid "This option isn't available yet!" +msgstr "This option isn't available yet!" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#, python-format +msgid "Tick: %d" +msgstr "Tick: %d" + +#: ../plcopen/iec_std.csv:40 +msgid "Time" +msgstr "Time" + +#: ../plcopen/iec_std.csv:40 ../plcopen/iec_std.csv:41 +msgid "Time addition" +msgstr "Time addition" + +#: ../plcopen/iec_std.csv:86 +msgid "Time concatenation" +msgstr "Time concatenation" + +#: ../plcopen/iec_std.csv:60 ../plcopen/iec_std.csv:61 +msgid "Time division" +msgstr "Time division" + +#: ../plcopen/iec_std.csv:46 ../plcopen/iec_std.csv:47 +msgid "Time multiplication" +msgstr "Time multiplication" + +#: ../plcopen/iec_std.csv:48 ../plcopen/iec_std.csv:49 +msgid "Time subtraction" +msgstr "Time subtraction" + +#: ../plcopen/iec_std.csv:42 ../plcopen/iec_std.csv:43 +msgid "Time-of-day addition" +msgstr "Time-of-day addition" + +#: ../plcopen/iec_std.csv:52 ../plcopen/iec_std.csv:53 +#: ../plcopen/iec_std.csv:54 ../plcopen/iec_std.csv:55 +msgid "Time-of-day subtraction" +msgstr "Time-of-day subtraction" + +#: ../dialogs/ForceVariableDialog.py:172 +msgid "Toggle value" +msgstr "Toggle value" + +#: ../editors/Viewer.py:548 +msgid "Top" +msgstr "Top" + +#: ../ProjectController.py:1855 +msgid "Transfer" +msgstr "Transfer" + +#: ../ProjectController.py:1857 +msgid "Transfer PLC" +msgstr "Transfer PLC" + +#: ../ProjectController.py:1820 +msgid "Transfer completed successfully.\n" +msgstr "Transfer completed successfully.\n" + +#: ../ProjectController.py:1823 +msgid "Transfer failed\n" +msgstr "Transfer failed\n" + +#: ../editors/Viewer.py:613 ../editors/Viewer.py:2393 ../editors/Viewer.py:2420 +msgid "Transition" +msgstr "Transition" + +#: ../PLCGenerator.py:1518 +#, python-format +msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" +msgstr "Transition \"%s\" body must contain an output variable or coil referring to its name" + +#: ../dialogs/PouTransitionDialog.py:84 +msgid "Transition Name" +msgstr "Transition Name" + +#: ../dialogs/PouTransitionDialog.py:53 +msgid "Transition Name:" +msgstr "Transition Name:" + +#: ../PLCGenerator.py:1609 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" +msgstr "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" + +#: ../PLCGenerator.py:1598 +#, python-brace-format +msgid "Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU" +msgstr "Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU" + +#: ../plcopen/plcopen.py:1323 +#, python-format +msgid "Transition with name %s doesn't exist!" +msgstr "Transition with name %s doesn't exist!" + +#: ../PLCControler.py:98 +msgid "Transitions" +msgstr "Transitions" + +#: ../dialogs/AboutDialog.py:131 +msgid "Translated by" +msgstr "Translated by" + +#: ../editors/ResourceEditor.py:68 +msgid "Triggering" +msgstr "Triggering" + +#: ../Beremiz_service.py:478 +msgid "Twisted unavailable." +msgstr "Twisted unavailable." + +#: ../dialogs/ActionBlockDialog.py:39 ../editors/ResourceEditor.py:83 +#: ../editors/DataTypeEditor.py:50 ../editors/CodeFileEditor.py:739 +#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +msgid "Type" +msgstr "Type" + +#: ../dialogs/BrowseLocationsDialog.py:49 +msgid "Type and derivated" +msgstr "Type and derivated" + +#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#, python-format +msgid "Type conflict for location \"%s\"" +msgstr "Type conflict for location \"%s\"" + +#: ../plcopen/iec_std.csv:16 +msgid "Type conversion" +msgstr "Type conversion" + +#: ../editors/DataTypeEditor.py:162 +msgid "Type infos:" +msgstr "Type infos:" + +#: ../dialogs/BrowseLocationsDialog.py:50 +msgid "Type strict" +msgstr "Type strict" + +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:57 ../dialogs/BrowseLocationsDialog.py:100 +#: ../dialogs/FBDBlockDialog.py:66 ../dialogs/ConnectionDialog.py:59 +msgid "Type:" +msgstr "Type:" + +#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#, python-format +msgid "Unable to define PDO mapping for node %02x" +msgstr "Unable to define PDO mapping for node %02x" + +#: ../targets/Xenomai/__init__.py:39 +#, python-format +msgid "Unable to get Xenomai's %s \n" +msgstr "Unable to get Xenomai's %s \n" + +#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#, python-brace-format +msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" +msgstr "Undefined block type \"{a1}\" in \"{a2}\" POU" + +#: ../PLCGenerator.py:254 +#, python-format +msgid "Undefined pou type \"%s\"" +msgstr "Undefined pou type \"%s\"" + +#: ../IDEFrame.py:360 ../IDEFrame.py:421 +msgid "Undo" +msgstr "Undo" + +#: ../ProjectController.py:423 +msgid "Unknown" +msgstr "Unknown" + +#: ../editors/Viewer.py:394 +#, python-format +msgid "Unknown variable \"%s\" for this POU!" +msgstr "Unknown variable \"%s\" for this POU!" + +#: ../ProjectController.py:420 ../ProjectController.py:421 +msgid "Unnamed" +msgstr "Unnamed" + +#: ../PLCControler.py:638 +#, python-format +msgid "Unnamed%d" +msgstr "Unnamed%d" + +#: ../controls/VariablePanel.py:284 +#, python-format +msgid "Unrecognized data size \"%s\"" +msgstr "Unrecognized data size \"%s\"" + +#: ../editors/DataTypeEditor.py:630 ../controls/VariablePanel.py:827 +msgid "User Data Types" +msgstr "User Data Types" + +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +msgid "User Type" +msgstr "User Type" + +#: ../PLCControler.py:97 +msgid "User-defined POUs" +msgstr "User-defined POUs" + +#: ../dialogs/ActionBlockDialog.py:39 +msgid "Value" +msgstr "Value" + +#: ../editors/DataTypeEditor.py:259 +msgid "Values:" +msgstr "Values:" + +#: ../dialogs/ActionBlockDialog.py:43 ../editors/Viewer.py:585 +#: ../editors/Viewer.py:2423 +msgid "Variable" +msgstr "Variable" + +#: ../editors/Viewer.py:309 ../editors/Viewer.py:339 ../editors/Viewer.py:361 +#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 +#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +msgid "Variable Drop" +msgstr "Variable Drop" + +#: ../dialogs/FBDVariableDialog.py:64 +msgid "Variable Properties" +msgstr "Variable Properties" + +#: ../editors/Viewer.py:289 ../editors/TextViewer.py:306 +#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 +#: ../controls/VariablePanel.py:350 +msgid "Variable class" +msgstr "Variable class" + +#: ../editors/Viewer.py:396 ../editors/TextViewer.py:387 +msgid "Variable don't belong to this POU!" +msgstr "Variable don't belong to this POU!" + +#: ../dialogs/LDElementDialog.py:89 +msgid "Variable:" +msgstr "Variable:" + +#: ../controls/VariablePanel.py:72 +msgid "Variables" +msgstr "Variables" + +#: ../controls/ProjectPropertiesPanel.py:152 +msgid "Vertical:" +msgstr "Vertical:" + +#: ../Beremiz_service.py:588 +msgid "WAMP client startup failed. " +msgstr "WAMP client startup failed. " + +#: ../connectors/WAMP/__init__.py:91 +#, python-format +msgid "WAMP connecting to URL : %s\n" +msgstr "WAMP connecting to URL : %s\n" + +#: ../connectors/WAMP/__init__.py:131 +msgid "WAMP connection timeout" +msgstr "WAMP connection timeout" + +#: ../connectors/WAMP/__init__.py:150 +#, python-format +msgid "WAMP connection to '%s' failed.\n" +msgstr "WAMP connection to '%s' failed.\n" + +#: ../Beremiz_service.py:564 +msgid "WAMP import failed :" +msgstr "WAMP import failed :" + +#: ../wxglade_hmi/wxglade_hmi.py:37 +msgid "WXGLADE GUI" +msgstr "WXGLADE GUI" + +#: ../dialogs/PouDialog.py:129 ../editors/LDViewer.py:891 +msgid "Warning" +msgstr "Warning" + +#: ../ProjectController.py:707 +msgid "Warnings in ST/IL/SFC code generator :\n" +msgstr "Warnings in ST/IL/SFC code generator :\n" + +#: ../dialogs/SearchInProjectDialog.py:78 +msgid "Whole Project" +msgstr "Whole Project" + +#: ../controls/ProjectPropertiesPanel.py:120 +msgid "Width:" +msgstr "Width:" + +#: ../dialogs/FindInPouDialog.py:91 +msgid "Wrap search" +msgstr "Wrap search" + +#: ../dialogs/AboutDialog.py:130 +msgid "Written by" +msgstr "Written by" + +#: ../features.py:34 +msgid "WxGlade GUI" +msgstr "WxGlade GUI" + +#: ../svgui/svgui.py:142 +msgid "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" +msgstr "" +"You don't have write permissions.\n" +"Open Inkscape anyway ?" + +#: ../wxglade_hmi/wxglade_hmi.py:154 +msgid "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" +msgstr "" +"You don't have write permissions.\n" +"Open wxGlade anyway ?" + +#: ../ProjectController.py:371 +msgid "" +"You must have permission to work on the project\n" +"Work on a project copy ?" +msgstr "" +"You must have permission to work on the project\n" +"Work on a project copy ?" + +#: ../editors/LDViewer.py:886 +msgid "You must select the block or group of blocks around which a branch should be added!" +msgstr "You must select the block or group of blocks around which a branch should be added!" + +#: ../editors/LDViewer.py:666 +msgid "You must select the wire where a contact should be added!" +msgstr "You must select the wire where a contact should be added!" + +#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +msgid "You must type a name!" +msgstr "You must type a name!" + +#: ../dialogs/ForceVariableDialog.py:193 +msgid "You must type a value!" +msgstr "You must type a value!" + +#: ../IDEFrame.py:438 +msgid "Zoom" +msgstr "Zoom" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "days" +msgstr "days" + +#: ../PLCOpenEditor.py:343 +#, python-format +msgid "error: %s\n" +msgstr "error: %s\n" + +#: ../util/ProcessLogger.py:169 +#, python-brace-format +msgid "exited with status {a1} (pid {a2})\n" +msgstr "exited with status {a1} (pid {a2})\n" + +#: ../PLCOpenEditor.py:406 ../PLCOpenEditor.py:408 +msgid "file : " +msgstr "file : " + +#: ../dialogs/PouDialog.py:32 +msgid "function" +msgstr "function" + +#: ../PLCOpenEditor.py:409 +msgid "function : " +msgstr "function : " + +#: ../dialogs/PouDialog.py:32 +msgid "functionBlock" +msgstr "functionBlock" + +#: ../dialogs/DurationEditorDialog.py:155 +msgid "hours" +msgstr "hours" + +#: ../PLCOpenEditor.py:409 +msgid "line : " +msgstr "line : " + +#: ../dialogs/DurationEditorDialog.py:157 +msgid "milliseconds" +msgstr "milliseconds" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "minutes" +msgstr "minutes" + +#: ../dialogs/PouDialog.py:32 +msgid "program" +msgstr "program" + +#: ../dialogs/DurationEditorDialog.py:156 +msgid "seconds" +msgstr "seconds" + +#: ../plcopen/iec_std.csv:84 +msgid "string from the middle" +msgstr "string from the middle" + +#: ../plcopen/iec_std.csv:82 +msgid "string left of" +msgstr "string left of" + +#: ../plcopen/iec_std.csv:83 +msgid "string right of" +msgstr "string right of" + +#: ../Beremiz.py:164 +msgid "update info unavailable." +msgstr "update info unavailable." + +#: ../PLCOpenEditor.py:341 +#, python-format +msgid "warning: %s\n" +msgstr "warning: %s\n" + +#: ../PLCControler.py:972 +#, python-brace-format +msgid "{a1} \"{a2}\" can't be pasted as a {a3}." +msgstr "{a1} \"{a2}\" can't be pasted as a {a3}." + +#: ../ConfigTreeNode.py:56 +#, python-brace-format +msgid "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" +msgstr "" +"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a3}" + +#: Extra XSD strings +msgid "CanFestivalSlaveNode" +msgstr "CanFestivalSlaveNode" + +msgid "CAN_Device" +msgstr "CAN_Device" + +msgid "CAN_Baudrate" +msgstr "CAN_Baudrate" + +msgid "NodeId" +msgstr "NodeId" + +msgid "Sync_Align" +msgstr "Sync_Align" + +msgid "Sync_Align_Ratio" +msgstr "Sync_Align_Ratio" + +msgid "CanFestivalNode" +msgstr "CanFestivalNode" + +msgid "Sync_TPDOs" +msgstr "Sync_TPDOs" + +msgid "CanFestivalInstance" +msgstr "CanFestivalInstance" + +msgid "CAN_Driver" +msgstr "CAN_Driver" + +msgid "Generic" +msgstr "Generic" + +msgid "Command" +msgstr "Command" + +msgid "Xenomai" +msgstr "Xenomai" + +msgid "XenoConfig" +msgstr "XenoConfig" + +msgid "Compiler" +msgstr "Compiler" + +msgid "CFLAGS" +msgstr "CFLAGS" + +msgid "Linker" +msgstr "Linker" + +msgid "LDFLAGS" +msgstr "LDFLAGS" + +msgid "Linux" +msgstr "Linux" + +msgid "Win32" +msgstr "Win32" + +msgid "BaseParams" +msgstr "BaseParams" + +msgid "IEC_Channel" +msgstr "IEC_Channel" + +msgid "Enabled" +msgstr "Enabled" + +msgid "BeremizRoot" +msgstr "BeremizRoot" + +msgid "TargetType" +msgstr "TargetType" + +msgid "Libraries" +msgstr "Libraries" + +msgid "URI_location" +msgstr "URI_location" + +msgid "Disable_Extensions" +msgstr "Disable_Extensions" + +msgid "%(codefile_name)s" +msgstr "%(codefile_name)s" + +msgid "variables" +msgstr "variables" + +msgid "variable" +msgstr "variable" + +msgid "name" +msgstr "name" + +msgid "type" +msgstr "type" + +msgid "class" +msgstr "class" + +msgid "initial" +msgstr "initial" + +msgid "desc" +msgstr "desc" + +msgid "onchange" +msgstr "onchange" + +msgid "opts" +msgstr "opts" + +#: Extra TC6 documentation strings +msgid "0 - current time, 1 - load time from PDT" +msgstr "0 - current time, 1 - load time from PDT" + +msgid "Preset datetime" +msgstr "Preset datetime" + +msgid "Copy of IN" +msgstr "Copy of IN" + +msgid "Datetime, current or relative to PDT" +msgstr "Datetime, current or relative to PDT" + +msgid "The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." +msgstr "The real time clock has many uses including time stamping, setting dates and times of day in batch reports, in alarm messages and so on." + +msgid "1 = integrate, 0 = hold" +msgstr "1 = integrate, 0 = hold" + +msgid "Overriding reset" +msgstr "Overriding reset" + +msgid "Input variable" +msgstr "Input variable" + +msgid "Initial value" +msgstr "Initial value" + +msgid "Sampling period" +msgstr "Sampling period" + +msgid "NOT R1" +msgstr "NOT R1" + +msgid "Integrated output" +msgstr "Integrated output" + +msgid "The integral function block integrates the value of input XIN over time." +msgstr "The integral function block integrates the value of input XIN over time." + +msgid "0 = reset" +msgstr "0 = reset" + +msgid "Input to be differentiated" +msgstr "Input to be differentiated" + +msgid "Differentiated output" +msgstr "Differentiated output" + +msgid "The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." +msgstr "The derivative function block produces an output XOUT proportional to the rate of change of the input XIN." + +msgid "0 - manual , 1 - automatic" +msgstr "0 - manual , 1 - automatic" + +msgid "Process variable" +msgstr "Process variable" + +msgid "Set point" +msgstr "Set point" + +msgid "Manual output adjustment - Typically from transfer station" +msgstr "Manual output adjustment - Typically from transfer station" + +msgid "Proportionality constant" +msgstr "Proportionality constant" + +msgid "Reset time" +msgstr "Reset time" + +msgid "Derivative time constant" +msgstr "Derivative time constant" + +msgid "PV - SP" +msgstr "PV - SP" + +msgid "FB for integral term" +msgstr "FB for integral term" + +msgid "FB for derivative term" +msgstr "FB for derivative term" + +msgid "The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." +msgstr "The PID (proportional, Integral, Derivative) function block provides the classical three term controller for closed loop control." + +msgid "0 - track X0, 1 - ramp to/track X1" +msgstr "0 - track X0, 1 - ramp to/track X1" + +msgid "Ramp duration" +msgstr "Ramp duration" + +msgid "BUSY = 1 during ramping period" +msgstr "BUSY = 1 during ramping period" + +msgid "Elapsed time of ramp" +msgstr "Elapsed time of ramp" + +msgid "The RAMP function block is modelled on example given in the standard." +msgstr "The RAMP function block is modelled on example given in the standard." + +msgid "The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." +msgstr "The hysteresis function block provides a hysteresis boolean output driven by the difference of two floating point (REAL) inputs XIN1 and XIN2." + +msgid "The SR bistable is a latch where the Set dominates." +msgstr "The SR bistable is a latch where the Set dominates." + +msgid "The RS bistable is a latch where the Reset dominates." +msgstr "The RS bistable is a latch where the Reset dominates." + +msgid "The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." +msgstr "The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." + +msgid "The output produces a single pulse when a rising edge is detected." +msgstr "The output produces a single pulse when a rising edge is detected." + +msgid "The output produces a single pulse when a falling edge is detected." +msgstr "The output produces a single pulse when a falling edge is detected." + +msgid "The up-counter can be used to signal when a count has reached a maximum value." +msgstr "The up-counter can be used to signal when a count has reached a maximum value." + +msgid "The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." +msgstr "The down-counter can be used to signal when a count has reached zero, on counting down from a preset value." + +msgid "The 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." +msgstr "The 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." + +msgid "first input parameter" +msgstr "first input parameter" + +msgid "second input parameter" +msgstr "second input parameter" + +msgid "first output parameter" +msgstr "first output parameter" + +msgid "second output parameter" +msgstr "second output parameter" + +msgid "internal state: 0-reset, 1-counting, 2-set" +msgstr "internal state: 0-reset, 1-counting, 2-set" + +msgid "The pulse timer can be used to generate output pulses of a given time duration." +msgstr "The pulse timer can be used to generate output pulses of a given time duration." + +msgid "The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." +msgstr "The on-delay timer can be used to delay setting an output true, for fixed period after an input becomes true." + +msgid "The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." +msgstr "The off-delay timer can be used to delay setting an output false, for fixed period after input goes false." diff -r c1298e7ffe3a -r 8391c11477f4 i18n/messages.pot --- a/i18n/messages.pot Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/messages.pot Tue Jan 30 16:06:58 2018 +0100 @@ -1,14 +1,14 @@ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. +# This file is distributed under the same license as the Beremiz package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" +"Project-Id-Version: Beremiz\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-01-12 14:39+0300\n" +"POT-Creation-Date: 2017-09-13 15:37+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,7 +17,7 @@ "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../PLCOpenEditor.py:408 ../Beremiz.py:1191 +#: ../util/ExceptionHandler.py:56 #, python-format msgid "" "\n" @@ -32,381 +32,384 @@ "Traceback:\n" msgstr "" -#: ../controls/VariablePanel.py:72 +#: ../controls/VariablePanel.py:89 msgid " External" msgstr "" -#: ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:88 msgid " InOut" msgstr "" -#: ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:88 msgid " Input" msgstr "" -#: ../controls/VariablePanel.py:72 +#: ../controls/VariablePanel.py:89 msgid " Local" msgstr "" -#: ../controls/VariablePanel.py:71 +#: ../controls/VariablePanel.py:88 msgid " Output" msgstr "" -#: ../controls/VariablePanel.py:73 +#: ../controls/VariablePanel.py:90 msgid " Temp" msgstr "" -#: ../dialogs/PouTransitionDialog.py:99 ../dialogs/ProjectDialog.py:66 -#: ../dialogs/PouActionDialog.py:91 ../dialogs/PouDialog.py:113 +#: ../dialogs/PouTransitionDialog.py:99 ../dialogs/ProjectDialog.py:72 +#: ../dialogs/PouActionDialog.py:97 ../dialogs/PouDialog.py:120 #, python-format msgid " and %s" msgstr "" -#: ../ProjectController.py:1089 +#: ../ProjectController.py:1177 msgid " generation failed !\n" msgstr "" -#: ../plcopen/plcopen.py:881 +#: ../plcopen/plcopen.py:936 #, python-format msgid "\"%s\" Data Type doesn't exist !!!" msgstr "" -#: ../plcopen/plcopen.py:899 +#: ../plcopen/plcopen.py:954 #, python-format msgid "\"%s\" POU already exists !!!" msgstr "" -#: ../plcopen/plcopen.py:920 +#: ../plcopen/plcopen.py:975 #, python-format msgid "\"%s\" POU doesn't exist !!!" msgstr "" -#: ../editors/Viewer.py:246 +#: ../editors/Viewer.py:285 #, python-format msgid "\"%s\" can't use itself!" msgstr "" -#: ../IDEFrame.py:1652 ../IDEFrame.py:1671 +#: ../IDEFrame.py:1692 ../IDEFrame.py:1711 #, python-format msgid "\"%s\" config already exists!" msgstr "" -#: ../plcopen/plcopen.py:467 +#: ../plcopen/plcopen.py:509 #, python-format msgid "\"%s\" configuration already exists !!!" msgstr "" -#: ../IDEFrame.py:1602 +#: ../plcopen/plcopen.py:518 +#, python-format +msgid "\"%s\" configuration doesn't exist !!!" +msgstr "" + +#: ../IDEFrame.py:1642 #, python-format msgid "\"%s\" data type already exists!" msgstr "" -#: ../dialogs/PouTransitionDialog.py:110 ../dialogs/BlockPreviewDialog.py:219 -#: ../dialogs/PouActionDialog.py:102 ../editors/Viewer.py:262 -#: ../editors/Viewer.py:330 ../editors/Viewer.py:354 ../editors/Viewer.py:374 -#: ../editors/TextViewer.py:272 ../editors/TextViewer.py:301 -#: ../controls/VariablePanel.py:396 +#: ../dialogs/PouTransitionDialog.py:110 ../dialogs/BlockPreviewDialog.py:220 +#: ../dialogs/PouActionDialog.py:108 ../editors/Viewer.py:301 +#: ../editors/Viewer.py:371 ../editors/Viewer.py:395 ../editors/Viewer.py:415 +#: ../editors/TextViewer.py:275 ../editors/TextViewer.py:304 +#: ../controls/VariablePanel.py:425 #, python-format msgid "\"%s\" element for this pou already exists!" msgstr "" -#: ../Beremiz.py:994 +#: ../BeremizIDE.py:933 #, python-format msgid "\"%s\" folder is not a valid Beremiz project\n" msgstr "" -#: ../dialogs/SFCStepNameDialog.py:52 ../dialogs/PouTransitionDialog.py:106 -#: ../dialogs/BlockPreviewDialog.py:207 ../dialogs/PouNameDialog.py:50 -#: ../dialogs/PouActionDialog.py:98 ../dialogs/PouDialog.py:120 -#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:584 -#: ../editors/CodeFileEditor.py:770 ../controls/VariablePanel.py:751 -#: ../IDEFrame.py:1593 +#: ../dialogs/SFCStepNameDialog.py:53 ../dialogs/PouTransitionDialog.py:106 +#: ../dialogs/BlockPreviewDialog.py:209 ../dialogs/PouNameDialog.py:51 +#: ../dialogs/PouActionDialog.py:104 ../dialogs/PouDialog.py:127 +#: ../editors/ResourceEditor.py:464 ../editors/ResourceEditor.py:499 +#: ../editors/DataTypeEditor.py:570 ../editors/DataTypeEditor.py:602 +#: ../editors/CodeFileEditor.py:789 ../controls/VariablePanel.py:808 +#: ../IDEFrame.py:1633 #, python-format msgid "\"%s\" is a keyword. It can't be used!" msgstr "" -#: ../plcopen/plcopen.py:2412 +#: ../plcopen/plcopen.py:2531 #, python-format msgid "\"%s\" is an invalid value!" msgstr "" -#: ../PLCOpenEditor.py:339 ../PLCOpenEditor.py:381 +#: ../PLCOpenEditor.py:370 ../PLCOpenEditor.py:412 #, python-format msgid "\"%s\" is not a valid folder!" msgstr "" -#: ../dialogs/SFCStepNameDialog.py:50 ../dialogs/PouTransitionDialog.py:104 -#: ../dialogs/BlockPreviewDialog.py:203 ../dialogs/PouNameDialog.py:48 -#: ../dialogs/PouActionDialog.py:96 ../dialogs/PouDialog.py:118 -#: ../editors/DataTypeEditor.py:579 ../editors/CodeFileEditor.py:768 -#: ../controls/VariablePanel.py:749 ../IDEFrame.py:1591 +#: ../dialogs/SFCStepNameDialog.py:51 ../dialogs/PouTransitionDialog.py:104 +#: ../dialogs/BlockPreviewDialog.py:205 ../dialogs/PouNameDialog.py:49 +#: ../dialogs/PouActionDialog.py:102 ../dialogs/PouDialog.py:125 +#: ../editors/ResourceEditor.py:462 ../editors/ResourceEditor.py:497 +#: ../editors/DataTypeEditor.py:600 ../editors/CodeFileEditor.py:787 +#: ../controls/VariablePanel.py:806 ../IDEFrame.py:1631 #, python-format msgid "\"%s\" is not a valid identifier!" msgstr "" -#: ../IDEFrame.py:2396 +#: ../IDEFrame.py:2445 #, python-format msgid "\"%s\" is used by one or more POUs. Do you wish to continue?" msgstr "" -#: ../dialogs/BlockPreviewDialog.py:211 ../dialogs/PouDialog.py:122 -#: ../editors/Viewer.py:260 ../editors/Viewer.py:315 ../editors/Viewer.py:345 -#: ../editors/Viewer.py:367 ../editors/TextViewer.py:270 -#: ../editors/TextViewer.py:299 ../editors/TextViewer.py:350 -#: ../editors/TextViewer.py:373 ../controls/VariablePanel.py:338 -#: ../IDEFrame.py:1611 +#: ../dialogs/BlockPreviewDialog.py:213 ../dialogs/PouDialog.py:129 +#: ../editors/Viewer.py:299 ../editors/Viewer.py:356 ../editors/Viewer.py:386 +#: ../editors/Viewer.py:408 ../editors/TextViewer.py:273 +#: ../editors/TextViewer.py:302 ../editors/TextViewer.py:356 +#: ../editors/TextViewer.py:379 ../controls/VariablePanel.py:364 +#: ../IDEFrame.py:1651 #, python-format msgid "\"%s\" pou already exists!" msgstr "" -#: ../dialogs/SFCStepNameDialog.py:58 +#: ../dialogs/SFCStepNameDialog.py:59 #, python-format msgid "\"%s\" step already exists!" msgstr "" -#: ../editors/DataTypeEditor.py:550 +#: ../editors/DataTypeEditor.py:565 #, python-format msgid "\"%s\" value already defined!" msgstr "" -#: ../dialogs/ArrayTypeDialog.py:97 ../editors/DataTypeEditor.py:745 +#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:758 #, python-format msgid "\"%s\" value isn't a valid array dimension!" msgstr "" -#: ../dialogs/ArrayTypeDialog.py:103 ../editors/DataTypeEditor.py:752 +#: ../dialogs/ArrayTypeDialog.py:107 ../editors/DataTypeEditor.py:765 #, python-format msgid "" "\"%s\" value isn't a valid array dimension!\n" "Right value must be greater than left value." msgstr "" -#: ../PLCGenerator.py:1101 +#: ../PLCGenerator.py:1114 #, python-brace-format msgid "\"{a1}\" function cancelled in \"{a2}\" POU: No input connected" msgstr "" -#: ../editors/Viewer.py:250 +#: ../editors/Viewer.py:289 #, python-brace-format msgid "\"{a1}\" is already used by \"{a2}\"!" msgstr "" -#: ../plcopen/plcopen.py:491 +#: ../plcopen/plcopen.py:535 #, python-brace-format msgid "\"{a1}\" resource already exists in \"{a2}\" configuration !!!" msgstr "" -#: ../plcopen/plcopen.py:509 +#: ../plcopen/plcopen.py:555 #, python-brace-format msgid "\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!" msgstr "" -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:578 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:577 #, python-format msgid "%03gms" msgstr "" +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:568 +#, python-format +msgid "%dd" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:58 #: ../controls/DebugVariablePanel/DebugVariablePanel.py:569 #, python-format -msgid "%dd" +msgid "%dh" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:57 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 +#, python-format +msgid "%dm" +msgstr "" + +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 +#, python-format +msgid "%dms" msgstr "" #: ../controls/DebugVariablePanel/DebugVariablePanel.py:56 -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:570 -#, python-format -msgid "%dh" -msgstr "" - -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:55 #: ../controls/DebugVariablePanel/DebugVariablePanel.py:571 #, python-format -msgid "%dm" -msgstr "" - -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:53 -#, python-format -msgid "%dms" -msgstr "" - -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:54 -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:572 -#, python-format msgid "%ds" msgstr "" -#: ../PLCControler.py:1531 +#: ../PLCControler.py:1591 #, python-format msgid "%s Data Types" msgstr "" -#: ../PLCControler.py:1514 +#: ../PLCControler.py:1574 #, python-format msgid "%s POUs" msgstr "" -#: ../canfestival/SlaveEditor.py:69 ../canfestival/NetworkEditor.py:90 +#: ../canfestival/SlaveEditor.py:72 ../canfestival/NetworkEditor.py:97 #, python-format msgid "%s Profile" msgstr "" -#: ../plcopen/plcopen.py:1645 ../plcopen/plcopen.py:1652 -#: ../plcopen/plcopen.py:1664 ../plcopen/plcopen.py:1672 -#: ../plcopen/plcopen.py:1682 +#: ../plcopen/plcopen.py:1727 ../plcopen/plcopen.py:1734 +#: ../plcopen/plcopen.py:1747 ../plcopen/plcopen.py:1755 +#: ../plcopen/plcopen.py:1765 ../plcopen/plcopen.py:1776 #, python-format msgid "%s body don't have instances!" msgstr "" -#: ../plcopen/plcopen.py:1700 ../plcopen/plcopen.py:1707 -#: ../plcopen/plcopen.py:1714 +#: ../plcopen/plcopen.py:1783 ../plcopen/plcopen.py:1790 +#: ../plcopen/plcopen.py:1797 #, python-format msgid "%s body don't have text!" msgstr "" -#: ../IDEFrame.py:386 +#: ../IDEFrame.py:400 msgid "&Add Element" msgstr "" -#: ../dialogs/AboutDialog.py:65 ../dialogs/AboutDialog.py:113 -#: ../dialogs/AboutDialog.py:150 +#: ../dialogs/AboutDialog.py:71 ../dialogs/AboutDialog.py:117 +#: ../dialogs/AboutDialog.py:152 msgid "&Close" msgstr "" -#: ../IDEFrame.py:356 +#: ../IDEFrame.py:370 msgid "&Configuration" msgstr "" -#: ../IDEFrame.py:345 +#: ../IDEFrame.py:359 msgid "&Data Type" msgstr "" -#: ../IDEFrame.py:390 +#: ../IDEFrame.py:404 msgid "&Delete" msgstr "" -#: ../IDEFrame.py:337 +#: ../IDEFrame.py:351 msgid "&Display" msgstr "" -#: ../IDEFrame.py:336 +#: ../IDEFrame.py:350 msgid "&Edit" msgstr "" -#: ../IDEFrame.py:335 +#: ../IDEFrame.py:349 msgid "&File" msgstr "" -#: ../IDEFrame.py:347 +#: ../IDEFrame.py:361 msgid "&Function" msgstr "" -#: ../IDEFrame.py:338 +#: ../IDEFrame.py:352 msgid "&Help" msgstr "" -#: ../dialogs/AboutDialog.py:64 +#: ../dialogs/AboutDialog.py:70 msgid "&License" msgstr "" -#: ../IDEFrame.py:351 +#: ../IDEFrame.py:365 msgid "&Program" msgstr "" -#: ../PLCOpenEditor.py:125 +#: ../PLCOpenEditor.py:145 msgid "&Properties" msgstr "" -#: ../Beremiz.py:324 +#: ../BeremizIDE.py:249 msgid "&Recent Projects" msgstr "" -#: ../IDEFrame.py:353 +#: ../IDEFrame.py:367 msgid "&Resource" msgstr "" -#: ../controls/SearchResultPanel.py:239 +#: ../controls/SearchResultPanel.py:247 #, python-brace-format msgid "'{a1}' - {a2} match in project" msgstr "" -#: ../controls/SearchResultPanel.py:241 +#: ../controls/SearchResultPanel.py:249 #, python-brace-format msgid "'{a1}' - {a2} matches in project" msgstr "" -#: ../connectors/PYRO/__init__.py:90 +#: ../connectors/PYRO/__init__.py:93 #, python-brace-format msgid "'{a1}' is located at {a2}\n" msgstr "" -#: ../controls/SearchResultPanel.py:291 +#: ../controls/SearchResultPanel.py:299 #, python-format msgid "(%d matches)" msgstr "" -#: ../PLCOpenEditor.py:396 ../PLCOpenEditor.py:398 ../PLCOpenEditor.py:399 -msgid ", " -msgstr "" - -#: ../dialogs/PouTransitionDialog.py:101 ../dialogs/PouActionDialog.py:93 -#: ../dialogs/PouDialog.py:115 +#: ../dialogs/PouTransitionDialog.py:101 ../dialogs/PouActionDialog.py:99 +#: ../dialogs/PouDialog.py:122 #, python-format msgid ", %s" msgstr "" -#: ../PLCOpenEditor.py:394 -msgid ". " -msgstr "" - -#: ../controls/LogViewer.py:279 +#: ../controls/LogViewer.py:286 msgid "1d" msgstr "" -#: ../controls/LogViewer.py:280 +#: ../controls/LogViewer.py:287 msgid "1h" msgstr "" -#: ../controls/LogViewer.py:281 +#: ../controls/LogViewer.py:288 msgid "1m" msgstr "" -#: ../controls/LogViewer.py:282 +#: ../controls/LogViewer.py:289 msgid "1s" msgstr "" -#: ../dialogs/PouDialog.py:124 ../IDEFrame.py:1614 ../IDEFrame.py:1660 -#: ../IDEFrame.py:1679 +#: ../dialogs/PouDialog.py:131 ../IDEFrame.py:1654 ../IDEFrame.py:1700 +#: ../IDEFrame.py:1719 #, python-format msgid "A POU has an element named \"%s\". This could cause a conflict. Do you wish to continue?" msgstr "" -#: ../dialogs/SFCStepNameDialog.py:54 ../dialogs/PouTransitionDialog.py:108 -#: ../dialogs/PouNameDialog.py:52 ../dialogs/PouActionDialog.py:100 -#: ../controls/VariablePanel.py:753 ../IDEFrame.py:1628 ../IDEFrame.py:1641 +#: ../dialogs/SFCStepNameDialog.py:55 ../dialogs/PouTransitionDialog.py:108 +#: ../dialogs/PouNameDialog.py:53 ../dialogs/PouActionDialog.py:106 +#: ../controls/VariablePanel.py:810 ../IDEFrame.py:1668 ../IDEFrame.py:1681 #, python-format msgid "A POU named \"%s\" already exists!" msgstr "" -#: ../ConfigTreeNode.py:424 +#: ../ConfigTreeNode.py:427 #, python-brace-format msgid "A child named \"{a1}\" already exists -> \"{a2}\"\n" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:216 +#: ../dialogs/BrowseLocationsDialog.py:229 msgid "A location must be selected!" msgstr "" -#: ../dialogs/SFCStepNameDialog.py:56 ../controls/VariablePanel.py:755 -#: ../IDEFrame.py:1630 ../IDEFrame.py:1643 +#: ../editors/ResourceEditor.py:466 +msgid "A task with the same name already exists!" +msgstr "" + +#: ../dialogs/SFCStepNameDialog.py:57 ../controls/VariablePanel.py:812 +#: ../IDEFrame.py:1670 ../IDEFrame.py:1683 #, python-format msgid "A variable with \"%s\" as name already exists in this pou!" msgstr "" -#: ../editors/CodeFileEditor.py:774 +#: ../editors/CodeFileEditor.py:793 #, python-format msgid "A variable with \"%s\" as name already exists!" msgstr "" -#: ../dialogs/AboutDialog.py:40 ../PLCOpenEditor.py:158 ../Beremiz.py:381 +#: ../BeremizIDE.py:316 ../dialogs/AboutDialog.py:46 ../PLCOpenEditor.py:189 msgid "About" msgstr "" @@ -414,45 +417,45 @@ msgid "Absolute number" msgstr "" -#: ../dialogs/SFCStepDialog.py:72 ../dialogs/ActionBlockDialog.py:42 +#: ../dialogs/SFCStepDialog.py:73 ../dialogs/ActionBlockDialog.py:45 msgid "Action" msgstr "" -#: ../editors/Viewer.py:555 ../editors/Viewer.py:2345 +#: ../editors/Viewer.py:656 ../editors/Viewer.py:2437 msgid "Action Block" msgstr "" -#: ../dialogs/PouActionDialog.py:81 +#: ../dialogs/PouActionDialog.py:87 msgid "Action Name" msgstr "" -#: ../dialogs/PouActionDialog.py:49 +#: ../dialogs/PouActionDialog.py:54 msgid "Action Name:" msgstr "" -#: ../plcopen/plcopen.py:1359 +#: ../plcopen/plcopen.py:1423 #, python-format msgid "Action with name %s doesn't exist!" msgstr "" -#: ../PLCControler.py:96 +#: ../PLCControler.py:105 msgid "Actions" msgstr "" -#: ../dialogs/ActionBlockDialog.py:133 +#: ../dialogs/ActionBlockDialog.py:137 msgid "Actions:" msgstr "" -#: ../editors/Viewer.py:1100 +#: ../editors/Viewer.py:470 msgid "Active" msgstr "" -#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:101 -#: ../editors/Viewer.py:588 ../Beremiz.py:1060 +#: ../canfestival/SlaveEditor.py:83 ../canfestival/NetworkEditor.py:108 +#: ../BeremizIDE.py:1001 ../editors/Viewer.py:689 msgid "Add" msgstr "" -#: ../IDEFrame.py:1890 ../IDEFrame.py:1925 +#: ../IDEFrame.py:1930 ../IDEFrame.py:1965 msgid "Add Action" msgstr "" @@ -460,23 +463,23 @@ msgid "Add C code accessing located variables synchronously" msgstr "" -#: ../IDEFrame.py:1873 +#: ../IDEFrame.py:1913 msgid "Add Configuration" msgstr "" -#: ../IDEFrame.py:1853 +#: ../IDEFrame.py:1893 msgid "Add DataType" msgstr "" -#: ../editors/Viewer.py:513 +#: ../editors/Viewer.py:612 msgid "Add Divergence Branch" msgstr "" -#: ../dialogs/DiscoveryDialog.py:116 +#: ../dialogs/DiscoveryDialog.py:127 msgid "Add IP" msgstr "" -#: ../IDEFrame.py:1861 +#: ../IDEFrame.py:1901 msgid "Add POU" msgstr "" @@ -484,15 +487,15 @@ msgid "Add Python code executed asynchronously" msgstr "" -#: ../IDEFrame.py:1901 ../IDEFrame.py:1951 +#: ../IDEFrame.py:1941 ../IDEFrame.py:1991 msgid "Add Resource" msgstr "" -#: ../IDEFrame.py:1879 ../IDEFrame.py:1922 +#: ../IDEFrame.py:1919 ../IDEFrame.py:1962 msgid "Add Transition" msgstr "" -#: ../editors/Viewer.py:500 +#: ../editors/Viewer.py:599 msgid "Add Wire Segment" msgstr "" @@ -500,7 +503,7 @@ msgid "Add a new initial step" msgstr "" -#: ../editors/Viewer.py:2706 ../editors/SFCViewer.py:770 +#: ../editors/Viewer.py:2801 ../editors/SFCViewer.py:770 msgid "Add a new jump" msgstr "" @@ -512,27 +515,27 @@ msgid "Add a simple WxGlade based GUI." msgstr "" -#: ../dialogs/ActionBlockDialog.py:137 +#: ../dialogs/ActionBlockDialog.py:141 msgid "Add action" msgstr "" -#: ../editors/DataTypeEditor.py:352 +#: ../editors/DataTypeEditor.py:365 msgid "Add element" msgstr "" -#: ../editors/ResourceEditor.py:268 +#: ../editors/ResourceEditor.py:281 msgid "Add instance" msgstr "" -#: ../canfestival/NetworkEditor.py:103 +#: ../canfestival/NetworkEditor.py:110 msgid "Add slave" msgstr "" -#: ../editors/ResourceEditor.py:239 +#: ../editors/ResourceEditor.py:250 msgid "Add task" msgstr "" -#: ../editors/CodeFileEditor.py:658 ../controls/VariablePanel.py:450 +#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:481 msgid "Add variable" msgstr "" @@ -540,23 +543,23 @@ msgid "Addition" msgstr "" -#: ../plcopen/definitions.py:47 +#: ../plcopen/definitions.py:50 msgid "Additional function blocks" msgstr "" -#: ../editors/Viewer.py:571 +#: ../editors/Viewer.py:672 msgid "Adjust Block Size" msgstr "" -#: ../editors/Viewer.py:1637 +#: ../editors/Viewer.py:1728 msgid "Alignment" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:39 -#: ../dialogs/BrowseLocationsDialog.py:47 -#: ../dialogs/BrowseLocationsDialog.py:140 -#: ../dialogs/BrowseLocationsDialog.py:143 ../controls/LogViewer.py:298 -#: ../controls/VariablePanel.py:70 +#: ../dialogs/BrowseLocationsDialog.py:42 +#: ../dialogs/BrowseLocationsDialog.py:53 +#: ../dialogs/BrowseLocationsDialog.py:152 +#: ../dialogs/BrowseLocationsDialog.py:155 ../controls/LogViewer.py:306 +#: ../controls/VariablePanel.py:87 msgid "All" msgstr "" @@ -564,16 +567,20 @@ msgid "All files (*.*)|*.*|CSV files (*.csv)|*.csv" msgstr "" -#: ../ProjectController.py:1623 +#: ../ProjectController.py:1716 msgid "Already connected. Please disconnect\n" msgstr "" -#: ../editors/DataTypeEditor.py:594 +#: ../editors/DataTypeEditor.py:606 #, python-format msgid "An element named \"%s\" already exists in this structure!" msgstr "" -#: ../dialogs/ConnectionDialog.py:96 +#: ../editors/ResourceEditor.py:501 +msgid "An instance with the same name already exists!" +msgstr "" + +#: ../dialogs/ConnectionDialog.py:101 msgid "Apply name modification to all continuations with the same name" msgstr "" @@ -593,8 +600,8 @@ msgid "Arithmetic" msgstr "" -#: ../editors/DataTypeEditor.py:54 ../editors/DataTypeEditor.py:635 -#: ../controls/VariablePanel.py:829 +#: ../editors/DataTypeEditor.py:58 ../editors/DataTypeEditor.py:648 +#: ../controls/VariablePanel.py:893 msgid "Array" msgstr "" @@ -602,45 +609,45 @@ msgid "Assignment" msgstr "" -#: ../dialogs/FBDVariableDialog.py:217 +#: ../dialogs/FBDVariableDialog.py:225 msgid "At least a variable or an expression must be selected!" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:99 +#: ../controls/ProjectPropertiesPanel.py:103 msgid "Author" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:96 +#: ../controls/ProjectPropertiesPanel.py:100 msgid "Author Name (optional):" msgstr "" -#: ../dialogs/FindInPouDialog.py:79 +#: ../dialogs/FindInPouDialog.py:78 msgid "Backward" msgstr "" -#: ../util/Zeroconf.py:599 +#: ../util/Zeroconf.py:619 msgid "Bad domain name (circular) at " msgstr "" -#: ../util/Zeroconf.py:602 +#: ../util/Zeroconf.py:622 msgid "Bad domain name at " msgstr "" -#: ../canfestival/config_utils.py:342 ../canfestival/config_utils.py:630 +#: ../canfestival/config_utils.py:359 ../canfestival/config_utils.py:666 #, python-format msgid "Bad location size : %s" msgstr "" -#: ../dialogs/ArrayTypeDialog.py:55 ../editors/DataTypeEditor.py:175 -#: ../editors/DataTypeEditor.py:205 ../editors/DataTypeEditor.py:297 +#: ../dialogs/ArrayTypeDialog.py:55 ../editors/DataTypeEditor.py:183 +#: ../editors/DataTypeEditor.py:213 ../editors/DataTypeEditor.py:308 msgid "Base Type:" msgstr "" -#: ../editors/DataTypeEditor.py:625 ../controls/VariablePanel.py:787 +#: ../editors/DataTypeEditor.py:638 ../controls/VariablePanel.py:851 msgid "Base Types" msgstr "" -#: ../Beremiz.py:553 +#: ../BeremizIDE.py:487 msgid "Beremiz" msgstr "" @@ -672,68 +679,68 @@ msgid "Bitwise inverting" msgstr "" -#: ../editors/Viewer.py:525 ../editors/Viewer.py:2358 +#: ../editors/Viewer.py:624 ../editors/Viewer.py:2450 msgid "Block" msgstr "" -#: ../dialogs/FBDBlockDialog.py:59 +#: ../dialogs/FBDBlockDialog.py:61 msgid "Block Properties" msgstr "" -#: ../editors/TextViewer.py:262 +#: ../editors/TextViewer.py:265 msgid "Block name" msgstr "" -#: ../editors/Viewer.py:491 +#: ../editors/Viewer.py:590 msgid "Bottom" msgstr "" -#: ../ProjectController.py:1301 +#: ../ProjectController.py:1395 msgid "Broken" msgstr "" -#: ../dialogs/BrowseValuesLibraryDialog.py:37 +#: ../dialogs/BrowseValuesLibraryDialog.py:38 #, python-format msgid "Browse %s values library" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:65 +#: ../dialogs/BrowseLocationsDialog.py:72 msgid "Browse Locations" msgstr "" -#: ../ProjectController.py:1769 +#: ../ProjectController.py:1858 msgid "Build" msgstr "" -#: ../ProjectController.py:1235 +#: ../ProjectController.py:1330 msgid "Build directory already clean\n" msgstr "" -#: ../ProjectController.py:1770 +#: ../ProjectController.py:1859 msgid "Build project into build folder" msgstr "" -#: ../ProjectController.py:1018 +#: ../ProjectController.py:1106 msgid "C Build crashed !\n" msgstr "" -#: ../ProjectController.py:1015 +#: ../ProjectController.py:1103 msgid "C Build failed.\n" msgstr "" -#: ../c_ext/CFileEditor.py:63 +#: ../c_ext/CFileEditor.py:64 msgid "C code" msgstr "" -#: ../ProjectController.py:1093 +#: ../ProjectController.py:1181 msgid "C code generated successfully.\n" msgstr "" -#: ../targets/toolchain_makefile.py:122 +#: ../targets/toolchain_makefile.py:124 msgid "C compilation failed.\n" msgstr "" -#: ../targets/toolchain_gcc.py:156 +#: ../targets/toolchain_gcc.py:195 #, python-format msgid "C compilation of %s failed.\n" msgstr "" @@ -742,15 +749,15 @@ msgid "C extension" msgstr "" -#: ../dialogs/AboutDialog.py:63 +#: ../dialogs/AboutDialog.py:69 msgid "C&redits" msgstr "" -#: ../canfestival/NetworkEditor.py:52 +#: ../canfestival/NetworkEditor.py:58 msgid "CANOpen network" msgstr "" -#: ../canfestival/SlaveEditor.py:44 +#: ../canfestival/SlaveEditor.py:47 msgid "CANOpen slave" msgstr "" @@ -758,81 +765,81 @@ msgid "CANopen support" msgstr "" -#: ../plcopen/plcopen.py:1584 ../plcopen/plcopen.py:1598 -#: ../plcopen/plcopen.py:1622 ../plcopen/plcopen.py:1638 +#: ../plcopen/plcopen.py:1665 ../plcopen/plcopen.py:1679 +#: ../plcopen/plcopen.py:1704 ../plcopen/plcopen.py:1720 msgid "Can only generate execution order on FBD networks!" msgstr "" -#: ../controls/VariablePanel.py:267 +#: ../controls/VariablePanel.py:291 msgid "Can only give a location to local or global variables" msgstr "" -#: ../PLCOpenEditor.py:334 +#: ../PLCOpenEditor.py:365 #, python-format msgid "Can't generate program to file %s!" msgstr "" -#: ../controls/VariablePanel.py:265 +#: ../controls/VariablePanel.py:289 msgid "Can't give a location to a function block instance" msgstr "" -#: ../PLCOpenEditor.py:379 +#: ../PLCOpenEditor.py:410 #, python-format msgid "Can't save project to file %s!" msgstr "" -#: ../controls/VariablePanel.py:313 +#: ../controls/VariablePanel.py:339 msgid "Can't set an initial value to a function block instance" msgstr "" -#: ../ConfigTreeNode.py:529 +#: ../ConfigTreeNode.py:532 #, python-brace-format msgid "Cannot create child {a1} of type {a2} " msgstr "" -#: ../ConfigTreeNode.py:454 +#: ../ConfigTreeNode.py:457 #, python-format msgid "Cannot find lower free IEC channel than %d\n" msgstr "" -#: ../connectors/PYRO/__init__.py:131 +#: ../connectors/PYRO/__init__.py:134 msgid "Cannot get PLC status - connection failed.\n" msgstr "" -#: ../ProjectController.py:881 +#: ../ProjectController.py:965 msgid "Cannot open/parse VARIABLES.csv!\n" msgstr "" -#: ../canfestival/config_utils.py:374 +#: ../canfestival/config_utils.py:394 #, python-brace-format msgid "Cannot set bit offset for non bool '{a1}' variable (ID:{a2},Idx:{a3},sIdx:{a4}))" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:59 ../dialogs/FindInPouDialog.py:88 +#: ../dialogs/SearchInProjectDialog.py:62 ../dialogs/FindInPouDialog.py:87 msgid "Case sensitive" msgstr "" -#: ../editors/Viewer.py:486 +#: ../editors/Viewer.py:585 msgid "Center" msgstr "" -#: ../Beremiz_service.py:266 +#: ../Beremiz_service.py:276 msgid "Change IP of interface to bind" msgstr "" -#: ../Beremiz_service.py:265 +#: ../Beremiz_service.py:275 msgid "Change Name" msgstr "" -#: ../IDEFrame.py:1943 +#: ../IDEFrame.py:1983 msgid "Change POU Type To" msgstr "" -#: ../Beremiz_service.py:267 +#: ../Beremiz_service.py:277 msgid "Change Port Number" msgstr "" -#: ../Beremiz_service.py:268 +#: ../Beremiz_service.py:278 msgid "Change working directory" msgstr "" @@ -840,110 +847,119 @@ msgid "Character string" msgstr "" -#: ../svgui/svgui.py:125 +#: ../svgui/svgui.py:135 msgid "Choose a SVG file" msgstr "" -#: ../ProjectController.py:451 +#: ../ProjectController.py:559 msgid "Choose a directory to save project" msgstr "" -#: ../canfestival/canfestival.py:160 ../PLCOpenEditor.py:292 -#: ../PLCOpenEditor.py:324 ../PLCOpenEditor.py:373 +#: ../canfestival/canfestival.py:179 ../PLCOpenEditor.py:323 +#: ../PLCOpenEditor.py:355 ../PLCOpenEditor.py:404 msgid "Choose a file" msgstr "" -#: ../Beremiz.py:931 ../Beremiz.py:966 +#: ../BeremizIDE.py:905 msgid "Choose a project" msgstr "" -#: ../dialogs/BrowseValuesLibraryDialog.py:42 +#: ../dialogs/BrowseValuesLibraryDialog.py:41 #, python-format msgid "Choose a value for %s:" msgstr "" -#: ../Beremiz_service.py:323 +#: ../Beremiz_service.py:333 msgid "Choose a working directory " msgstr "" -#: ../ProjectController.py:358 +#: ../BeremizIDE.py:869 +msgid "Choose an empty directory for new project" +msgstr "" + +#: ../ProjectController.py:466 msgid "Chosen folder doesn't contain a program. It's not a valid project!" msgstr "" -#: ../ProjectController.py:325 +#: ../ProjectController.py:433 msgid "Chosen folder isn't empty. You can't use it for a new project!" msgstr "" -#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +#: ../controls/VariablePanel.py:59 msgid "Class" msgstr "" -#: ../controls/VariablePanel.py:441 +#: ../controls/VariablePanel.py:472 msgid "Class Filter:" msgstr "" -#: ../dialogs/FBDVariableDialog.py:69 +#: ../dialogs/FBDVariableDialog.py:73 msgid "Class:" msgstr "" -#: ../ProjectController.py:1773 +#: ../ProjectController.py:1864 msgid "Clean" msgstr "" -#: ../controls/LogViewer.py:318 +#: ../controls/LogViewer.py:326 msgid "Clean log messages" msgstr "" -#: ../ProjectController.py:1775 +#: ../ProjectController.py:1865 msgid "Clean project build folder" msgstr "" -#: ../ProjectController.py:1232 +#: ../ProjectController.py:1327 msgid "Cleaning the build directory\n" msgstr "" -#: ../IDEFrame.py:435 +#: ../IDEFrame.py:449 msgid "Clear Errors" msgstr "" -#: ../editors/Viewer.py:582 +#: ../editors/Viewer.py:683 msgid "Clear Execution Order" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:105 ../dialogs/FindInPouDialog.py:111 +#: ../dialogs/SearchInProjectDialog.py:106 ../dialogs/FindInPouDialog.py:110 msgid "Close" msgstr "" -#: ../PLCOpenEditor.py:199 ../Beremiz.py:693 +#: ../BeremizIDE.py:631 ../PLCOpenEditor.py:230 msgid "Close Application" msgstr "" -#: ../PLCOpenEditor.py:108 ../Beremiz.py:333 ../Beremiz.py:637 -#: ../IDEFrame.py:1009 +#: ../BeremizIDE.py:258 ../BeremizIDE.py:570 ../PLCOpenEditor.py:128 +#: ../IDEFrame.py:1046 msgid "Close Project" msgstr "" -#: ../PLCOpenEditor.py:106 ../Beremiz.py:331 +#: ../BeremizIDE.py:256 ../PLCOpenEditor.py:126 msgid "Close Tab" msgstr "" -#: ../editors/Viewer.py:541 ../editors/Viewer.py:2366 +#: ../editors/Viewer.py:641 ../editors/Viewer.py:2458 msgid "Coil" msgstr "" -#: ../editors/Viewer.py:561 ../editors/LDViewer.py:506 +#: ../editors/Viewer.py:662 ../editors/LDViewer.py:515 msgid "Comment" msgstr "" -#: ../dialogs/ProjectDialog.py:57 +#: ../BeremizIDE.py:308 ../BeremizIDE.py:312 ../PLCOpenEditor.py:181 +#: ../PLCOpenEditor.py:185 +msgid "Community support" +msgstr "" + +#: ../dialogs/ProjectDialog.py:63 msgid "Company Name" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:94 +#: ../controls/ProjectPropertiesPanel.py:98 msgid "Company Name (required):" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:95 +#: ../controls/ProjectPropertiesPanel.py:99 msgid "Company URL (optional):" msgstr "" @@ -951,7 +967,7 @@ msgid "Comparison" msgstr "" -#: ../ProjectController.py:672 +#: ../ProjectController.py:754 msgid "Compiling IEC Program into C code...\n" msgstr "" @@ -959,93 +975,93 @@ msgid "Concatenation" msgstr "" -#: ../editors/ConfTreeNodeEditor.py:229 +#: ../editors/ConfTreeNodeEditor.py:240 msgid "Config" msgstr "" -#: ../editors/ProjectNodeEditor.py:36 +#: ../editors/ProjectNodeEditor.py:37 msgid "Config variables" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:39 +#: ../dialogs/SearchInProjectDialog.py:42 msgid "Configuration" msgstr "" -#: ../PLCControler.py:97 +#: ../PLCControler.py:106 msgid "Configurations" msgstr "" -#: ../editors/Viewer.py:307 ../editors/Viewer.py:337 ../editors/Viewer.py:359 -#: ../editors/TextViewer.py:291 ../editors/TextViewer.py:342 -#: ../editors/TextViewer.py:365 ../controls/VariablePanel.py:328 +#: ../editors/Viewer.py:348 ../editors/Viewer.py:378 ../editors/Viewer.py:400 +#: ../editors/TextViewer.py:294 ../editors/TextViewer.py:348 +#: ../editors/TextViewer.py:371 ../controls/VariablePanel.py:354 msgid "Confirm or change variable name" msgstr "" -#: ../ProjectController.py:1788 +#: ../ProjectController.py:1885 msgid "Connect" msgstr "" -#: ../ProjectController.py:1789 +#: ../ProjectController.py:1886 msgid "Connect to the target PLC" msgstr "" -#: ../ProjectController.py:1292 +#: ../ProjectController.py:1386 #, python-format msgid "Connected to URI: %s" msgstr "" -#: ../dialogs/SFCTransitionDialog.py:76 ../editors/Viewer.py:527 -#: ../editors/Viewer.py:2359 +#: ../dialogs/SFCTransitionDialog.py:77 ../editors/Viewer.py:626 +#: ../editors/Viewer.py:2451 msgid "Connection" msgstr "" -#: ../dialogs/ConnectionDialog.py:52 +#: ../dialogs/ConnectionDialog.py:53 msgid "Connection Properties" msgstr "" -#: ../ProjectController.py:1647 +#: ../ProjectController.py:1737 msgid "Connection canceled!\n" msgstr "" -#: ../ProjectController.py:1672 +#: ../ProjectController.py:1760 #, python-format msgid "Connection failed to %s!\n" msgstr "" -#: ../connectors/PYRO/__init__.py:115 ../connectors/WAMP/__init__.py:111 +#: ../connectors/PYRO/__init__.py:118 ../connectors/WAMP/__init__.py:119 msgid "Connection lost!\n" msgstr "" -#: ../connectors/PYRO/__init__.py:102 +#: ../connectors/PYRO/__init__.py:105 #, python-format msgid "Connection to '%s' failed.\n" msgstr "" -#: ../dialogs/ConnectionDialog.py:64 ../editors/Viewer.py:1594 +#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1684 msgid "Connector" msgstr "" -#: ../dialogs/SFCStepDialog.py:65 +#: ../dialogs/SFCStepDialog.py:66 msgid "Connectors:" msgstr "" -#: ../Beremiz.py:448 +#: ../BeremizIDE.py:383 msgid "Console" msgstr "" -#: ../controls/VariablePanel.py:60 +#: ../controls/VariablePanel.py:74 msgid "Constant" msgstr "" -#: ../editors/Viewer.py:537 ../editors/Viewer.py:2362 +#: ../editors/Viewer.py:637 ../editors/Viewer.py:2454 msgid "Contact" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:197 +#: ../controls/ProjectPropertiesPanel.py:203 msgid "Content Description (optional):" msgstr "" -#: ../dialogs/ConnectionDialog.py:65 ../editors/Viewer.py:1595 +#: ../dialogs/ConnectionDialog.py:66 ../editors/Viewer.py:1685 msgid "Continuation" msgstr "" @@ -1065,20 +1081,20 @@ msgid "Conversion to time-of-day" msgstr "" -#: ../editors/Viewer.py:597 ../controls/LogViewer.py:693 ../IDEFrame.py:370 -#: ../IDEFrame.py:425 +#: ../editors/Viewer.py:698 ../controls/LogViewer.py:712 ../IDEFrame.py:384 +#: ../IDEFrame.py:439 msgid "Copy" msgstr "" -#: ../IDEFrame.py:1930 +#: ../IDEFrame.py:1970 msgid "Copy POU" msgstr "" +#: ../editors/FileManagementPanel.py:66 +msgid "Copy file from left folder to right" +msgstr "" + #: ../editors/FileManagementPanel.py:65 -msgid "Copy file from left folder to right" -msgstr "" - -#: ../editors/FileManagementPanel.py:64 msgid "Copy file from right folder to left" msgstr "" @@ -1086,89 +1102,89 @@ msgid "Cosine" msgstr "" -#: ../ConfigTreeNode.py:656 +#: ../ConfigTreeNode.py:661 #, python-brace-format msgid "" "Could not add child \"{a1}\", type {a2} :\n" "{a3}\n" msgstr "" -#: ../py_ext/PythonFileCTNMixin.py:77 +#: ../py_ext/PythonFileCTNMixin.py:80 #, python-format msgid "Couldn't import old %s file." msgstr "" -#: ../ConfigTreeNode.py:626 +#: ../ConfigTreeNode.py:631 #, python-brace-format msgid "" "Couldn't load confnode base parameters {a1} :\n" " {a2}" msgstr "" -#: ../ConfigTreeNode.py:643 ../CodeFileTreeNode.py:124 +#: ../ConfigTreeNode.py:648 ../CodeFileTreeNode.py:127 #, python-brace-format msgid "" "Couldn't load confnode parameters {a1} :\n" " {a2}" msgstr "" -#: ../PLCControler.py:946 +#: ../PLCControler.py:1004 msgid "Couldn't paste non-POU object." msgstr "" -#: ../ProjectController.py:1589 +#: ../ProjectController.py:1682 msgid "Couldn't start PLC !\n" msgstr "" -#: ../ProjectController.py:1597 +#: ../ProjectController.py:1690 msgid "Couldn't stop PLC !\n" msgstr "" -#: ../ProjectController.py:1561 +#: ../ProjectController.py:1654 msgid "Couldn't stop debugger.\n" msgstr "" -#: ../svgui/svgui.py:47 +#: ../svgui/svgui.py:56 msgid "Create HMI" msgstr "" -#: ../dialogs/PouDialog.py:45 +#: ../dialogs/PouDialog.py:52 msgid "Create a new POU" msgstr "" -#: ../dialogs/PouActionDialog.py:38 +#: ../dialogs/PouActionDialog.py:43 msgid "Create a new action" msgstr "" -#: ../IDEFrame.py:159 +#: ../IDEFrame.py:165 msgid "Create a new action block" msgstr "" -#: ../IDEFrame.py:108 ../IDEFrame.py:138 ../IDEFrame.py:171 +#: ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:177 msgid "Create a new block" msgstr "" +#: ../IDEFrame.py:138 +msgid "Create a new branch" +msgstr "" + #: ../IDEFrame.py:132 -msgid "Create a new branch" -msgstr "" - -#: ../IDEFrame.py:126 msgid "Create a new coil" msgstr "" -#: ../IDEFrame.py:102 ../IDEFrame.py:117 ../IDEFrame.py:147 +#: ../IDEFrame.py:108 ../IDEFrame.py:123 ../IDEFrame.py:153 msgid "Create a new comment" msgstr "" -#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 +#: ../IDEFrame.py:117 ../IDEFrame.py:147 ../IDEFrame.py:180 msgid "Create a new connection" msgstr "" -#: ../IDEFrame.py:129 ../IDEFrame.py:180 +#: ../IDEFrame.py:135 ../IDEFrame.py:186 msgid "Create a new contact" msgstr "" -#: ../IDEFrame.py:162 +#: ../IDEFrame.py:168 msgid "Create a new divergence" msgstr "" @@ -1176,47 +1192,47 @@ msgid "Create a new divergence or convergence" msgstr "" -#: ../IDEFrame.py:150 +#: ../IDEFrame.py:156 msgid "Create a new initial step" msgstr "" -#: ../IDEFrame.py:165 +#: ../IDEFrame.py:171 msgid "Create a new jump" msgstr "" -#: ../IDEFrame.py:120 ../IDEFrame.py:177 +#: ../IDEFrame.py:126 ../IDEFrame.py:183 msgid "Create a new power rail" msgstr "" -#: ../IDEFrame.py:123 +#: ../IDEFrame.py:129 msgid "Create a new rung" msgstr "" -#: ../IDEFrame.py:153 +#: ../IDEFrame.py:159 msgid "Create a new step" msgstr "" -#: ../dialogs/PouTransitionDialog.py:47 ../IDEFrame.py:156 +#: ../dialogs/PouTransitionDialog.py:47 ../IDEFrame.py:162 msgid "Create a new transition" msgstr "" -#: ../IDEFrame.py:105 ../IDEFrame.py:135 ../IDEFrame.py:168 +#: ../IDEFrame.py:111 ../IDEFrame.py:141 ../IDEFrame.py:174 msgid "Create a new variable" msgstr "" -#: ../dialogs/AboutDialog.py:105 +#: ../dialogs/AboutDialog.py:109 msgid "Credits" msgstr "" -#: ../Beremiz_service.py:432 +#: ../Beremiz_service.py:442 msgid "Current working directory :" msgstr "" -#: ../editors/Viewer.py:596 ../IDEFrame.py:368 ../IDEFrame.py:424 +#: ../editors/Viewer.py:697 ../IDEFrame.py:382 ../IDEFrame.py:438 msgid "Cut" msgstr "" -#: ../editors/ResourceEditor.py:72 +#: ../editors/ResourceEditor.py:79 msgid "Cyclic" msgstr "" @@ -1228,19 +1244,19 @@ msgid "DEPRECATED" msgstr "" -#: ../canfestival/SlaveEditor.py:76 ../canfestival/NetworkEditor.py:97 +#: ../canfestival/SlaveEditor.py:79 ../canfestival/NetworkEditor.py:104 msgid "DS-301 Profile" msgstr "" -#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:98 +#: ../canfestival/SlaveEditor.py:80 ../canfestival/NetworkEditor.py:105 msgid "DS-302 Profile" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:35 +#: ../dialogs/SearchInProjectDialog.py:38 msgid "Data Type" msgstr "" -#: ../PLCControler.py:96 +#: ../PLCControler.py:105 msgid "Data Types" msgstr "" @@ -1261,66 +1277,66 @@ msgid "Date subtraction" msgstr "" -#: ../dialogs/DurationEditorDialog.py:43 +#: ../dialogs/DurationEditorDialog.py:44 msgid "Days:" msgstr "" -#: ../ProjectController.py:1694 +#: ../ProjectController.py:1782 msgid "Debug does not match PLC - stop/transfert/start to re-enable\n" msgstr "" -#: ../controls/PouInstanceVariablesPanel.py:134 +#: ../controls/PouInstanceVariablesPanel.py:150 msgid "Debug instance" msgstr "" -#: ../editors/Viewer.py:1117 ../editors/Viewer.py:3653 +#: ../editors/Viewer.py:487 #, python-format msgid "Debug: %s" msgstr "" -#: ../ProjectController.py:1350 +#: ../ProjectController.py:1444 #, python-format msgid "Debug: Unknown variable '%s'\n" msgstr "" -#: ../ProjectController.py:1348 +#: ../ProjectController.py:1442 #, python-format msgid "Debug: Unsupported type to debug '%s'\n" msgstr "" -#: ../IDEFrame.py:639 +#: ../IDEFrame.py:675 msgid "Debugger" msgstr "" -#: ../ProjectController.py:1530 +#: ../ProjectController.py:1623 msgid "Debugger disabled\n" msgstr "" -#: ../ProjectController.py:1691 +#: ../ProjectController.py:1779 msgid "Debugger ready\n" msgstr "" -#: ../ProjectController.py:1563 +#: ../ProjectController.py:1656 msgid "Debugger stopped.\n" msgstr "" -#: ../editors/Viewer.py:572 ../Beremiz.py:1064 ../IDEFrame.py:1959 +#: ../BeremizIDE.py:1004 ../editors/Viewer.py:673 ../IDEFrame.py:1999 msgid "Delete" msgstr "" -#: ../editors/Viewer.py:514 +#: ../editors/Viewer.py:613 msgid "Delete Divergence Branch" msgstr "" -#: ../editors/FileManagementPanel.py:153 +#: ../editors/FileManagementPanel.py:155 msgid "Delete File" msgstr "" -#: ../editors/Viewer.py:501 +#: ../editors/Viewer.py:600 msgid "Delete Wire Segment" msgstr "" -#: ../controls/CustomEditableListBox.py:41 +#: ../controls/CustomEditableListBox.py:42 msgid "Delete item" msgstr "" @@ -1328,43 +1344,47 @@ msgid "Deletion (within)" msgstr "" -#: ../editors/DataTypeEditor.py:153 +#: ../editors/DataTypeEditor.py:161 msgid "Derivation Type:" msgstr "" -#: ../controls/VariablePanel.py:432 +#: ../editors/CodeFileEditor.py:750 +msgid "Description" +msgstr "" + +#: ../controls/VariablePanel.py:463 msgid "Description:" msgstr "" -#: ../dialogs/ArrayTypeDialog.py:61 ../editors/DataTypeEditor.py:321 +#: ../dialogs/ArrayTypeDialog.py:61 ../editors/DataTypeEditor.py:333 msgid "Dimensions:" msgstr "" -#: ../dialogs/FindInPouDialog.py:68 +#: ../dialogs/FindInPouDialog.py:67 msgid "Direction" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:90 +#: ../dialogs/BrowseLocationsDialog.py:102 msgid "Direction:" msgstr "" -#: ../editors/DataTypeEditor.py:54 +#: ../editors/DataTypeEditor.py:58 msgid "Directly" msgstr "" -#: ../ProjectController.py:1797 +#: ../ProjectController.py:1898 msgid "Disconnect" msgstr "" -#: ../ProjectController.py:1799 +#: ../ProjectController.py:1899 msgid "Disconnect from PLC" msgstr "" -#: ../ProjectController.py:1302 +#: ../ProjectController.py:1396 msgid "Disconnected" msgstr "" -#: ../editors/Viewer.py:556 ../editors/Viewer.py:2354 +#: ../editors/Viewer.py:657 ../editors/Viewer.py:2446 msgid "Divergence" msgstr "" @@ -1372,28 +1392,28 @@ msgid "Division" msgstr "" -#: ../editors/FileManagementPanel.py:152 +#: ../editors/FileManagementPanel.py:154 #, python-format msgid "Do you really want to delete the file '%s'?" msgstr "" -#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +#: ../controls/VariablePanel.py:64 msgid "Documentation" msgstr "" -#: ../PLCOpenEditor.py:328 +#: ../PLCOpenEditor.py:359 msgid "Done" msgstr "" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:40 msgid "Duration" msgstr "" -#: ../canfestival/canfestival.py:163 +#: ../canfestival/canfestival.py:182 msgid "EDS files (*.eds)|*.eds|All files|*.*" msgstr "" -#: ../editors/Viewer.py:570 +#: ../editors/Viewer.py:671 msgid "Edit Block" msgstr "" @@ -1405,19 +1425,19 @@ msgid "Edit Contact Values" msgstr "" -#: ../dialogs/DurationEditorDialog.py:59 +#: ../dialogs/DurationEditorDialog.py:60 msgid "Edit Duration" msgstr "" -#: ../dialogs/SFCStepDialog.py:50 +#: ../dialogs/SFCStepDialog.py:51 msgid "Edit Step" msgstr "" -#: ../wxglade_hmi/wxglade_hmi.py:36 +#: ../wxglade_hmi/wxglade_hmi.py:42 msgid "Edit a WxWidgets GUI with WXGlade" msgstr "" -#: ../dialogs/ActionBlockDialog.py:121 +#: ../dialogs/ActionBlockDialog.py:125 msgid "Edit action block properties" msgstr "" @@ -1425,67 +1445,71 @@ msgid "Edit array type properties" msgstr "" -#: ../editors/Viewer.py:2575 ../editors/Viewer.py:3004 +#: ../editors/Viewer.py:2670 ../editors/Viewer.py:3112 msgid "Edit comment" msgstr "" -#: ../editors/FileManagementPanel.py:66 +#: ../editors/FileManagementPanel.py:67 msgid "Edit file" msgstr "" -#: ../controls/CustomEditableListBox.py:39 +#: ../controls/CustomEditableListBox.py:40 msgid "Edit item" msgstr "" -#: ../editors/Viewer.py:2963 +#: ../editors/Viewer.py:3069 msgid "Edit jump target" msgstr "" -#: ../ProjectController.py:1811 +#: ../ProjectController.py:1916 msgid "Edit raw IEC code added to code generated by PLCGenerator" msgstr "" -#: ../editors/SFCViewer.py:799 +#: ../editors/SFCViewer.py:801 msgid "Edit step name" msgstr "" -#: ../dialogs/SFCTransitionDialog.py:51 +#: ../dialogs/SFCTransitionDialog.py:52 msgid "Edit transition" msgstr "" -#: ../IDEFrame.py:611 +#: ../IDEFrame.py:647 msgid "Editor ToolBar" msgstr "" -#: ../ProjectController.py:1195 +#: ../ProjectController.py:1289 msgid "Editor selection" msgstr "" -#: ../editors/DataTypeEditor.py:348 +#: ../editors/DataTypeEditor.py:361 msgid "Elements :" msgstr "" -#: ../ProjectController.py:1300 +#: ../ProjectController.py:1394 msgid "Empty" msgstr "" -#: ../IDEFrame.py:365 +#: ../dialogs/ArrayTypeDialog.py:98 +msgid "Empty dimension isn't allowed." +msgstr "" + +#: ../IDEFrame.py:379 msgid "Enable Undo/Redo" msgstr "" -#: ../Beremiz_service.py:331 +#: ../Beremiz_service.py:341 msgid "Enter a name " msgstr "" -#: ../Beremiz_service.py:316 +#: ../Beremiz_service.py:326 msgid "Enter a port number " msgstr "" -#: ../Beremiz_service.py:307 +#: ../Beremiz_service.py:317 msgid "Enter the IP of the interface to bind" msgstr "" -#: ../editors/DataTypeEditor.py:54 +#: ../editors/DataTypeEditor.py:58 msgid "Enumerated" msgstr "" @@ -1493,77 +1517,76 @@ msgid "Equal to" msgstr "" -#: ../dialogs/ForceVariableDialog.py:179 -#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/SFCStepNameDialog.py:60 -#: ../dialogs/DurationEditorDialog.py:121 -#: ../dialogs/DurationEditorDialog.py:163 ../dialogs/PouTransitionDialog.py:112 -#: ../dialogs/BlockPreviewDialog.py:236 ../dialogs/ProjectDialog.py:71 -#: ../dialogs/ArrayTypeDialog.py:97 ../dialogs/ArrayTypeDialog.py:103 -#: ../dialogs/PouNameDialog.py:54 ../dialogs/BrowseLocationsDialog.py:216 -#: ../dialogs/BrowseValuesLibraryDialog.py:83 ../dialogs/PouActionDialog.py:104 -#: ../dialogs/PouDialog.py:134 ../PLCOpenEditor.py:335 ../PLCOpenEditor.py:340 -#: ../PLCOpenEditor.py:420 ../PLCOpenEditor.py:430 ../editors/Viewer.py:423 -#: ../editors/LDViewer.py:666 ../editors/LDViewer.py:882 -#: ../editors/LDViewer.py:886 ../editors/DataTypeEditor.py:550 -#: ../editors/DataTypeEditor.py:555 ../editors/DataTypeEditor.py:579 -#: ../editors/DataTypeEditor.py:584 ../editors/DataTypeEditor.py:594 -#: ../editors/DataTypeEditor.py:745 ../editors/DataTypeEditor.py:752 -#: ../editors/TextViewer.py:389 ../editors/CodeFileEditor.py:783 -#: ../ProjectController.py:293 ../ProjectController.py:421 -#: ../ProjectController.py:428 ../controls/FolderTree.py:217 -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:166 +#: ../dialogs/ForceVariableDialog.py:211 +#: ../dialogs/SearchInProjectDialog.py:171 ../dialogs/SFCStepNameDialog.py:61 +#: ../dialogs/DurationEditorDialog.py:122 +#: ../dialogs/DurationEditorDialog.py:168 ../dialogs/PouTransitionDialog.py:112 +#: ../dialogs/BlockPreviewDialog.py:237 ../dialogs/ProjectDialog.py:78 +#: ../dialogs/ArrayTypeDialog.py:112 ../dialogs/PouNameDialog.py:55 +#: ../dialogs/BrowseLocationsDialog.py:229 +#: ../dialogs/BrowseValuesLibraryDialog.py:83 ../dialogs/PouActionDialog.py:110 +#: ../dialogs/PouDialog.py:141 ../PLCOpenEditor.py:366 ../PLCOpenEditor.py:371 +#: ../editors/ResourceEditor.py:451 ../editors/Viewer.py:464 +#: ../editors/LDViewer.py:675 ../editors/LDViewer.py:891 +#: ../editors/LDViewer.py:895 ../editors/DataTypeEditor.py:565 +#: ../editors/DataTypeEditor.py:570 ../editors/DataTypeEditor.py:589 +#: ../editors/DataTypeEditor.py:758 ../editors/DataTypeEditor.py:765 +#: ../editors/TextViewer.py:395 ../editors/CodeFileEditor.py:775 +#: ../ProjectController.py:389 ../ProjectController.py:529 +#: ../ProjectController.py:536 ../controls/FolderTree.py:220 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:170 #: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:137 -#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:231 -#: ../controls/VariablePanel.py:402 ../controls/VariablePanel.py:772 -#: ../Beremiz.py:1203 ../IDEFrame.py:1003 ../IDEFrame.py:1614 -#: ../IDEFrame.py:1655 ../IDEFrame.py:1660 ../IDEFrame.py:1674 -#: ../IDEFrame.py:1679 ../Beremiz_service.py:211 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:232 +#: ../controls/VariablePanel.py:431 ../controls/VariablePanel.py:794 +#: ../util/ExceptionHandler.py:68 ../IDEFrame.py:1042 ../IDEFrame.py:1654 +#: ../IDEFrame.py:1695 ../IDEFrame.py:1700 ../IDEFrame.py:1714 +#: ../IDEFrame.py:1719 ../Beremiz_service.py:221 msgid "Error" msgstr "" -#: ../ProjectController.py:727 +#: ../ProjectController.py:809 msgid "Error : At least one configuration and one resource must be declared in PLC !\n" msgstr "" -#: ../ProjectController.py:719 +#: ../ProjectController.py:801 #, python-format msgid "Error : IEC to C compiler returned %d\n" msgstr "" -#: ../ProjectController.py:621 +#: ../ProjectController.py:729 #, python-format msgid "" "Error in ST/IL/SFC code generator :\n" "%s\n" msgstr "" -#: ../ConfigTreeNode.py:216 +#: ../ConfigTreeNode.py:219 #, python-format msgid "Error while saving \"%s\"\n" msgstr "" -#: ../canfestival/canfestival.py:168 +#: ../canfestival/canfestival.py:187 msgid "Error: Export slave failed\n" msgstr "" -#: ../canfestival/canfestival.py:369 +#: ../canfestival/canfestival.py:397 msgid "Error: No Master generated\n" msgstr "" -#: ../canfestival/canfestival.py:364 +#: ../canfestival/canfestival.py:392 msgid "Error: No PLC built\n" msgstr "" -#: ../ProjectController.py:1666 +#: ../ProjectController.py:1754 #, python-format msgid "Exception while connecting %s!\n" msgstr "" -#: ../dialogs/FBDBlockDialog.py:117 +#: ../dialogs/FBDBlockDialog.py:121 msgid "Execution Control:" msgstr "" -#: ../dialogs/FBDVariableDialog.py:79 ../dialogs/FBDBlockDialog.py:105 +#: ../dialogs/FBDVariableDialog.py:83 ../dialogs/FBDBlockDialog.py:109 msgid "Execution Order:" msgstr "" @@ -1579,7 +1602,7 @@ msgid "Exponentiation" msgstr "" -#: ../canfestival/canfestival.py:174 +#: ../canfestival/canfestival.py:194 msgid "Export CanOpen slave to EDS file" msgstr "" @@ -1587,69 +1610,69 @@ msgid "Export graph values to clipboard" msgstr "" -#: ../canfestival/canfestival.py:173 +#: ../canfestival/canfestival.py:193 msgid "Export slave" msgstr "" -#: ../dialogs/FBDVariableDialog.py:89 +#: ../dialogs/FBDVariableDialog.py:93 msgid "Expression:" msgstr "" -#: ../controls/VariablePanel.py:72 +#: ../controls/VariablePanel.py:89 msgid "External" msgstr "" -#: ../ProjectController.py:740 +#: ../ProjectController.py:824 msgid "Extracting Located Variables...\n" msgstr "" -#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +#: ../dialogs/PouTransitionDialog.py:38 ../dialogs/PouActionDialog.py:34 +#: ../dialogs/PouDialog.py:42 ../controls/ProjectPropertiesPanel.py:148 msgid "FBD" msgstr "" -#: ../ProjectController.py:1729 +#: ../ProjectController.py:1816 msgid "Failed : Must build before transfer.\n" msgstr "" -#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:462 +#: ../dialogs/LDElementDialog.py:78 ../editors/Viewer.py:560 msgid "Falling Edge" msgstr "" -#: ../ProjectController.py:1008 +#: ../ProjectController.py:1096 msgid "Fatal : cannot get builder.\n" msgstr "" -#: ../Beremiz.py:118 +#: ../Beremiz.py:156 #, python-format msgid "Fetching %s" msgstr "" -#: ../dialogs/DurationEditorDialog.py:160 +#: ../dialogs/DurationEditorDialog.py:165 #, python-format msgid "Field %s hasn't a valid value!" msgstr "" -#: ../dialogs/DurationEditorDialog.py:162 +#: ../dialogs/DurationEditorDialog.py:167 #, python-format msgid "Fields %s haven't a valid value!" msgstr "" -#: ../controls/FolderTree.py:216 +#: ../controls/FolderTree.py:219 #, python-format msgid "File '%s' already exists!" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:100 ../dialogs/FindInPouDialog.py:36 -#: ../dialogs/FindInPouDialog.py:106 ../IDEFrame.py:375 +#: ../dialogs/SearchInProjectDialog.py:101 ../dialogs/FindInPouDialog.py:38 +#: ../dialogs/FindInPouDialog.py:105 ../IDEFrame.py:389 msgid "Find" msgstr "" -#: ../IDEFrame.py:377 +#: ../IDEFrame.py:391 msgid "Find Next" msgstr "" -#: ../IDEFrame.py:379 +#: ../IDEFrame.py:393 msgid "Find Previous" msgstr "" @@ -1657,85 +1680,85 @@ msgid "Find position" msgstr "" -#: ../dialogs/FindInPouDialog.py:57 +#: ../dialogs/FindInPouDialog.py:56 msgid "Find:" msgstr "" -#: ../connectors/PYRO/__init__.py:163 +#: ../connectors/PYRO/__init__.py:166 msgid "Force runtime reload\n" msgstr "" -#: ../editors/Viewer.py:1553 +#: ../editors/Viewer.py:1641 msgid "Force value" msgstr "" -#: ../dialogs/ForceVariableDialog.py:162 +#: ../dialogs/ForceVariableDialog.py:174 msgid "Forcing Variable Value" msgstr "" -#: ../dialogs/SFCTransitionDialog.py:179 ../dialogs/PouTransitionDialog.py:102 -#: ../dialogs/ProjectDialog.py:70 ../dialogs/PouActionDialog.py:94 -#: ../dialogs/PouDialog.py:116 +#: ../dialogs/SFCTransitionDialog.py:183 ../dialogs/PouTransitionDialog.py:102 +#: ../dialogs/ProjectDialog.py:77 ../dialogs/PouActionDialog.py:100 +#: ../dialogs/PouDialog.py:123 #, python-format msgid "Form isn't complete. %s must be filled!" msgstr "" -#: ../dialogs/SFCStepDialog.py:144 ../dialogs/FBDBlockDialog.py:232 -#: ../dialogs/ConnectionDialog.py:160 +#: ../dialogs/SFCStepDialog.py:148 ../dialogs/FBDBlockDialog.py:237 +#: ../dialogs/ConnectionDialog.py:164 msgid "Form isn't complete. Name must be filled!" msgstr "" -#: ../dialogs/FBDBlockDialog.py:228 +#: ../dialogs/FBDBlockDialog.py:233 msgid "Form isn't complete. Valid block type must be selected!" msgstr "" -#: ../dialogs/FindInPouDialog.py:74 +#: ../dialogs/FindInPouDialog.py:73 msgid "Forward" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:36 ../IDEFrame.py:1746 +#: ../dialogs/SearchInProjectDialog.py:39 ../IDEFrame.py:1786 msgid "Function" msgstr "" -#: ../IDEFrame.py:349 +#: ../IDEFrame.py:363 msgid "Function &Block" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:37 ../IDEFrame.py:1745 -#: ../IDEFrame.py:1938 +#: ../dialogs/SearchInProjectDialog.py:40 ../IDEFrame.py:1785 +#: ../IDEFrame.py:1978 msgid "Function Block" msgstr "" -#: ../controls/VariablePanel.py:825 +#: ../controls/VariablePanel.py:889 msgid "Function Block Types" msgstr "" -#: ../PLCControler.py:95 +#: ../PLCControler.py:104 msgid "Function Blocks" msgstr "" -#: ../editors/Viewer.py:248 +#: ../editors/Viewer.py:287 msgid "Function Blocks can't be used in Functions!" msgstr "" -#: ../PLCControler.py:2337 +#: ../PLCControler.py:2409 #, python-format msgid "FunctionBlock \"%s\" can't be pasted in a Function!!!" msgstr "" -#: ../PLCControler.py:95 +#: ../PLCControler.py:104 msgid "Functions" msgstr "" -#: ../PLCOpenEditor.py:115 +#: ../PLCOpenEditor.py:135 msgid "Generate Program" msgstr "" -#: ../ProjectController.py:612 +#: ../ProjectController.py:720 msgid "Generating SoftPLC IEC-61131 ST/IL/SFC code...\n" msgstr "" -#: ../controls/VariablePanel.py:73 +#: ../controls/VariablePanel.py:90 msgid "Global" msgstr "" @@ -1743,7 +1766,7 @@ msgid "Go to current value" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:173 +#: ../controls/ProjectPropertiesPanel.py:179 msgid "Graphics" msgstr "" @@ -1755,105 +1778,109 @@ msgid "Greater than or equal to" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:134 +#: ../controls/ProjectPropertiesPanel.py:139 msgid "Grid Resolution:" msgstr "" -#: ../runtime/NevowServer.py:181 +#: ../runtime/NevowServer.py:188 msgid "HTTP interface port :" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:120 +#: ../controls/ProjectPropertiesPanel.py:125 msgid "Height:" msgstr "" -#: ../editors/FileManagementPanel.py:85 +#: ../editors/FileManagementPanel.py:87 msgid "Home Directory:" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:150 +#: ../controls/ProjectPropertiesPanel.py:155 msgid "Horizontal:" msgstr "" -#: ../dialogs/DurationEditorDialog.py:44 +#: ../dialogs/DurationEditorDialog.py:45 msgid "Hours:" msgstr "" -#: ../dialogs/PouActionDialog.py:31 ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:38 ../dialogs/PouActionDialog.py:34 +#: ../dialogs/PouDialog.py:42 msgid "IL" msgstr "" -#: ../dialogs/DiscoveryDialog.py:94 +#: ../dialogs/DiscoveryDialog.py:101 msgid "IP" msgstr "" -#: ../Beremiz_service.py:308 ../Beremiz_service.py:309 +#: ../Beremiz_service.py:318 ../Beremiz_service.py:320 msgid "IP is not valid!" msgstr "" -#: ../svgui/svgui.py:42 ../svgui/svgui.py:43 +#: ../svgui/svgui.py:49 ../svgui/svgui.py:50 msgid "Import SVG" msgstr "" -#: ../dialogs/FBDVariableDialog.py:38 ../editors/Viewer.py:1580 -#: ../controls/VariablePanel.py:71 +#: ../dialogs/FBDVariableDialog.py:40 ../editors/Viewer.py:1670 +#: ../controls/VariablePanel.py:88 msgid "InOut" msgstr "" -#: ../editors/Viewer.py:1100 +#: ../editors/Viewer.py:470 msgid "Inactive" msgstr "" -#: ../controls/VariablePanel.py:276 +#: ../controls/VariablePanel.py:300 #, python-brace-format msgid "Incompatible data types between \"{a1}\" and \"{a2}\"" msgstr "" -#: ../controls/VariablePanel.py:282 +#: ../controls/VariablePanel.py:306 #, python-format msgid "Incompatible size of data between \"%s\" and \"BOOL\"" msgstr "" -#: ../controls/VariablePanel.py:286 +#: ../controls/VariablePanel.py:310 #, python-brace-format msgid "Incompatible size of data between \"{a1}\" and \"{a2}\"" msgstr "" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:40 msgid "Indicator" msgstr "" -#: ../editors/Viewer.py:552 +#: ../editors/CodeFileEditor.py:749 +msgid "Initial" +msgstr "" + +#: ../editors/Viewer.py:653 msgid "Initial Step" msgstr "" -#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 -#: ../controls/VariablePanel.py:54 +#: ../editors/DataTypeEditor.py:53 ../controls/VariablePanel.py:62 msgid "Initial Value" msgstr "" -#: ../editors/DataTypeEditor.py:185 ../editors/DataTypeEditor.py:216 -#: ../editors/DataTypeEditor.py:272 ../editors/DataTypeEditor.py:310 +#: ../editors/DataTypeEditor.py:193 ../editors/DataTypeEditor.py:224 +#: ../editors/DataTypeEditor.py:282 ../editors/DataTypeEditor.py:321 msgid "Initial Value:" msgstr "" -#: ../svgui/svgui.py:46 +#: ../svgui/svgui.py:55 msgid "Inkscape" msgstr "" -#: ../dialogs/SFCTransitionDialog.py:75 ../dialogs/ActionBlockDialog.py:42 +#: ../dialogs/SFCTransitionDialog.py:76 ../dialogs/ActionBlockDialog.py:45 msgid "Inline" msgstr "" -#: ../dialogs/SFCStepDialog.py:70 ../dialogs/FBDVariableDialog.py:37 -#: ../dialogs/BrowseLocationsDialog.py:40 ../editors/Viewer.py:289 -#: ../editors/Viewer.py:1578 ../editors/TextViewer.py:307 -#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 -#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:39 +#: ../dialogs/BrowseLocationsDialog.py:43 ../editors/Viewer.py:330 +#: ../editors/Viewer.py:1668 ../editors/TextViewer.py:312 +#: ../controls/LocationCellEditor.py:101 ../controls/VariablePanel.py:88 +#: ../controls/VariablePanel.py:317 ../controls/VariablePanel.py:380 msgid "Input" msgstr "" -#: ../dialogs/FBDBlockDialog.py:93 +#: ../dialogs/FBDBlockDialog.py:97 msgid "Inputs:" msgstr "" @@ -1861,110 +1888,110 @@ msgid "Insertion (into)" msgstr "" -#: ../plcopen/plcopen.py:1691 +#: ../plcopen/plcopen.py:1774 #, python-format msgid "Instance with id %d doesn't exist!" msgstr "" -#: ../editors/ResourceEditor.py:264 +#: ../editors/ResourceEditor.py:277 msgid "Instances:" msgstr "" -#: ../controls/VariablePanel.py:70 +#: ../controls/VariablePanel.py:87 msgid "Interface" msgstr "" -#: ../editors/ResourceEditor.py:72 +#: ../editors/ResourceEditor.py:79 msgid "Interrupt" msgstr "" -#: ../editors/ResourceEditor.py:68 +#: ../editors/ResourceEditor.py:74 msgid "Interval" msgstr "" -#: ../PLCControler.py:2325 +#: ../PLCControler.py:2397 msgid "Invalid plcopen element(s)!!!" msgstr "" -#: ../canfestival/config_utils.py:381 +#: ../canfestival/config_utils.py:401 #, python-brace-format -msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location\"{a4}\"" -msgstr "" - -#: ../canfestival/config_utils.py:645 +msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" +msgstr "" + +#: ../canfestival/config_utils.py:683 #, python-brace-format msgid "Invalid type \"{a1}\"-> {a2} != {a3} for location \"{a4}\"" msgstr "" -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:132 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:136 #: ../controls/DebugVariablePanel/DebugVariableTextViewer.py:92 -#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:166 +#: ../controls/DebugVariablePanel/DebugVariableGraphicViewer.py:167 #, python-format msgid "Invalid value \"%s\" for debug variable" msgstr "" -#: ../controls/VariablePanel.py:255 ../controls/VariablePanel.py:258 +#: ../controls/VariablePanel.py:279 ../controls/VariablePanel.py:282 #, python-format msgid "Invalid value \"%s\" for variable grid element" msgstr "" -#: ../editors/Viewer.py:233 ../editors/Viewer.py:236 +#: ../editors/Viewer.py:272 ../editors/Viewer.py:275 #, python-format msgid "Invalid value \"%s\" for viewer block" msgstr "" -#: ../dialogs/ForceVariableDialog.py:177 +#: ../dialogs/ForceVariableDialog.py:209 #, python-brace-format msgid "Invalid value \"{a1}\" for \"{a2}\" variable!" msgstr "" -#: ../dialogs/DurationEditorDialog.py:121 +#: ../dialogs/DurationEditorDialog.py:122 msgid "" "Invalid value!\n" "You must fill a numeric value." msgstr "" -#: ../editors/Viewer.py:557 ../editors/Viewer.py:2343 +#: ../editors/Viewer.py:658 ../editors/Viewer.py:2435 msgid "Jump" msgstr "" -#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +#: ../dialogs/PouTransitionDialog.py:38 ../dialogs/PouActionDialog.py:34 +#: ../dialogs/PouDialog.py:42 ../controls/ProjectPropertiesPanel.py:148 msgid "LD" msgstr "" -#: ../editors/LDViewer.py:215 ../editors/LDViewer.py:231 +#: ../editors/LDViewer.py:219 ../editors/LDViewer.py:238 #, python-format msgid "Ladder element with id %d is on more than one rung." msgstr "" -#: ../dialogs/PouTransitionDialog.py:91 ../dialogs/PouActionDialog.py:83 -#: ../dialogs/PouDialog.py:104 +#: ../dialogs/PouTransitionDialog.py:91 ../dialogs/PouActionDialog.py:89 +#: ../dialogs/PouDialog.py:111 msgid "Language" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:186 +#: ../controls/ProjectPropertiesPanel.py:192 msgid "Language (optional):" msgstr "" -#: ../dialogs/PouTransitionDialog.py:65 ../dialogs/PouActionDialog.py:56 -#: ../dialogs/PouDialog.py:73 +#: ../dialogs/PouTransitionDialog.py:65 ../dialogs/PouActionDialog.py:61 +#: ../dialogs/PouDialog.py:79 msgid "Language:" msgstr "" -#: ../ProjectController.py:1735 +#: ../ProjectController.py:1822 msgid "Latest build already matches current target. Transfering anyway...\n" msgstr "" -#: ../Beremiz_service.py:271 +#: ../Beremiz_service.py:281 msgid "Launch WX GUI inspector" msgstr "" -#: ../Beremiz_service.py:270 +#: ../Beremiz_service.py:280 msgid "Launch a live Python shell" msgstr "" -#: ../editors/Viewer.py:485 +#: ../editors/Viewer.py:584 msgid "Left" msgstr "" @@ -1984,11 +2011,11 @@ msgid "Less than or equal to" msgstr "" -#: ../IDEFrame.py:631 +#: ../IDEFrame.py:667 msgid "Library" msgstr "" -#: ../dialogs/AboutDialog.py:143 +#: ../dialogs/AboutDialog.py:145 msgid "License" msgstr "" @@ -1996,27 +2023,27 @@ msgid "Limitation" msgstr "" -#: ../targets/toolchain_gcc.py:166 +#: ../targets/toolchain_gcc.py:205 msgid "Linking :\n" msgstr "" -#: ../dialogs/DiscoveryDialog.py:111 ../controls/VariablePanel.py:72 +#: ../dialogs/DiscoveryDialog.py:121 ../controls/VariablePanel.py:89 msgid "Local" msgstr "" -#: ../canfestival/canfestival.py:346 +#: ../canfestival/canfestival.py:368 msgid "Local entries" msgstr "" -#: ../ProjectController.py:1641 +#: ../ProjectController.py:1731 msgid "Local service discovery failed!\n" msgstr "" -#: ../controls/VariablePanel.py:53 +#: ../controls/VariablePanel.py:61 msgid "Location" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:72 +#: ../dialogs/BrowseLocationsDialog.py:79 msgid "Locations available:" msgstr "" @@ -2024,12 +2051,12 @@ msgid "Logarithm to base 10" msgstr "" -#: ../connectors/PYRO/__init__.py:94 +#: ../connectors/PYRO/__init__.py:97 #, python-format msgid "MDNS resolution failure for '%s'\n" msgstr "" -#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:85 +#: ../canfestival/SlaveEditor.py:67 ../canfestival/NetworkEditor.py:92 msgid "Map Variable" msgstr "" @@ -2037,11 +2064,11 @@ msgid "Map located variables over CANopen" msgstr "" -#: ../canfestival/NetworkEditor.py:106 +#: ../canfestival/NetworkEditor.py:113 msgid "Master" msgstr "" -#: ../ConfigTreeNode.py:539 +#: ../ConfigTreeNode.py:544 #, python-brace-format msgid "Max count ({a1}) reached for this confnode of type {a2} " msgstr "" @@ -2050,29 +2077,29 @@ msgid "Maximum" msgstr "" -#: ../editors/DataTypeEditor.py:239 +#: ../editors/DataTypeEditor.py:247 msgid "Maximum:" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:42 ../editors/Viewer.py:289 -#: ../editors/TextViewer.py:307 ../controls/LocationCellEditor.py:98 -#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +#: ../dialogs/BrowseLocationsDialog.py:45 ../editors/Viewer.py:330 +#: ../editors/TextViewer.py:312 ../controls/LocationCellEditor.py:101 +#: ../controls/VariablePanel.py:317 ../controls/VariablePanel.py:380 msgid "Memory" msgstr "" -#: ../IDEFrame.py:599 +#: ../IDEFrame.py:632 msgid "Menu ToolBar" msgstr "" +#: ../dialogs/DurationEditorDialog.py:49 +msgid "Microseconds:" +msgstr "" + +#: ../editors/Viewer.py:589 +msgid "Middle" +msgstr "" + #: ../dialogs/DurationEditorDialog.py:48 -msgid "Microseconds:" -msgstr "" - -#: ../editors/Viewer.py:490 -msgid "Middle" -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:47 msgid "Milliseconds:" msgstr "" @@ -2080,15 +2107,15 @@ msgid "Minimum" msgstr "" -#: ../editors/DataTypeEditor.py:226 +#: ../editors/DataTypeEditor.py:234 msgid "Minimum:" msgstr "" -#: ../dialogs/DurationEditorDialog.py:45 +#: ../dialogs/DurationEditorDialog.py:46 msgid "Minutes:" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:210 +#: ../controls/ProjectPropertiesPanel.py:217 msgid "Miscellaneous" msgstr "" @@ -2096,60 +2123,60 @@ msgid "Modifier:" msgstr "" -#: ../PLCGenerator.py:786 ../PLCGenerator.py:1230 +#: ../PLCGenerator.py:794 ../PLCGenerator.py:1245 #, python-brace-format msgid "More than one connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" msgstr "" -#: ../dialogs/ActionBlockDialog.py:140 +#: ../dialogs/ActionBlockDialog.py:144 msgid "Move action down" msgstr "" -#: ../dialogs/ActionBlockDialog.py:139 +#: ../dialogs/ActionBlockDialog.py:143 msgid "Move action up" msgstr "" +#: ../controls/CustomEditableListBox.py:44 +msgid "Move down" +msgstr "" + +#: ../editors/DataTypeEditor.py:368 +msgid "Move element down" +msgstr "" + +#: ../editors/DataTypeEditor.py:367 +msgid "Move element up" +msgstr "" + +#: ../editors/ResourceEditor.py:284 +msgid "Move instance down" +msgstr "" + +#: ../editors/ResourceEditor.py:283 +msgid "Move instance up" +msgstr "" + +#: ../editors/ResourceEditor.py:253 +msgid "Move task down" +msgstr "" + +#: ../editors/ResourceEditor.py:252 +msgid "Move task up" +msgstr "" + +#: ../IDEFrame.py:105 ../IDEFrame.py:120 ../IDEFrame.py:150 ../IDEFrame.py:191 +msgid "Move the view" +msgstr "" + #: ../controls/CustomEditableListBox.py:43 -msgid "Move down" -msgstr "" - -#: ../editors/DataTypeEditor.py:355 -msgid "Move element down" -msgstr "" - -#: ../editors/DataTypeEditor.py:354 -msgid "Move element up" -msgstr "" - -#: ../editors/ResourceEditor.py:271 -msgid "Move instance down" -msgstr "" - -#: ../editors/ResourceEditor.py:270 -msgid "Move instance up" -msgstr "" - -#: ../editors/ResourceEditor.py:242 -msgid "Move task down" -msgstr "" - -#: ../editors/ResourceEditor.py:241 -msgid "Move task up" -msgstr "" - -#: ../IDEFrame.py:99 ../IDEFrame.py:114 ../IDEFrame.py:144 ../IDEFrame.py:185 -msgid "Move the view" -msgstr "" - -#: ../controls/CustomEditableListBox.py:42 msgid "Move up" msgstr "" -#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:453 +#: ../editors/CodeFileEditor.py:662 ../controls/VariablePanel.py:484 msgid "Move variable down" msgstr "" -#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:452 +#: ../editors/CodeFileEditor.py:661 ../controls/VariablePanel.py:483 msgid "Move variable up" msgstr "" @@ -2161,26 +2188,26 @@ msgid "Multiplication" msgstr "" -#: ../editors/FileManagementPanel.py:83 +#: ../editors/FileManagementPanel.py:85 msgid "My Computer:" msgstr "" -#: ../dialogs/DiscoveryDialog.py:92 +#: ../dialogs/DiscoveryDialog.py:99 msgid "NAME" msgstr "" -#: ../editors/ResourceEditor.py:68 ../editors/ResourceEditor.py:83 -#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 -#: ../controls/VariablePanel.py:54 +#: ../editors/ResourceEditor.py:74 ../editors/ResourceEditor.py:95 +#: ../editors/DataTypeEditor.py:53 ../editors/CodeFileEditor.py:747 +#: ../controls/VariablePanel.py:58 msgid "Name" msgstr "" -#: ../Beremiz_service.py:332 +#: ../Beremiz_service.py:342 msgid "Name must not be null!" msgstr "" -#: ../dialogs/SFCStepDialog.py:56 ../dialogs/FBDBlockDialog.py:83 -#: ../dialogs/ConnectionDialog.py:75 +#: ../dialogs/SFCStepDialog.py:57 ../dialogs/FBDBlockDialog.py:87 +#: ../dialogs/ConnectionDialog.py:76 msgid "Name:" msgstr "" @@ -2188,76 +2215,76 @@ msgid "Natural logarithm" msgstr "" -#: ../dialogs/LDElementDialog.py:75 ../editors/Viewer.py:460 +#: ../dialogs/LDElementDialog.py:76 ../editors/Viewer.py:558 msgid "Negated" msgstr "" -#: ../Beremiz_service.py:578 +#: ../Beremiz_service.py:597 msgid "Nevow Web service failed. " msgstr "" -#: ../Beremiz_service.py:554 +#: ../Beremiz_service.py:573 msgid "Nevow/Athena import failed :" msgstr "" -#: ../PLCOpenEditor.py:102 ../PLCOpenEditor.py:144 ../Beremiz.py:321 -#: ../Beremiz.py:356 +#: ../BeremizIDE.py:246 ../BeremizIDE.py:281 ../PLCOpenEditor.py:122 +#: ../PLCOpenEditor.py:164 msgid "New" msgstr "" -#: ../controls/CustomEditableListBox.py:40 +#: ../controls/CustomEditableListBox.py:41 msgid "New item" msgstr "" -#: ../editors/Viewer.py:459 +#: ../editors/Viewer.py:557 msgid "No Modifier" msgstr "" -#: ../ProjectController.py:1763 +#: ../ProjectController.py:1851 msgid "No PLC to transfer (did build succeed ?)\n" msgstr "" -#: ../PLCGenerator.py:1631 +#: ../PLCGenerator.py:1654 #, python-format msgid "No body defined in \"%s\" POU" msgstr "" -#: ../PLCGenerator.py:806 ../PLCGenerator.py:1241 +#: ../PLCGenerator.py:815 ../PLCGenerator.py:1257 #, python-brace-format msgid "No connector found corresponding to \"{a1}\" continuation in \"{a2}\" POU" msgstr "" -#: ../PLCOpenEditor.py:347 +#: ../PLCOpenEditor.py:378 msgid "" "No documentation available.\n" "Coming soon." msgstr "" -#: ../PLCGenerator.py:829 +#: ../PLCGenerator.py:840 #, python-format msgid "No informations found for \"%s\" block" msgstr "" -#: ../PLCGenerator.py:1194 +#: ../PLCGenerator.py:1208 #, python-brace-format msgid "No output {a1} variable found in block {a2} in POU {a3}. Connection must be broken" msgstr "" -#: ../controls/SearchResultPanel.py:169 +#: ../controls/SearchResultPanel.py:175 msgid "No search results available." msgstr "" -#: ../svgui/svgui.py:131 +#: ../svgui/svgui.py:141 #, python-format msgid "No such SVG file: %s\n" msgstr "" -#: ../canfestival/config_utils.py:639 +#: ../canfestival/config_utils.py:676 #, python-brace-format msgid "No such index/subindex ({a1},{a2}) (variable {a3})" msgstr "" -#: ../canfestival/config_utils.py:362 +#: ../canfestival/config_utils.py:381 #, python-brace-format msgid "No such index/subindex ({a1},{a2}) in ID : {a3} (variable {a4})" msgstr "" @@ -2266,25 +2293,25 @@ msgid "No valid value selected!" msgstr "" -#: ../PLCGenerator.py:1629 +#: ../PLCGenerator.py:1652 #, python-format msgid "No variable defined in \"%s\" POU" msgstr "" -#: ../canfestival/config_utils.py:355 +#: ../canfestival/config_utils.py:373 #, python-brace-format msgid "Non existing node ID : {a1} (variable {a2})" msgstr "" -#: ../controls/VariablePanel.py:64 +#: ../controls/VariablePanel.py:78 msgid "Non-Retain" msgstr "" -#: ../dialogs/LDElementDialog.py:75 +#: ../dialogs/LDElementDialog.py:76 msgid "Normal" msgstr "" -#: ../canfestival/config_utils.py:389 +#: ../canfestival/config_utils.py:420 #, python-brace-format msgid "Not PDO mappable variable : '{a1}' (ID:{a2},Idx:{a3},sIdx:{a4}))" msgstr "" @@ -2293,7 +2320,7 @@ msgid "Not equal to" msgstr "" -#: ../dialogs/SFCDivergenceDialog.py:89 +#: ../dialogs/SFCDivergenceDialog.py:90 msgid "Number of sequences:" msgstr "" @@ -2301,161 +2328,165 @@ msgid "Numerical" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:86 +#: ../editors/CodeFileEditor.py:751 +msgid "OnChange" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:87 msgid "Only Elements" msgstr "" -#: ../PLCOpenEditor.py:104 ../PLCOpenEditor.py:145 ../Beremiz.py:323 -#: ../Beremiz.py:357 +#: ../BeremizIDE.py:248 ../BeremizIDE.py:282 ../PLCOpenEditor.py:124 +#: ../PLCOpenEditor.py:165 msgid "Open" msgstr "" -#: ../svgui/svgui.py:140 +#: ../svgui/svgui.py:150 msgid "Open Inkscape" msgstr "" -#: ../version.py:66 +#: ../version.py:85 msgid "Open Source framework for automation, implemented IEC 61131 IDE with constantly growing set of extensions and flexible PLC runtime." msgstr "" -#: ../ProjectController.py:1815 +#: ../ProjectController.py:1922 msgid "Open a file explorer to manage project files" msgstr "" -#: ../wxglade_hmi/wxglade_hmi.py:138 +#: ../wxglade_hmi/wxglade_hmi.py:159 msgid "Open wxGlade" msgstr "" -#: ../controls/VariablePanel.py:53 ../controls/VariablePanel.py:54 +#: ../controls/VariablePanel.py:63 msgid "Option" msgstr "" -#: ../dialogs/FindInPouDialog.py:83 +#: ../dialogs/FindInPouDialog.py:82 ../editors/CodeFileEditor.py:752 msgid "Options" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:97 +#: ../controls/ProjectPropertiesPanel.py:101 msgid "Organization (optional):" msgstr "" -#: ../canfestival/SlaveEditor.py:74 ../canfestival/NetworkEditor.py:95 +#: ../canfestival/SlaveEditor.py:77 ../canfestival/NetworkEditor.py:102 msgid "Other Profile" msgstr "" -#: ../dialogs/SFCStepDialog.py:71 ../dialogs/FBDVariableDialog.py:39 -#: ../dialogs/BrowseLocationsDialog.py:41 ../editors/Viewer.py:289 -#: ../editors/Viewer.py:1579 ../editors/TextViewer.py:307 -#: ../controls/LocationCellEditor.py:98 ../controls/VariablePanel.py:71 -#: ../controls/VariablePanel.py:291 ../controls/VariablePanel.py:351 +#: ../dialogs/SFCStepDialog.py:72 ../dialogs/FBDVariableDialog.py:41 +#: ../dialogs/BrowseLocationsDialog.py:44 ../editors/Viewer.py:330 +#: ../editors/Viewer.py:1669 ../editors/TextViewer.py:312 +#: ../controls/LocationCellEditor.py:101 ../controls/VariablePanel.py:88 +#: ../controls/VariablePanel.py:317 ../controls/VariablePanel.py:380 msgid "Output" msgstr "" -#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:84 +#: ../canfestival/SlaveEditor.py:66 ../canfestival/NetworkEditor.py:91 msgid "PDO Receive" msgstr "" -#: ../canfestival/SlaveEditor.py:62 ../canfestival/NetworkEditor.py:83 +#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:90 msgid "PDO Transmit" msgstr "" -#: ../targets/toolchain_gcc.py:131 +#: ../targets/toolchain_gcc.py:170 msgid "PLC :\n" msgstr "" -#: ../Beremiz.py:453 +#: ../BeremizIDE.py:388 msgid "PLC Log" msgstr "" -#: ../ProjectController.py:992 +#: ../ProjectController.py:1080 msgid "PLC code generation failed !\n" msgstr "" -#: ../Beremiz_service.py:295 +#: ../Beremiz_service.py:305 msgid "PLC is empty or already started." msgstr "" -#: ../Beremiz_service.py:302 +#: ../Beremiz_service.py:312 msgid "PLC is not started." msgstr "" -#: ../PLCOpenEditor.py:196 ../PLCOpenEditor.py:309 +#: ../PLCOpenEditor.py:227 ../PLCOpenEditor.py:340 #, python-brace-format msgid "" "PLC syntax error at line {a1}:\n" "{a2}" msgstr "" -#: ../PLCOpenEditor.py:292 ../PLCOpenEditor.py:373 +#: ../PLCOpenEditor.py:323 ../PLCOpenEditor.py:404 msgid "PLCOpen files (*.xml)|*.xml|All files|*.*" msgstr "" -#: ../PLCOpenEditor.py:152 ../PLCOpenEditor.py:209 +#: ../PLCOpenEditor.py:172 ../PLCOpenEditor.py:240 msgid "PLCOpenEditor" msgstr "" -#: ../PLCOpenEditor.py:355 +#: ../PLCOpenEditor.py:386 msgid "" "PLCOpenEditor is part of Beremiz project.\n" "\n" "Beremiz is an " msgstr "" -#: ../dialogs/DiscoveryDialog.py:95 +#: ../dialogs/DiscoveryDialog.py:102 msgid "PORT" msgstr "" -#: ../dialogs/PouDialog.py:100 +#: ../dialogs/PouDialog.py:107 msgid "POU Name" msgstr "" -#: ../dialogs/PouDialog.py:58 +#: ../dialogs/PouDialog.py:64 msgid "POU Name:" msgstr "" -#: ../dialogs/PouDialog.py:102 +#: ../dialogs/PouDialog.py:109 msgid "POU Type" msgstr "" -#: ../dialogs/PouDialog.py:65 +#: ../dialogs/PouDialog.py:71 msgid "POU Type:" msgstr "" -#: ../connectors/PYRO/__init__.py:45 +#: ../connectors/PYRO/__init__.py:47 #, python-format msgid "PYRO connecting to URI : %s\n" msgstr "" -#: ../connectors/PYRO/__init__.py:61 +#: ../connectors/PYRO/__init__.py:63 #, python-format msgid "PYRO using certificates in '%s' \n" msgstr "" -#: ../PLCOpenEditor.py:118 ../Beremiz.py:336 +#: ../BeremizIDE.py:261 ../PLCOpenEditor.py:138 msgid "Page Setup" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:110 +#: ../controls/ProjectPropertiesPanel.py:114 msgid "Page Size (optional):" msgstr "" -#: ../IDEFrame.py:2599 +#: ../IDEFrame.py:2651 #, python-format msgid "Page: %d" msgstr "" -#: ../controls/PouInstanceVariablesPanel.py:124 +#: ../controls/PouInstanceVariablesPanel.py:140 msgid "Parent instance" msgstr "" -#: ../editors/Viewer.py:598 ../IDEFrame.py:372 ../IDEFrame.py:426 +#: ../editors/Viewer.py:699 ../IDEFrame.py:386 ../IDEFrame.py:440 msgid "Paste" msgstr "" -#: ../IDEFrame.py:1865 +#: ../IDEFrame.py:1905 msgid "Paste POU" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:56 +#: ../dialogs/SearchInProjectDialog.py:59 msgid "Pattern to search:" msgstr "" @@ -2463,42 +2494,42 @@ msgid "Pin number:" msgstr "" -#: ../editors/Viewer.py:2706 ../editors/Viewer.py:2963 -#: ../editors/SFCViewer.py:770 +#: ../editors/Viewer.py:2802 ../editors/Viewer.py:3070 +#: ../editors/SFCViewer.py:771 msgid "Please choose a target" msgstr "" -#: ../editors/TextViewer.py:262 +#: ../editors/TextViewer.py:265 msgid "Please enter a block name" msgstr "" -#: ../editors/Viewer.py:2576 ../editors/Viewer.py:3005 +#: ../editors/Viewer.py:2671 ../editors/Viewer.py:3113 msgid "Please enter comment text" msgstr "" #: ../editors/SFCViewer.py:433 ../editors/SFCViewer.py:455 -#: ../editors/SFCViewer.py:799 +#: ../editors/SFCViewer.py:801 msgid "Please enter step name" msgstr "" -#: ../Beremiz_service.py:194 +#: ../dialogs/PouNameDialog.py:34 ../Beremiz_service.py:204 msgid "Please enter text" msgstr "" -#: ../dialogs/ForceVariableDialog.py:163 +#: ../dialogs/ForceVariableDialog.py:175 #, python-format msgid "Please enter value for a \"%s\" variable:" msgstr "" -#: ../Beremiz_service.py:317 +#: ../Beremiz_service.py:327 msgid "Port number must be 0 <= port <= 65535!" msgstr "" -#: ../Beremiz_service.py:317 +#: ../Beremiz_service.py:327 msgid "Port number must be an integer!" msgstr "" -#: ../editors/Viewer.py:536 ../editors/Viewer.py:2367 +#: ../editors/Viewer.py:636 ../editors/Viewer.py:2459 msgid "Power Rail" msgstr "" @@ -2506,7 +2537,7 @@ msgid "Power Rail Properties" msgstr "" -#: ../PLCOpenEditor.py:120 ../Beremiz.py:338 +#: ../BeremizIDE.py:263 ../PLCOpenEditor.py:140 msgid "Preview" msgstr "" @@ -2514,131 +2545,131 @@ msgid "Preview:" msgstr "" -#: ../PLCOpenEditor.py:122 ../PLCOpenEditor.py:148 ../Beremiz.py:340 -#: ../Beremiz.py:360 +#: ../BeremizIDE.py:265 ../BeremizIDE.py:285 ../PLCOpenEditor.py:142 +#: ../PLCOpenEditor.py:168 msgid "Print" msgstr "" -#: ../IDEFrame.py:1075 +#: ../IDEFrame.py:1116 msgid "Print preview" msgstr "" -#: ../editors/ResourceEditor.py:68 +#: ../editors/ResourceEditor.py:74 msgid "Priority" msgstr "" -#: ../dialogs/SFCTransitionDialog.py:89 +#: ../dialogs/SFCTransitionDialog.py:90 msgid "Priority:" msgstr "" -#: ../runtime/PLCObject.py:370 +#: ../runtime/PLCObject.py:375 #, python-format msgid "Problem starting PLC : error %d" msgstr "" -#: ../dialogs/ProjectDialog.py:55 +#: ../dialogs/ProjectDialog.py:61 msgid "Product Name" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:80 +#: ../controls/ProjectPropertiesPanel.py:84 msgid "Product Name (required):" msgstr "" +#: ../controls/ProjectPropertiesPanel.py:86 +msgid "Product Release (optional):" +msgstr "" + +#: ../dialogs/ProjectDialog.py:62 +msgid "Product Version" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:85 +msgid "Product Version (required):" +msgstr "" + +#: ../dialogs/SearchInProjectDialog.py:41 ../IDEFrame.py:1784 +#: ../IDEFrame.py:1981 +msgid "Program" +msgstr "" + +#: ../PLCOpenEditor.py:368 +msgid "Program was successfully generated!" +msgstr "" + +#: ../PLCControler.py:105 +msgid "Programs" +msgstr "" + +#: ../editors/Viewer.py:281 +msgid "Programs can't be used by other POUs!" +msgstr "" + +#: ../controls/ProjectPropertiesPanel.py:88 ../IDEFrame.py:616 +msgid "Project" +msgstr "" + +#: ../controls/SearchResultPanel.py:180 +#, python-format +msgid "Project '%s':" +msgstr "" + +#: ../ProjectController.py:1921 +msgid "Project Files" +msgstr "" + +#: ../dialogs/ProjectDialog.py:60 +msgid "Project Name" +msgstr "" + #: ../controls/ProjectPropertiesPanel.py:82 -msgid "Product Release (optional):" -msgstr "" - -#: ../dialogs/ProjectDialog.py:56 -msgid "Product Version" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:81 -msgid "Product Version (required):" -msgstr "" - -#: ../dialogs/SearchInProjectDialog.py:38 ../IDEFrame.py:1744 -#: ../IDEFrame.py:1941 -msgid "Program" -msgstr "" - -#: ../PLCOpenEditor.py:337 -msgid "Program was successfully generated!" -msgstr "" - -#: ../PLCControler.py:96 -msgid "Programs" -msgstr "" - -#: ../editors/Viewer.py:242 -msgid "Programs can't be used by other POUs!" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:84 ../IDEFrame.py:584 -msgid "Project" -msgstr "" - -#: ../controls/SearchResultPanel.py:173 -#, python-format -msgid "Project '%s':" -msgstr "" - -#: ../ProjectController.py:1814 -msgid "Project Files" -msgstr "" - -#: ../dialogs/ProjectDialog.py:54 -msgid "Project Name" -msgstr "" - -#: ../controls/ProjectPropertiesPanel.py:78 msgid "Project Name (required):" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:79 +#: ../controls/ProjectPropertiesPanel.py:83 msgid "Project Version (optional):" msgstr "" -#: ../PLCControler.py:3158 +#: ../PLCControler.py:3230 msgid "" "Project file syntax error:\n" "\n" msgstr "" -#: ../dialogs/ProjectDialog.py:32 ../editors/ProjectNodeEditor.py:37 +#: ../dialogs/ProjectDialog.py:34 ../editors/ProjectNodeEditor.py:38 msgid "Project properties" msgstr "" -#: ../ConfigTreeNode.py:566 +#: ../ConfigTreeNode.py:572 #, python-brace-format msgid "Project tree layout do not match confnode.xml {a1}!={a2} " msgstr "" -#: ../dialogs/ConnectionDialog.py:94 +#: ../dialogs/ConnectionDialog.py:99 msgid "Propagate Name" msgstr "" -#: ../PLCControler.py:97 +#: ../PLCControler.py:106 msgid "Properties" msgstr "" -#: ../Beremiz_service.py:440 +#: ../Beremiz_service.py:450 msgid "Publishing service on local network" msgstr "" -#: ../connectors/PYRO/__init__.py:118 +#: ../connectors/PYRO/__init__.py:121 #, python-format msgid "Pyro exception: %s\n" msgstr "" -#: ../Beremiz_service.py:427 +#: ../Beremiz_service.py:437 msgid "Pyro object's uri :" msgstr "" -#: ../Beremiz_service.py:426 +#: ../Beremiz_service.py:436 msgid "Pyro port :" msgstr "" -#: ../py_ext/PythonEditor.py:81 +#: ../py_ext/PythonEditor.py:82 msgid "Python code" msgstr "" @@ -2646,48 +2677,48 @@ msgid "Python file" msgstr "" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:40 msgid "Qualifier" msgstr "" -#: ../PLCOpenEditor.py:128 ../Beremiz.py:343 ../Beremiz_service.py:273 +#: ../BeremizIDE.py:268 ../PLCOpenEditor.py:148 ../Beremiz_service.py:283 msgid "Quit" msgstr "" -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:225 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:224 msgid "Range:" msgstr "" -#: ../ProjectController.py:1810 +#: ../ProjectController.py:1915 msgid "Raw IEC code" msgstr "" -#: ../Beremiz.py:1143 +#: ../BeremizIDE.py:1085 #, python-format msgid "Really delete node '%s'?" msgstr "" -#: ../IDEFrame.py:362 ../IDEFrame.py:422 +#: ../IDEFrame.py:376 ../IDEFrame.py:436 msgid "Redo" msgstr "" -#: ../dialogs/SFCTransitionDialog.py:74 +#: ../dialogs/SFCTransitionDialog.py:75 msgid "Reference" msgstr "" -#: ../dialogs/DiscoveryDialog.py:106 ../IDEFrame.py:432 +#: ../dialogs/DiscoveryDialog.py:115 ../IDEFrame.py:446 msgid "Refresh" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:66 +#: ../dialogs/SearchInProjectDialog.py:69 msgid "Regular expression" msgstr "" -#: ../dialogs/FindInPouDialog.py:98 +#: ../dialogs/FindInPouDialog.py:97 msgid "Regular expressions" msgstr "" -#: ../editors/Viewer.py:1556 +#: ../editors/Viewer.py:1644 msgid "Release value" msgstr "" @@ -2695,56 +2726,56 @@ msgid "Remainder (modulo)" msgstr "" -#: ../Beremiz.py:1144 +#: ../BeremizIDE.py:1086 #, python-format msgid "Remove %s node" msgstr "" -#: ../IDEFrame.py:2405 +#: ../IDEFrame.py:2454 msgid "Remove Datatype" msgstr "" -#: ../IDEFrame.py:2410 +#: ../IDEFrame.py:2459 msgid "Remove Pou" msgstr "" -#: ../dialogs/ActionBlockDialog.py:138 +#: ../dialogs/ActionBlockDialog.py:142 msgid "Remove action" msgstr "" -#: ../editors/DataTypeEditor.py:353 +#: ../editors/DataTypeEditor.py:366 msgid "Remove element" msgstr "" -#: ../editors/FileManagementPanel.py:63 +#: ../editors/FileManagementPanel.py:64 msgid "Remove file from left folder" msgstr "" -#: ../editors/ResourceEditor.py:269 +#: ../editors/ResourceEditor.py:282 msgid "Remove instance" msgstr "" -#: ../canfestival/NetworkEditor.py:104 +#: ../canfestival/NetworkEditor.py:111 msgid "Remove slave" msgstr "" -#: ../editors/ResourceEditor.py:240 +#: ../editors/ResourceEditor.py:251 msgid "Remove task" msgstr "" -#: ../editors/CodeFileEditor.py:659 ../controls/VariablePanel.py:451 +#: ../editors/CodeFileEditor.py:660 ../controls/VariablePanel.py:482 msgid "Remove variable" msgstr "" -#: ../IDEFrame.py:1945 +#: ../IDEFrame.py:1985 msgid "Rename" msgstr "" -#: ../editors/FileManagementPanel.py:181 +#: ../editors/FileManagementPanel.py:185 msgid "Replace File" msgstr "" -#: ../editors/Viewer.py:502 +#: ../editors/Viewer.py:601 msgid "Replace Wire by connections" msgstr "" @@ -2752,35 +2783,35 @@ msgid "Replacement (within)" msgstr "" -#: ../dialogs/LDElementDialog.py:76 +#: ../dialogs/LDElementDialog.py:77 msgid "Reset" msgstr "" -#: ../editors/Viewer.py:583 +#: ../editors/Viewer.py:684 msgid "Reset Execution Order" msgstr "" -#: ../IDEFrame.py:451 +#: ../IDEFrame.py:465 msgid "Reset Perspective" msgstr "" -#: ../controls/SearchResultPanel.py:105 +#: ../controls/SearchResultPanel.py:111 msgid "Reset search result" msgstr "" -#: ../PLCControler.py:97 ../Beremiz.py:1075 +#: ../BeremizIDE.py:1015 ../PLCControler.py:106 msgid "Resources" msgstr "" -#: ../controls/VariablePanel.py:62 +#: ../controls/VariablePanel.py:76 msgid "Retain" msgstr "" -#: ../controls/VariablePanel.py:424 +#: ../controls/VariablePanel.py:455 msgid "Return Type:" msgstr "" -#: ../editors/Viewer.py:487 +#: ../editors/Viewer.py:586 msgid "Right" msgstr "" @@ -2788,7 +2819,7 @@ msgid "Right PowerRail" msgstr "" -#: ../dialogs/LDElementDialog.py:77 ../editors/Viewer.py:461 +#: ../dialogs/LDElementDialog.py:78 ../editors/Viewer.py:559 msgid "Rising Edge" msgstr "" @@ -2804,50 +2835,50 @@ msgid "Rounding up/down" msgstr "" -#: ../ProjectController.py:1778 +#: ../ProjectController.py:1871 msgid "Run" msgstr "" -#: ../ProjectController.py:1037 +#: ../ProjectController.py:1125 msgid "Runtime IO extensions C code generation failed !\n" msgstr "" -#: ../ProjectController.py:1046 +#: ../ProjectController.py:1134 msgid "Runtime library extensions C code generation failed !\n" msgstr "" -#: ../canfestival/SlaveEditor.py:61 ../canfestival/NetworkEditor.py:82 +#: ../canfestival/SlaveEditor.py:64 ../canfestival/NetworkEditor.py:89 msgid "SDO Client" msgstr "" -#: ../canfestival/SlaveEditor.py:60 ../canfestival/NetworkEditor.py:81 +#: ../canfestival/SlaveEditor.py:63 ../canfestival/NetworkEditor.py:88 msgid "SDO Server" msgstr "" -#: ../dialogs/PouDialog.py:36 ../controls/ProjectPropertiesPanel.py:143 +#: ../dialogs/PouDialog.py:42 ../controls/ProjectPropertiesPanel.py:148 msgid "SFC" msgstr "" -#: ../PLCGenerator.py:1392 +#: ../PLCGenerator.py:1409 #, python-brace-format msgid "SFC jump in pou \"{a1}\" refers to non-existent SFC step \"{a2}\"" msgstr "" -#: ../PLCGenerator.py:773 +#: ../PLCGenerator.py:779 #, python-format msgid "SFC transition in POU \"%s\" must be connected." msgstr "" -#: ../dialogs/PouTransitionDialog.py:40 ../dialogs/PouActionDialog.py:31 -#: ../dialogs/PouDialog.py:36 +#: ../dialogs/PouTransitionDialog.py:38 ../dialogs/PouActionDialog.py:34 +#: ../dialogs/PouDialog.py:42 msgid "ST" msgstr "" -#: ../PLCOpenEditor.py:324 +#: ../PLCOpenEditor.py:355 msgid "ST files (*.st)|*.st|All files|*.*" msgstr "" -#: ../svgui/svgui.py:125 +#: ../svgui/svgui.py:135 msgid "SVG files (*.svg)|*.svg|All files|*.*" msgstr "" @@ -2855,62 +2886,62 @@ msgid "SVGUI" msgstr "" -#: ../PLCOpenEditor.py:111 ../PLCOpenEditor.py:146 ../Beremiz.py:327 -#: ../Beremiz.py:358 +#: ../BeremizIDE.py:252 ../BeremizIDE.py:283 ../PLCOpenEditor.py:131 +#: ../PLCOpenEditor.py:166 msgid "Save" msgstr "" -#: ../PLCOpenEditor.py:113 ../PLCOpenEditor.py:147 ../Beremiz.py:359 +#: ../BeremizIDE.py:284 ../PLCOpenEditor.py:133 ../PLCOpenEditor.py:167 msgid "Save As..." msgstr "" -#: ../Beremiz.py:329 +#: ../BeremizIDE.py:254 msgid "Save as" msgstr "" -#: ../ProjectController.py:420 +#: ../ProjectController.py:528 msgid "Save path is the same as path of a project! \n" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:69 +#: ../dialogs/SearchInProjectDialog.py:72 msgid "Scope" msgstr "" -#: ../IDEFrame.py:623 +#: ../IDEFrame.py:659 msgid "Search" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:44 ../IDEFrame.py:382 ../IDEFrame.py:428 +#: ../dialogs/SearchInProjectDialog.py:48 ../IDEFrame.py:396 ../IDEFrame.py:442 msgid "Search in Project" msgstr "" -#: ../dialogs/DurationEditorDialog.py:46 +#: ../dialogs/DurationEditorDialog.py:47 msgid "Seconds:" msgstr "" -#: ../IDEFrame.py:388 +#: ../IDEFrame.py:402 msgid "Select All" msgstr "" -#: ../editors/Viewer.py:288 ../editors/TextViewer.py:306 -#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 -#: ../controls/VariablePanel.py:350 +#: ../editors/Viewer.py:328 ../editors/TextViewer.py:310 +#: ../controls/LocationCellEditor.py:99 ../controls/VariablePanel.py:315 +#: ../controls/VariablePanel.py:378 msgid "Select a variable class:" msgstr "" -#: ../ProjectController.py:1195 +#: ../ProjectController.py:1288 msgid "Select an editor:" msgstr "" -#: ../controls/PouInstanceVariablesPanel.py:276 +#: ../controls/PouInstanceVariablesPanel.py:298 msgid "Select an instance" msgstr "" -#: ../IDEFrame.py:607 +#: ../IDEFrame.py:643 msgid "Select an object" msgstr "" -#: ../ProjectController.py:427 +#: ../ProjectController.py:535 msgid "Selected directory already contains another project. Overwrite? \n" msgstr "" @@ -2926,15 +2957,15 @@ msgid "Selection Divergence" msgstr "" -#: ../dialogs/DiscoveryDialog.py:82 +#: ../dialogs/DiscoveryDialog.py:87 msgid "Service Discovery" msgstr "" -#: ../dialogs/DiscoveryDialog.py:85 +#: ../dialogs/DiscoveryDialog.py:91 msgid "Services available:" msgstr "" -#: ../dialogs/LDElementDialog.py:76 +#: ../dialogs/LDElementDialog.py:77 msgid "Set" msgstr "" @@ -2946,19 +2977,19 @@ msgid "Shift right" msgstr "" -#: ../ProjectController.py:1804 +#: ../ProjectController.py:1906 msgid "Show IEC code generated by PLCGenerator" msgstr "" -#: ../canfestival/canfestival.py:387 +#: ../canfestival/canfestival.py:416 msgid "Show Master" msgstr "" -#: ../canfestival/canfestival.py:388 +#: ../canfestival/canfestival.py:417 msgid "Show Master generated by config_utils" msgstr "" -#: ../ProjectController.py:1802 +#: ../ProjectController.py:1905 msgid "Show code" msgstr "" @@ -2974,15 +3005,15 @@ msgid "Sine" msgstr "" -#: ../editors/ResourceEditor.py:68 +#: ../editors/ResourceEditor.py:74 msgid "Single" msgstr "" -#: ../targets/toolchain_makefile.py:126 +#: ../targets/toolchain_makefile.py:128 msgid "Source didn't change, no build.\n" msgstr "" -#: ../PLCGenerator.py:397 +#: ../PLCGenerator.py:401 #, python-brace-format msgid "Source signal has to be defined for single task '{a1}' in resource '{a2}.{a3}'." msgstr "" @@ -2991,60 +3022,60 @@ msgid "Square root (base 2)" msgstr "" -#: ../plcopen/definitions.py:46 +#: ../plcopen/definitions.py:49 msgid "Standard function blocks" msgstr "" -#: ../ProjectController.py:1780 ../Beremiz_service.py:261 +#: ../ProjectController.py:1872 ../Beremiz_service.py:271 msgid "Start PLC" msgstr "" -#: ../ProjectController.py:984 +#: ../ProjectController.py:1072 #, python-format msgid "Start build in %s\n" msgstr "" -#: ../ProjectController.py:1298 +#: ../ProjectController.py:1392 msgid "Started" msgstr "" -#: ../ProjectController.py:1586 +#: ../ProjectController.py:1679 msgid "Starting PLC\n" msgstr "" -#: ../Beremiz.py:463 +#: ../BeremizIDE.py:398 msgid "Status ToolBar" msgstr "" -#: ../editors/Viewer.py:553 ../editors/Viewer.py:2342 +#: ../editors/Viewer.py:654 ../editors/Viewer.py:2434 msgid "Step" msgstr "" -#: ../ProjectController.py:1783 +#: ../ProjectController.py:1878 msgid "Stop" msgstr "" -#: ../Beremiz_service.py:262 +#: ../Beremiz_service.py:272 msgid "Stop PLC" msgstr "" -#: ../ProjectController.py:1785 +#: ../ProjectController.py:1879 msgid "Stop Running PLC" msgstr "" -#: ../ProjectController.py:1299 +#: ../ProjectController.py:1393 msgid "Stopped" msgstr "" -#: ../ProjectController.py:1558 +#: ../ProjectController.py:1651 msgid "Stopping debugger...\n" msgstr "" -#: ../editors/DataTypeEditor.py:54 +#: ../editors/DataTypeEditor.py:58 msgid "Structure" msgstr "" -#: ../editors/DataTypeEditor.py:54 +#: ../editors/DataTypeEditor.py:58 msgid "Subrange" msgstr "" @@ -3052,19 +3083,19 @@ msgid "Subtraction" msgstr "" -#: ../ProjectController.py:1023 +#: ../ProjectController.py:1111 msgid "Successfully built.\n" msgstr "" -#: ../IDEFrame.py:447 +#: ../IDEFrame.py:461 msgid "Switch perspective" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:165 ../dialogs/FindInPouDialog.py:172 +#: ../dialogs/SearchInProjectDialog.py:168 ../dialogs/FindInPouDialog.py:116 msgid "Syntax error in regular expression of pattern to search!" msgstr "" -#: ../dialogs/DiscoveryDialog.py:93 +#: ../dialogs/DiscoveryDialog.py:100 msgid "TYPE" msgstr "" @@ -3072,49 +3103,61 @@ msgid "Tangent" msgstr "" -#: ../editors/ResourceEditor.py:83 +#: ../editors/ResourceEditor.py:95 msgid "Task" msgstr "" -#: ../editors/ResourceEditor.py:235 +#: ../editors/ResourceEditor.py:246 msgid "Tasks:" msgstr "" -#: ../controls/VariablePanel.py:73 +#: ../controls/VariablePanel.py:90 msgid "Temp" msgstr "" -#: ../editors/FileManagementPanel.py:180 +#: ../version.py:34 +msgid "" +"The best place to ask questions about Beremiz/PLCOpenEditor\n" +"is project's mailing list: beremiz-devel@lists.sourceforge.net\n" +"\n" +"This is the main community support channel.\n" +"For posting it is required to be subscribed to the mailing list.\n" +"\n" +"You can subscribe to the list here:\n" +"https://lists.sourceforge.net/lists/listinfo/beremiz-devel" +msgstr "" + +#: ../editors/FileManagementPanel.py:184 #, python-format msgid "" "The file '%s' already exist.\n" "Do you want to replace it?" msgstr "" -#: ../editors/LDViewer.py:882 +#: ../editors/LDViewer.py:891 msgid "The group of block must be coherent!" msgstr "" -#: ../Beremiz.py:640 ../IDEFrame.py:1011 +#: ../BeremizIDE.py:577 ../IDEFrame.py:1052 msgid "There are changes, do you want to save?" msgstr "" -#: ../IDEFrame.py:1655 ../IDEFrame.py:1674 +#: ../IDEFrame.py:1695 ../IDEFrame.py:1714 #, python-format msgid "There is a POU named \"%s\". This could cause a conflict. Do you wish to continue?" msgstr "" -#: ../IDEFrame.py:1098 +#: ../IDEFrame.py:1139 msgid "" "There was a problem printing.\n" "Perhaps your current printer is not set correctly?" msgstr "" -#: ../editors/LDViewer.py:891 +#: ../editors/LDViewer.py:900 msgid "This option isn't available yet!" msgstr "" -#: ../controls/DebugVariablePanel/DebugVariablePanel.py:565 +#: ../controls/DebugVariablePanel/DebugVariablePanel.py:564 #, python-format msgid "Tick: %d" msgstr "" @@ -3152,31 +3195,35 @@ msgid "Time-of-day subtraction" msgstr "" -#: ../editors/Viewer.py:489 +#: ../dialogs/ForceVariableDialog.py:186 +msgid "Toggle value" +msgstr "" + +#: ../editors/Viewer.py:588 msgid "Top" msgstr "" -#: ../ProjectController.py:1792 +#: ../ProjectController.py:1891 msgid "Transfer" msgstr "" -#: ../ProjectController.py:1794 +#: ../ProjectController.py:1892 msgid "Transfer PLC" msgstr "" -#: ../ProjectController.py:1758 +#: ../ProjectController.py:1845 msgid "Transfer completed successfully.\n" msgstr "" -#: ../ProjectController.py:1760 +#: ../ProjectController.py:1848 msgid "Transfer failed\n" msgstr "" -#: ../editors/Viewer.py:554 ../editors/Viewer.py:2344 ../editors/Viewer.py:2371 +#: ../editors/Viewer.py:655 ../editors/Viewer.py:2436 ../editors/Viewer.py:2463 msgid "Transition" msgstr "" -#: ../PLCGenerator.py:1518 +#: ../PLCGenerator.py:1540 #, python-format msgid "Transition \"%s\" body must contain an output variable or coil referring to its name" msgstr "" @@ -3189,48 +3236,48 @@ msgid "Transition Name:" msgstr "" -#: ../PLCGenerator.py:1609 +#: ../PLCGenerator.py:1633 #, python-brace-format msgid "Transition with content \"{a1}\" not connected to a next step in \"{a2}\" POU" msgstr "" -#: ../PLCGenerator.py:1598 +#: ../PLCGenerator.py:1622 #, python-brace-format msgid "Transition with content \"{a1}\" not connected to a previous step in \"{a2}\" POU" msgstr "" -#: ../plcopen/plcopen.py:1318 +#: ../plcopen/plcopen.py:1382 #, python-format msgid "Transition with name %s doesn't exist!" msgstr "" -#: ../PLCControler.py:96 +#: ../PLCControler.py:105 msgid "Transitions" msgstr "" -#: ../dialogs/AboutDialog.py:123 +#: ../dialogs/AboutDialog.py:127 msgid "Translated by" msgstr "" -#: ../editors/ResourceEditor.py:68 +#: ../editors/ResourceEditor.py:74 msgid "Triggering" msgstr "" -#: ../Beremiz_service.py:476 +#: ../Beremiz_service.py:487 msgid "Twisted unavailable." msgstr "" -#: ../dialogs/ActionBlockDialog.py:38 ../editors/ResourceEditor.py:83 -#: ../editors/DataTypeEditor.py:50 ../controls/VariablePanel.py:53 -#: ../controls/VariablePanel.py:54 +#: ../dialogs/ActionBlockDialog.py:40 ../editors/ResourceEditor.py:95 +#: ../editors/DataTypeEditor.py:53 ../editors/CodeFileEditor.py:748 +#: ../controls/VariablePanel.py:60 msgid "Type" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:48 +#: ../dialogs/BrowseLocationsDialog.py:54 msgid "Type and derivated" msgstr "" -#: ../canfestival/config_utils.py:336 ../canfestival/config_utils.py:624 +#: ../canfestival/config_utils.py:353 ../canfestival/config_utils.py:660 #, python-format msgid "Type conflict for location \"%s\"" msgstr "" @@ -3239,171 +3286,171 @@ msgid "Type conversion" msgstr "" -#: ../editors/DataTypeEditor.py:162 +#: ../editors/DataTypeEditor.py:170 msgid "Type infos:" msgstr "" -#: ../dialogs/BrowseLocationsDialog.py:49 +#: ../dialogs/BrowseLocationsDialog.py:55 msgid "Type strict" msgstr "" -#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:57 -#: ../dialogs/LDPowerRailDialog.py:56 ../dialogs/BrowseLocationsDialog.py:99 -#: ../dialogs/FBDBlockDialog.py:65 ../dialogs/ConnectionDialog.py:58 +#: ../dialogs/SFCDivergenceDialog.py:59 ../dialogs/SFCTransitionDialog.py:58 +#: ../dialogs/LDPowerRailDialog.py:56 ../dialogs/BrowseLocationsDialog.py:111 +#: ../dialogs/FBDBlockDialog.py:67 ../dialogs/ConnectionDialog.py:59 msgid "Type:" msgstr "" -#: ../canfestival/config_utils.py:462 ../canfestival/config_utils.py:476 +#: ../canfestival/config_utils.py:494 ../canfestival/config_utils.py:509 #, python-format msgid "Unable to define PDO mapping for node %02x" msgstr "" -#: ../targets/Xenomai/__init__.py:39 +#: ../targets/Xenomai/__init__.py:41 #, python-format msgid "Unable to get Xenomai's %s \n" msgstr "" -#: ../PLCGenerator.py:961 ../PLCGenerator.py:1214 +#: ../PLCGenerator.py:973 ../PLCGenerator.py:1228 #, python-brace-format msgid "Undefined block type \"{a1}\" in \"{a2}\" POU" msgstr "" -#: ../PLCGenerator.py:254 +#: ../PLCGenerator.py:257 #, python-format msgid "Undefined pou type \"%s\"" msgstr "" -#: ../IDEFrame.py:360 ../IDEFrame.py:421 +#: ../IDEFrame.py:374 ../IDEFrame.py:435 msgid "Undo" msgstr "" -#: ../ProjectController.py:332 +#: ../ProjectController.py:440 msgid "Unknown" msgstr "" -#: ../editors/Viewer.py:393 +#: ../editors/Viewer.py:434 #, python-format msgid "Unknown variable \"%s\" for this POU!" msgstr "" -#: ../ProjectController.py:329 ../ProjectController.py:330 +#: ../ProjectController.py:437 ../ProjectController.py:438 msgid "Unnamed" msgstr "" -#: ../PLCControler.py:636 +#: ../PLCControler.py:667 #, python-format msgid "Unnamed%d" msgstr "" -#: ../controls/VariablePanel.py:284 +#: ../controls/VariablePanel.py:308 #, python-format msgid "Unrecognized data size \"%s\"" msgstr "" -#: ../editors/DataTypeEditor.py:632 ../controls/VariablePanel.py:798 +#: ../editors/DataTypeEditor.py:645 ../controls/VariablePanel.py:862 msgid "User Data Types" msgstr "" -#: ../canfestival/SlaveEditor.py:65 ../canfestival/NetworkEditor.py:86 +#: ../canfestival/SlaveEditor.py:68 ../canfestival/NetworkEditor.py:93 msgid "User Type" msgstr "" -#: ../PLCControler.py:95 +#: ../PLCControler.py:104 msgid "User-defined POUs" msgstr "" -#: ../dialogs/ActionBlockDialog.py:38 +#: ../dialogs/ActionBlockDialog.py:40 msgid "Value" msgstr "" -#: ../editors/DataTypeEditor.py:259 +#: ../editors/DataTypeEditor.py:268 msgid "Values:" msgstr "" -#: ../dialogs/ActionBlockDialog.py:42 ../editors/Viewer.py:526 -#: ../editors/Viewer.py:2374 +#: ../dialogs/ActionBlockDialog.py:45 ../editors/Viewer.py:625 +#: ../editors/Viewer.py:2466 msgid "Variable" msgstr "" -#: ../editors/Viewer.py:308 ../editors/Viewer.py:338 ../editors/Viewer.py:360 -#: ../editors/TextViewer.py:292 ../editors/TextViewer.py:343 -#: ../editors/TextViewer.py:366 ../controls/VariablePanel.py:329 +#: ../editors/Viewer.py:349 ../editors/Viewer.py:379 ../editors/Viewer.py:401 +#: ../editors/TextViewer.py:295 ../editors/TextViewer.py:349 +#: ../editors/TextViewer.py:372 ../controls/VariablePanel.py:355 msgid "Variable Drop" msgstr "" -#: ../dialogs/FBDVariableDialog.py:63 +#: ../dialogs/FBDVariableDialog.py:67 msgid "Variable Properties" msgstr "" -#: ../editors/Viewer.py:288 ../editors/TextViewer.py:306 -#: ../controls/LocationCellEditor.py:97 ../controls/VariablePanel.py:290 -#: ../controls/VariablePanel.py:350 +#: ../editors/Viewer.py:329 ../editors/TextViewer.py:311 +#: ../controls/LocationCellEditor.py:100 ../controls/VariablePanel.py:316 +#: ../controls/VariablePanel.py:379 msgid "Variable class" msgstr "" -#: ../editors/Viewer.py:395 ../editors/TextViewer.py:387 +#: ../editors/Viewer.py:436 ../editors/TextViewer.py:393 msgid "Variable don't belong to this POU!" msgstr "" -#: ../dialogs/LDElementDialog.py:89 +#: ../dialogs/LDElementDialog.py:90 msgid "Variable:" msgstr "" -#: ../controls/VariablePanel.py:72 +#: ../controls/VariablePanel.py:89 msgid "Variables" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:151 +#: ../controls/ProjectPropertiesPanel.py:156 msgid "Vertical:" msgstr "" -#: ../Beremiz_service.py:586 +#: ../Beremiz_service.py:605 msgid "WAMP client startup failed. " msgstr "" -#: ../connectors/WAMP/__init__.py:91 +#: ../connectors/WAMP/__init__.py:97 #, python-format msgid "WAMP connecting to URL : %s\n" msgstr "" -#: ../connectors/WAMP/__init__.py:131 +#: ../connectors/WAMP/__init__.py:139 msgid "WAMP connection timeout" msgstr "" -#: ../connectors/WAMP/__init__.py:150 +#: ../connectors/WAMP/__init__.py:158 #, python-format msgid "WAMP connection to '%s' failed.\n" msgstr "" -#: ../Beremiz_service.py:562 +#: ../Beremiz_service.py:581 msgid "WAMP import failed :" msgstr "" -#: ../wxglade_hmi/wxglade_hmi.py:35 +#: ../wxglade_hmi/wxglade_hmi.py:41 msgid "WXGLADE GUI" msgstr "" -#: ../dialogs/PouDialog.py:128 ../editors/LDViewer.py:891 +#: ../dialogs/PouDialog.py:135 ../editors/LDViewer.py:900 msgid "Warning" msgstr "" -#: ../ProjectController.py:616 +#: ../ProjectController.py:724 msgid "Warnings in ST/IL/SFC code generator :\n" msgstr "" -#: ../dialogs/SearchInProjectDialog.py:78 +#: ../dialogs/SearchInProjectDialog.py:81 msgid "Whole Project" msgstr "" -#: ../controls/ProjectPropertiesPanel.py:119 +#: ../controls/ProjectPropertiesPanel.py:124 msgid "Width:" msgstr "" -#: ../dialogs/FindInPouDialog.py:93 +#: ../dialogs/FindInPouDialog.py:92 msgid "Wrap search" msgstr "" -#: ../dialogs/AboutDialog.py:122 +#: ../dialogs/AboutDialog.py:126 msgid "Written by" msgstr "" @@ -3411,95 +3458,87 @@ msgid "WxGlade GUI" msgstr "" -#: ../svgui/svgui.py:139 +#: ../svgui/svgui.py:149 msgid "" "You don't have write permissions.\n" "Open Inkscape anyway ?" msgstr "" -#: ../wxglade_hmi/wxglade_hmi.py:137 +#: ../wxglade_hmi/wxglade_hmi.py:158 msgid "" "You don't have write permissions.\n" "Open wxGlade anyway ?" msgstr "" -#: ../ProjectController.py:292 +#: ../ProjectController.py:388 msgid "" "You must have permission to work on the project\n" "Work on a project copy ?" msgstr "" -#: ../editors/LDViewer.py:886 +#: ../editors/LDViewer.py:895 msgid "You must select the block or group of blocks around which a branch should be added!" msgstr "" -#: ../editors/LDViewer.py:666 +#: ../editors/LDViewer.py:675 msgid "You must select the wire where a contact should be added!" msgstr "" -#: ../dialogs/SFCStepNameDialog.py:48 ../dialogs/PouNameDialog.py:46 +#: ../dialogs/SFCStepNameDialog.py:49 ../dialogs/PouNameDialog.py:47 msgid "You must type a name!" msgstr "" -#: ../dialogs/ForceVariableDialog.py:175 +#: ../dialogs/ForceVariableDialog.py:207 msgid "You must type a value!" msgstr "" -#: ../IDEFrame.py:438 +#: ../IDEFrame.py:452 msgid "Zoom" msgstr "" -#: ../dialogs/DurationEditorDialog.py:151 +#: ../dialogs/DurationEditorDialog.py:156 msgid "days" msgstr "" -#: ../PLCOpenEditor.py:333 +#: ../PLCOpenEditor.py:364 #, python-format msgid "error: %s\n" msgstr "" -#: ../util/ProcessLogger.py:169 +#: ../util/ProcessLogger.py:174 #, python-brace-format msgid "exited with status {a1} (pid {a2})\n" msgstr "" -#: ../PLCOpenEditor.py:396 ../PLCOpenEditor.py:398 -msgid "file : " -msgstr "" - -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:34 msgid "function" msgstr "" -#: ../PLCOpenEditor.py:399 -msgid "function : " -msgstr "" - -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:34 msgid "functionBlock" msgstr "" -#: ../dialogs/DurationEditorDialog.py:151 +#: ../dialogs/DurationEditorDialog.py:156 msgid "hours" msgstr "" -#: ../PLCOpenEditor.py:399 -msgid "line : " -msgstr "" - -#: ../dialogs/DurationEditorDialog.py:153 +#: ../ProjectController.py:751 +msgid "matiec installation is not found\n" +msgstr "" + +#: ../dialogs/DurationEditorDialog.py:158 msgid "milliseconds" msgstr "" -#: ../dialogs/DurationEditorDialog.py:152 +#: ../dialogs/DurationEditorDialog.py:157 msgid "minutes" msgstr "" -#: ../dialogs/PouDialog.py:31 +#: ../dialogs/PouDialog.py:34 msgid "program" msgstr "" -#: ../dialogs/DurationEditorDialog.py:152 +#: ../dialogs/DurationEditorDialog.py:157 msgid "seconds" msgstr "" @@ -3515,24 +3554,24 @@ msgid "string right of" msgstr "" -#: ../Beremiz.py:126 +#: ../Beremiz.py:164 msgid "update info unavailable." msgstr "" -#: ../PLCOpenEditor.py:331 +#: ../PLCOpenEditor.py:362 #, python-format msgid "warning: %s\n" msgstr "" -#: ../PLCControler.py:970 +#: ../PLCControler.py:1028 #, python-brace-format msgid "{a1} \"{a2}\" can't be pasted as a {a3}." msgstr "" -#: ../ConfigTreeNode.py:56 +#: ../ConfigTreeNode.py:58 #, python-brace-format msgid "" -"{a1} XML file doesn't follow XSD schema at line %{a2}:\n" +"{a1} XML file doesn't follow XSD schema at line {a2}:\n" "{a3}" msgstr "" @@ -3593,9 +3632,6 @@ msgid "LDFLAGS" msgstr "" -msgid "PLC" -msgstr "" - msgid "Linux" msgstr "" @@ -3767,7 +3803,7 @@ msgid "The RS bistable is a latch where the Reset dominates." msgstr "" -msgid "The semaphore provides a mechanism to allow software elements mutually exclusive access to certain ressources." +msgid "The semaphore provides a mechanism to allow software elements mutually exclusive access to certain resources." msgstr "" msgid "The output produces a single pulse when a rising edge is detected." diff -r c1298e7ffe3a -r 8391c11477f4 i18n/mki18n.py --- a/i18n/mki18n.py Fri Mar 24 12:07:47 2017 +0000 +++ b/i18n/mki18n.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,498 +1,502 @@ -#! /usr/bin/env python -# -*- coding: iso-8859-1 -*- -# -# PYTHON MODULE: MKI18N.PY -# ========= -# -# Abstract: Make Internationalization (i18n) files for an application. -# -# Copyright Pierre Rouleau. 2003. Released to public domain. -# -# Last update: Saturday, November 8, 2003. @ 15:55:18. -# -# File: ROUP2003N01::C:/dev/python/mki18n.py -# -# RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $ -# -# Update history: -# -# - File created: Saturday, June 7, 2003. by Pierre Rouleau -# - 10/06/03 rcs : RCS Revision 1.1 2003/06/10 10:06:12 PRouleau -# - 10/06/03 rcs : RCS Initial revision -# - 23/08/03 rcs : RCS Revision 1.2 2003/06/10 10:54:27 PRouleau -# - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format. Added the encoding -# notification to Python to comply with Python's 2.3 PEP 263. -# - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file. -# - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient. -# - 05/11/03 rcs : RCS Revision 1.4 2003/10/22 06:39:31 PRouleau -# - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file. -# - 08/11/03 rcs : RCS Revision 1.5 2003/11/05 19:40:04 PRouleau -# -# RCS $Log: $ -# -# -# ----------------------------------------------------------------------------- -""" -mki18n allows you to internationalize your software. You can use it to -create the GNU .po files (Portable Object) and the compiled .mo files -(Machine Object). - -mki18n module can be used from the command line or from within a script (see -the Usage at the end of this page). - - Table of Contents - ----------------- - - makePO() -- Build the Portable Object file for the application -- - catPO() -- Concatenate one or several PO files with the application domain files. -- - makeMO() -- Compile the Portable Object files into the Machine Object stored in the right location. -- - printUsage -- Displays how to use this script from the command line -- - - Scriptexecution -- Runs when invoked from the command line -- - - -NOTE: this module uses GNU gettext utilities. - -You can get the gettext tools from the following sites: - - - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available. - Note that you need to use `GNU libiconv`_ to use this. Get it from the `GNU - libiconv ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP - files and install the packages inside c:/gnu. All binaries will be stored - inside c:/gnu/bin. Just put c:/gnu/bin inside your PATH. You will need - the following files: - - - `gettext-runtime-0.12.1.bin.woe32.zip`_ - - `gettext-tools-0.12.1.bin.woe32.zip`_ - - `libiconv-1.9.1.bin.woe32.zip`_ - - -.. _GNU libiconv: http://www.gnu.org/software/libiconv/ -.. _GNU libiconv ftp site: http://www.ibiblio.org/pub/gnu/libiconv/ -.. _gettext-runtime-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip -.. _gettext-tools-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip -.. _libiconv-1.9.1.bin.woe32.zip: http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip - -""" -# ----------------------------------------------------------------------------- -# Module Import -# ------------- -# -import os -import sys -import wx -import re - -# ----------------------------------------------------------------------------- -# Global variables -# ---------------- -# - -__author__ = "Pierre Rouleau" -__version__= "$Revision: 1.5 $" - -# ----------------------------------------------------------------------------- - -def getlanguageDict(): - languageDict = {} - - if wx.VERSION >= (3, 0, 0): - app = wx.App() - else: - app = wx.PySimpleApp() - - for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]: - i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang)) - if i: - languageDict[i.CanonicalName] = i.Description - - return languageDict - - - -def processCustomFiles(filein, fileout, regexp, prefix = ''): - appfil_file = open(filein, 'r') - messages_file = open(fileout, 'r') - messages = messages_file.read() - messages_file.close() - messages_file = open(fileout, 'a') - messages_file.write('\n') - messages_file.write('#: %s\n' % prefix) - messages_file.write('\n') - - words_found = {} - for filepath in appfil_file.xreadlines(): - code_file = open(filepath.strip(), 'r') - for match in regexp.finditer(code_file.read()): - word = match.group(1) - if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1: - words_found[word] = True - messages_file.write('\n') - messages_file.write("msgid \"%s\"\n"%word) - messages_file.write("msgstr \"\"\n") - code_file.close() - - messages_file.close() - appfil_file.close() - - -# ----------------------------------------------------------------------------- -# m a k e P O ( ) -- Build the Portable Object file for the application -- -# ^^^^^^^^^^^^^^^ -# -def makePO(applicationDirectoryPath, applicationDomain=None, verbose=0) : - """Build the Portable Object Template file for the application. - - makePO builds the .pot file for the application stored inside - a specified directory by running xgettext for all application source - files. It finds the name of all files by looking for a file called 'app.fil'. - If this file does not exists, makePo raises an IOError exception. - By default the application domain (the application - name) is the same as the directory name but it can be overridden by the - 'applicationDomain' argument. - - makePO always creates a new file called messages.pot. If it finds files - of the form app_xx.po where 'app' is the application name and 'xx' is one - of the ISO 639 two-letter language codes, makePO resynchronizes those - files with the latest extracted strings (now contained in messages.pot). - This process updates all line location number in the language-specific - .po files and may also create new entries for translation (or comment out - some). The .po file is not changed, instead a new file is created with - the .new extension appended to the name of the .po file. - - By default the function does not display what it is doing. Set the - verbose argument to 1 to force it to print its commands. - """ - - if applicationDomain is None: - applicationName = fileBaseOf(applicationDirectoryPath,withPath=0) - else: - applicationName = applicationDomain - currentDir = os.getcwd() - os.chdir(applicationDirectoryPath) - filelist = 'app.fil' - if not os.path.exists(filelist): - raise IOError(2,'No module file: ' % filelist) - - fileout = 'messages.pot' - # Steps: - # Use xgettext to parse all application modules - # The following switches are used: - # - # -s : sort output by string content (easier to use when we need to merge several .po files) - # --files-from=app.fil : The list of files is taken from the file: app.fil - # --output= : specifies the name of the output file (using a .pot extension) - cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout - if verbose: print cmd - os.system(cmd) - - XSD_STRING_MODEL = re.compile("]*\>") - processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings') - - XML_TC6_STRING_MODEL = re.compile("\s*\s*", re.MULTILINE | re.DOTALL) - processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings') - - languageDict = getlanguageDict() - - for langCode in languageDict.keys(): - if langCode == 'en': - pass - else: - langPOfileName = "%s_%s.po" % (applicationName , langCode) - if os.path.exists(langPOfileName): - cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName) - if verbose: print cmd - os.system(cmd) - os.chdir(currentDir) - -# ----------------------------------------------------------------------------- -# c a t P O ( ) -- Concatenate one or several PO files with the application domain files. -- -# ^^^^^^^^^^^^^ -# -def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0) : - """Concatenate one or several PO files with the application domain files. - """ - - if applicationDomain is None: - applicationName = fileBaseOf(applicationDirectoryPath,withPath=0) - else: - applicationName = applicationDomain - currentDir = os.getcwd() - os.chdir(applicationDirectoryPath) - - languageDict = getlanguageDict() - - for langCode in languageDict.keys(): - if langCode == 'en': - pass - else: - langPOfileName = "%s_%s.po" % (applicationName , langCode) - if os.path.exists(langPOfileName): - fileList = '' - for fileName in listOf_extraPo: - fileList += ("%s_%s.po " % (fileName,langCode)) - cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName) - if verbose: print cmd - os.system(cmd) - if targetDir is None: - pass - else: - mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode) - cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir,applicationName,applicationName,langCode) - if verbose: print cmd - os.system(cmd) - os.chdir(currentDir) - -# ----------------------------------------------------------------------------- -# m a k e M O ( ) -- Compile the Portable Object files into the Machine Object stored in the right location. -- -# ^^^^^^^^^^^^^^^ -# -def makeMO(applicationDirectoryPath,targetDir='./locale',applicationDomain=None, verbose=0, forceEnglish=0) : - """Compile the Portable Object files into the Machine Object stored in the right location. - - makeMO converts all translated language-specific PO files located inside - the application directory into the binary .MO files stored inside the - LC_MESSAGES sub-directory for the found locale files. - - makeMO searches for all files that have a name of the form 'app_xx.po' - inside the application directory specified by the first argument. The - 'app' is the application domain name (that can be specified by the - applicationDomain argument or is taken from the directory name). The 'xx' - corresponds to one of the ISO 639 two-letter language codes. - - makeMo stores the resulting files inside a sub-directory of `targetDir` - called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language - code. - """ - if targetDir is None: - targetDir = './locale' - if verbose: - print "Target directory for .mo files is: %s" % targetDir - - if applicationDomain is None: - applicationName = fileBaseOf(applicationDirectoryPath,withPath=0) - else: - applicationName = applicationDomain - currentDir = os.getcwd() - os.chdir(applicationDirectoryPath) - - languageDict = getlanguageDict() - - for langCode in languageDict.keys(): - if (langCode == 'en') and (forceEnglish==0): - pass - else: - langPOfileName = "%s_%s.po" % (applicationName , langCode) - if os.path.exists(langPOfileName): - mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir,langCode) - if not os.path.exists(mo_targetDir): - mkdir(mo_targetDir) - cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir,applicationName,applicationName,langCode) - if verbose: print cmd - os.system(cmd) - os.chdir(currentDir) - -# ----------------------------------------------------------------------------- -# p r i n t U s a g e -- Displays how to use this script from the command line -- -# ^^^^^^^^^^^^^^^^^^^ -# -def printUsage(errorMsg=None) : - """Displays how to use this script from the command line.""" - print """ - ################################################################################## - # mki18n : Make internationalization files. # - # Uses the GNU gettext system to create PO (Portable Object) files # - # from source code, coimpile PO into MO (Machine Object) files. # - # Supports C,C++,Python source files. # - # # - # Usage: mki18n {OPTION} [appDirPath] # - # # - # Options: # - # -e : When -m is used, forces English .mo file creation # - # -h : prints this help # - # -m : make MO from existing PO files # - # -p : make PO, update PO files: Creates a new messages.pot # - # file. Creates a dom_xx.po.new for every existing # - # language specific .po file. ('xx' stands for the ISO639 # - # two-letter language code and 'dom' stands for the # - # application domain name). mki18n requires that you # - # write a 'app.fil' file which contains the list of all # - # source code to parse. # - # -v : verbose (prints comments while running) # - # --domain=appName : specifies the application domain name. By default # - # the directory name is used. # - # --moTarget=dir : specifies the directory where .mo files are stored. # - # If not specified, the target is './locale' # - # # - # You must specify one of the -p or -m option to perform the work. You can # - # specify the path of the target application. If you leave it out mki18n # - # will use the current directory as the application main directory. # - # # - ##################################################################################""" - if errorMsg: - print "\n ERROR: %s" % errorMsg - -# ----------------------------------------------------------------------------- -# f i l e B a s e O f ( ) -- Return base name of filename -- -# ^^^^^^^^^^^^^^^^^^^^^^^ -# -def fileBaseOf(filename,withPath=0) : - """fileBaseOf(filename,withPath) ---> string - - Return base name of filename. The returned string never includes the extension. - Use os.path.basename() to return the basename with the extension. The - second argument is optional. If specified and if set to 'true' (non zero) - the string returned contains the full path of the file name. Otherwise the - path is excluded. - - [Example] - >>> fn = 'd:/dev/telepath/tvapp/code/test.html' - >>> fileBaseOf(fn) - 'test' - >>> fileBaseOf(fn) - 'test' - >>> fileBaseOf(fn,1) - 'd:/dev/telepath/tvapp/code/test' - >>> fileBaseOf(fn,0) - 'test' - >>> fn = 'abcdef' - >>> fileBaseOf(fn) - 'abcdef' - >>> fileBaseOf(fn,1) - 'abcdef' - >>> fn = "abcdef." - >>> fileBaseOf(fn) - 'abcdef' - >>> fileBaseOf(fn,1) - 'abcdef' - """ - pos = filename.rfind('.') - if pos > 0: - filename = filename[:pos] - if withPath: - return filename - else: - return os.path.basename(filename) -# ----------------------------------------------------------------------------- -# m k d i r ( ) -- Create a directory (and possibly the entire tree) -- -# ^^^^^^^^^^^^^ -# -def mkdir(directory) : - """Create a directory (and possibly the entire tree). - - The os.mkdir() will fail to create a directory if one of the - directory in the specified path does not exist. mkdir() - solves this problem. It creates every intermediate directory - required to create the final path. Under Unix, the function - only supports forward slash separator, but under Windows and MacOS - the function supports the forward slash and the OS separator (backslash - under windows). - """ - - # translate the path separators - directory = unixpath(directory) - # build a list of all directory elements - aList = filter(lambda x: len(x)>0, directory.split('/')) - theLen = len(aList) - # if the first element is a Windows-style disk drive - # concatenate it with the first directory - if aList[0].endswith(':'): - if theLen > 1: - aList[1] = aList[0] + '/' + aList[1] - del aList[0] - theLen -= 1 - # if the original directory starts at root, - # make sure the first element of the list - # starts at root too - if directory[0] == '/': - aList[0] = '/' + aList[0] - # Now iterate through the list, check if the - # directory exists and if not create it - theDir = '' - for i in range(theLen): - theDir += aList[i] - if not os.path.exists(theDir): - os.mkdir(theDir) - theDir += '/' - -# ----------------------------------------------------------------------------- -# u n i x p a t h ( ) -- Return a path name that contains Unix separator. -- -# ^^^^^^^^^^^^^^^^^^^ -# -def unixpath(thePath) : - r"""Return a path name that contains Unix separator. - - [Example] - >>> unixpath(r"d:\test") - 'd:/test' - >>> unixpath("d:/test/file.txt") - 'd:/test/file.txt' - >>> - """ - thePath = os.path.normpath(thePath) - if os.sep == '/': - return thePath - else: - return thePath.replace(os.sep,'/') - -# ----------------------------------------------------------------------------- - -# S c r i p t e x e c u t i o n -- Runs when invoked from the command line -- -# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -# -if __name__ == "__main__": - import getopt # command line parsing - argc = len(sys.argv) - if argc == 1: - printUsage('Missing argument: specify at least one of -m or -p (or both).') - sys.exit(1) - # If there is some arguments, parse the command line - validOptions = "ehmpv" - validLongOptions = ['domain=', 'moTarget='] - option = {} - option['forceEnglish'] = 0 - option['mo'] = 0 - option['po'] = 0 - option['verbose'] = 0 - option['domain'] = None - option['moTarget'] = None - try: - optionList,pargs = getopt.getopt(sys.argv[1:],validOptions,validLongOptions) - except getopt.GetoptError, e: - printUsage(e[0]) - sys.exit(1) - for (opt,val) in optionList: - if (opt == '-h'): - printUsage() - sys.exit(0) - elif (opt == '-e'): option['forceEnglish'] = 1 - elif (opt == '-m'): option['mo'] = 1 - elif (opt == '-p'): option['po'] = 1 - elif (opt == '-v'): option['verbose'] = 1 - elif (opt == '--domain'): option['domain'] = val - elif (opt == '--moTarget'): option['moTarget'] = val - if len(pargs) == 0: - appDirPath = os.getcwd() - if option['verbose']: - print "No project directory given. Using current one: %s" % appDirPath - elif len(pargs) == 1: - appDirPath = pargs[0] - else: - printUsage('Too many arguments (%u). Use double quotes if you have space in directory name' % len(pargs)) - sys.exit(1) - if option['domain'] is None: - # If no domain specified, use the name of the target directory - option['domain'] = fileBaseOf(appDirPath) - if option['verbose']: - print "Application domain used is: '%s'" % option['domain'] - if option['po']: - try: - makePO(appDirPath,option['domain'],option['verbose']) - except IOError, e: - printUsage(e[1] + '\n You must write a file app.fil that contains the list of all files to parse.') - if option['mo']: - makeMO(appDirPath,option['moTarget'],option['domain'],option['verbose'],option['forceEnglish']) - sys.exit(1) - - -# ----------------------------------------------------------------------------- +#! /usr/bin/env python +# -*- coding: iso-8859-1 -*- +# +# PYTHON MODULE: MKI18N.PY +# ========= +# +# Abstract: Make Internationalization (i18n) files for an application. +# +# Copyright Pierre Rouleau. 2003. Released to public domain. +# +# Last update: Saturday, November 8, 2003. @ 15:55:18. +# +# File: ROUP2003N01::C:/dev/python/mki18n.py +# +# RCS $Header: //software/official/MKS/MKS_SI/TV_NT/dev/Python/rcs/mki18n.py 1.5 2003/11/05 19:40:04 PRouleau Exp $ +# +# Update history: +# +# - File created: Saturday, June 7, 2003. by Pierre Rouleau +# - 10/06/03 rcs : RCS Revision 1.1 2003/06/10 10:06:12 PRouleau +# - 10/06/03 rcs : RCS Initial revision +# - 23/08/03 rcs : RCS Revision 1.2 2003/06/10 10:54:27 PRouleau +# - 23/08/03 P.R.: [code:fix] : The strings encoded in this file are encode in iso-8859-1 format. Added the encoding +# notification to Python to comply with Python's 2.3 PEP 263. +# - 23/08/03 P.R.: [feature:new] : Added the '-e' switch which is used to force the creation of the empty English .mo file. +# - 22/10/03 P.R.: [code] : incorporated utility functions in here to make script self sufficient. +# - 05/11/03 rcs : RCS Revision 1.4 2003/10/22 06:39:31 PRouleau +# - 05/11/03 P.R.: [code:fix] : included the unixpath() in this file. +# - 08/11/03 rcs : RCS Revision 1.5 2003/11/05 19:40:04 PRouleau +# +# RCS $Log: $ +# +# +# ----------------------------------------------------------------------------- +""" +mki18n allows you to internationalize your software. You can use it to +create the GNU .po files (Portable Object) and the compiled .mo files +(Machine Object). + +mki18n module can be used from the command line or from within a script (see +the Usage at the end of this page). + + Table of Contents + ----------------- + + makePO() -- Build the Portable Object file for the application -- + catPO() -- Concatenate one or several PO files with the application domain files. -- + makeMO() -- Compile the Portable Object files into the Machine Object stored in the right location. -- + printUsage -- Displays how to use this script from the command line -- + + Scriptexecution -- Runs when invoked from the command line -- + + +NOTE: this module uses GNU gettext utilities. + +You can get the gettext tools from the following sites: + + - `GNU FTP site for gettetx`_ where several versions (0.10.40, 0.11.2, 0.11.5 and 0.12.1) are available. + Note that you need to use `GNU libiconv`_ to use this. Get it from the `GNU + libiconv ftp site`_ and get version 1.9.1 or later. Get the Windows .ZIP + files and install the packages inside c:/gnu. All binaries will be stored + inside c:/gnu/bin. Just put c:/gnu/bin inside your PATH. You will need + the following files: + + - `gettext-runtime-0.12.1.bin.woe32.zip`_ + - `gettext-tools-0.12.1.bin.woe32.zip`_ + - `libiconv-1.9.1.bin.woe32.zip`_ + + +.. _GNU libiconv: http://www.gnu.org/software/libiconv/ +.. _GNU libiconv ftp site: http://www.ibiblio.org/pub/gnu/libiconv/ +.. _gettext-runtime-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-runtime-0.12.1.bin.woe32.zip +.. _gettext-tools-0.12.1.bin.woe32.zip: ftp://ftp.gnu.org/gnu/gettext/gettext-tools-0.12.1.bin.woe32.zip +.. _libiconv-1.9.1.bin.woe32.zip: http://www.ibiblio.org/pub/gnu/libiconv/libiconv-1.9.1.bin.woe32.zip + +""" +# ----------------------------------------------------------------------------- +# Module Import +# ------------- +# + +from __future__ import absolute_import +from __future__ import print_function +import os +import sys +import re +import wx + +# ----------------------------------------------------------------------------- +# Global variables +# ---------------- +# + +__author__ = "Pierre Rouleau" +__version__ = "$Revision: 1.5 $" + +# ----------------------------------------------------------------------------- + + +def getlanguageDict(): + languageDict = {} + + if wx.VERSION >= (3, 0, 0): + _app = wx.App() + else: + _app = wx.PySimpleApp() + + for lang in [x for x in dir(wx) if x.startswith("LANGUAGE")]: + i = wx.Locale(wx.LANGUAGE_DEFAULT).GetLanguageInfo(getattr(wx, lang)) + if i: + languageDict[i.CanonicalName] = i.Description + + return languageDict + + +def verbosePrint(verbose, str): + if verbose: + print(str) + + +def processCustomFiles(filein, fileout, regexp, prefix=''): + appfil_file = open(filein, 'r') + messages_file = open(fileout, 'r') + messages = messages_file.read() + messages_file.close() + messages_file = open(fileout, 'a') + messages_file.write('\n') + messages_file.write('#: %s\n' % prefix) + messages_file.write('\n') + + words_found = {} + for filepath in appfil_file.xreadlines(): + code_file = open(filepath.strip(), 'r') + for match in regexp.finditer(code_file.read()): + word = match.group(1) + if not words_found.get(word, False) and messages.find("msgid \"%s\"\nmsgstr \"\"" % word) == -1: + words_found[word] = True + messages_file.write('\n') + messages_file.write("msgid \"%s\"\n" % word) + messages_file.write("msgstr \"\"\n") + code_file.close() + + messages_file.close() + appfil_file.close() + + +# ----------------------------------------------------------------------------- +# m a k e P O ( ) -- Build the Portable Object file for the application -- +# ^^^^^^^^^^^^^^^ +# +def makePO(applicationDirectoryPath, applicationDomain=None, verbose=0): + """Build the Portable Object Template file for the application. + + makePO builds the .pot file for the application stored inside + a specified directory by running xgettext for all application source + files. It finds the name of all files by looking for a file called 'app.fil'. + If this file does not exists, makePo raises an IOError exception. + By default the application domain (the application + name) is the same as the directory name but it can be overridden by the + 'applicationDomain' argument. + + makePO always creates a new file called messages.pot. If it finds files + of the form app_xx.po where 'app' is the application name and 'xx' is one + of the ISO 639 two-letter language codes, makePO resynchronizes those + files with the latest extracted strings (now contained in messages.pot). + This process updates all line location number in the language-specific + .po files and may also create new entries for translation (or comment out + some). The .po file is not changed, instead a new file is created with + the .new extension appended to the name of the .po file. + + By default the function does not display what it is doing. Set the + verbose argument to 1 to force it to print its commands. + """ + + if applicationDomain is None: + applicationName = fileBaseOf(applicationDirectoryPath, withPath=0) + else: + applicationName = applicationDomain + currentDir = os.getcwd() + os.chdir(applicationDirectoryPath) + filelist = 'app.fil' + if not os.path.exists(filelist): + raise IOError(2, 'No module file: ' % filelist) + + fileout = 'messages.pot' + # Steps: + # Use xgettext to parse all application modules + # The following switches are used: + # + # -s : sort output by string content (easier to use when we need to merge several .po files) + # --files-from=app.fil : The list of files is taken from the file: app.fil + # --output= : specifies the name of the output file (using a .pot extension) + cmd = 'xgettext -s --no-wrap --language=Python --files-from=' + filelist + ' --output=' + fileout + ' --package-name ' + applicationName + verbosePrint(verbose, cmd) + os.system(cmd) + + XSD_STRING_MODEL = re.compile("]*\>") + processCustomFiles(filelist, fileout, XSD_STRING_MODEL, 'Extra XSD strings') + + XML_TC6_STRING_MODEL = re.compile("\s*\s*", re.MULTILINE | re.DOTALL) + processCustomFiles(filelist, fileout, XML_TC6_STRING_MODEL, 'Extra TC6 documentation strings') + + # generate messages.po + cmd = 'msginit --no-wrap --no-translator -i %s -l en_US.UTF-8 -o messages.po' % (fileout) + verbosePrint(verbose, cmd) + os.system(cmd) + + languageDict = getlanguageDict() + + for langCode in languageDict.keys(): + if langCode == 'en': + pass + else: + langPOfileName = "%s_%s.po" % (applicationName, langCode) + if os.path.exists(langPOfileName): + cmd = 'msgmerge -s --no-wrap "%s" %s > "%s.new"' % (langPOfileName, fileout, langPOfileName) + verbosePrint(verbose, cmd) + os.system(cmd) + os.chdir(currentDir) + + +def catPO(applicationDirectoryPath, listOf_extraPo, applicationDomain=None, targetDir=None, verbose=0): + """Concatenate one or several PO files with the application domain files. + """ + + if applicationDomain is None: + applicationName = fileBaseOf(applicationDirectoryPath, withPath=0) + else: + applicationName = applicationDomain + currentDir = os.getcwd() + os.chdir(applicationDirectoryPath) + + languageDict = getlanguageDict() + + for langCode in languageDict.keys(): + if langCode == 'en': + pass + else: + langPOfileName = "%s_%s.po" % (applicationName, langCode) + if os.path.exists(langPOfileName): + fileList = '' + for fileName in listOf_extraPo: + fileList += ("%s_%s.po " % (fileName, langCode)) + cmd = "msgcat -s --no-wrap %s %s > %s.cat" % (langPOfileName, fileList, langPOfileName) + verbosePrint(verbose, cmd) + os.system(cmd) + if targetDir is None: + pass + else: + mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir, langCode) + cmd = "msgfmt --output-file=%s/%s.mo %s_%s.po.cat" % (mo_targetDir, applicationName, applicationName, langCode) + verbosePrint(verbose, cmd) + os.system(cmd) + os.chdir(currentDir) + + +def makeMO(applicationDirectoryPath, targetDir='./locale', applicationDomain=None, verbose=0, forceEnglish=0): + """Compile the Portable Object files into the Machine Object stored in the right location. + + makeMO converts all translated language-specific PO files located inside + the application directory into the binary .MO files stored inside the + LC_MESSAGES sub-directory for the found locale files. + + makeMO searches for all files that have a name of the form 'app_xx.po' + inside the application directory specified by the first argument. The + 'app' is the application domain name (that can be specified by the + applicationDomain argument or is taken from the directory name). The 'xx' + corresponds to one of the ISO 639 two-letter language codes. + + makeMo stores the resulting files inside a sub-directory of `targetDir` + called xx/LC_MESSAGES where 'xx' corresponds to the 2-letter language + code. + """ + + if targetDir is None: + targetDir = './locale' + + verbosePrint(verbose, "Target directory for .mo files is: %s" % targetDir) + + if applicationDomain is None: + applicationName = fileBaseOf(applicationDirectoryPath, withPath=0) + else: + applicationName = applicationDomain + currentDir = os.getcwd() + os.chdir(applicationDirectoryPath) + + languageDict = getlanguageDict() + + for langCode in languageDict.keys(): + if (langCode == 'en') and (forceEnglish == 0): + pass + else: + langPOfileName = "%s_%s.po" % (applicationName, langCode) + if os.path.exists(langPOfileName): + mo_targetDir = "%s/%s/LC_MESSAGES" % (targetDir, langCode) + if not os.path.exists(mo_targetDir): + mkdir(mo_targetDir) + cmd = 'msgfmt --output-file="%s/%s.mo" "%s_%s.po"' % (mo_targetDir, applicationName, applicationName, langCode) + verbosePrint(verbose, cmd) + os.system(cmd) + os.chdir(currentDir) + + +def printUsage(errorMsg=None): + """Displays how to use this script from the command line.""" + print(""" + ################################################################################## + # mki18n : Make internationalization files. # + # Uses the GNU gettext system to create PO (Portable Object) files # + # from source code, coimpile PO into MO (Machine Object) files. # + # Supports C,C++,Python source files. # + # # + # Usage: mki18n {OPTION} [appDirPath] # + # # + # Options: # + # -e : When -m is used, forces English .mo file creation # + # -h : prints this help # + # -m : make MO from existing PO files # + # -p : make PO, update PO files: Creates a new messages.pot # + # file. Creates a dom_xx.po.new for every existing # + # language specific .po file. ('xx' stands for the ISO639 # + # two-letter language code and 'dom' stands for the # + # application domain name). mki18n requires that you # + # write a 'app.fil' file which contains the list of all # + # source code to parse. # + # -v : verbose (prints comments while running) # + # --domain=appName : specifies the application domain name. By default # + # the directory name is used. # + # --moTarget=dir : specifies the directory where .mo files are stored. # + # If not specified, the target is './locale' # + # # + # You must specify one of the -p or -m option to perform the work. You can # + # specify the path of the target application. If you leave it out mki18n # + # will use the current directory as the application main directory. # + # # + ##################################################################################""") + if errorMsg: + print("\n ERROR: %s" % errorMsg) + + +def fileBaseOf(filename, withPath=0): + """fileBaseOf(filename,withPath) ---> string + + Return base name of filename. The returned string never includes the extension. + Use os.path.basename() to return the basename with the extension. The + second argument is optional. If specified and if set to 'true' (non zero) + the string returned contains the full path of the file name. Otherwise the + path is excluded. + + [Example] + >>> fn = 'd:/dev/telepath/tvapp/code/test.html' + >>> fileBaseOf(fn) + 'test' + >>> fileBaseOf(fn) + 'test' + >>> fileBaseOf(fn,1) + 'd:/dev/telepath/tvapp/code/test' + >>> fileBaseOf(fn,0) + 'test' + >>> fn = 'abcdef' + >>> fileBaseOf(fn) + 'abcdef' + >>> fileBaseOf(fn,1) + 'abcdef' + >>> fn = "abcdef." + >>> fileBaseOf(fn) + 'abcdef' + >>> fileBaseOf(fn,1) + 'abcdef' + """ + pos = filename.rfind('.') + if pos > 0: + filename = filename[:pos] + if withPath: + return filename + else: + return os.path.basename(filename) + + +def mkdir(directory): + """Create a directory (and possibly the entire tree). + + The os.mkdir() will fail to create a directory if one of the + directory in the specified path does not exist. mkdir() + solves this problem. It creates every intermediate directory + required to create the final path. Under Unix, the function + only supports forward slash separator, but under Windows and MacOS + the function supports the forward slash and the OS separator (backslash + under windows). + """ + + # translate the path separators + directory = unixpath(directory) + # build a list of all directory elements + aList = filter(lambda x: len(x) > 0, directory.split('/')) + theLen = len(aList) + # if the first element is a Windows-style disk drive + # concatenate it with the first directory + if aList[0].endswith(':'): + if theLen > 1: + aList[1] = aList[0] + '/' + aList[1] + del aList[0] + theLen -= 1 + # if the original directory starts at root, + # make sure the first element of the list + # starts at root too + if directory[0] == '/': + aList[0] = '/' + aList[0] + # Now iterate through the list, check if the + # directory exists and if not create it + theDir = '' + for i in range(theLen): + theDir += aList[i] + if not os.path.exists(theDir): + os.mkdir(theDir) + theDir += '/' + + +def unixpath(thePath): + r"""Return a path name that contains Unix separator. + + [Example] + >>> unixpath(r"d:\test") + 'd:/test' + >>> unixpath("d:/test/file.txt") + 'd:/test/file.txt' + >>> + """ + thePath = os.path.normpath(thePath) + if os.sep == '/': + return thePath + else: + return thePath.replace(os.sep, '/') + + +# ----------------------------------------------------------------------------- + +# S c r i p t e x e c u t i o n -- Runs when invoked from the command line -- +# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# +if __name__ == "__main__": + import getopt # command line parsing + argc = len(sys.argv) + if argc == 1: + printUsage('Missing argument: specify at least one of -m or -p (or both).') + sys.exit(1) + # If there is some arguments, parse the command line + validOptions = "ehmpv" + validLongOptions = ['domain=', 'moTarget='] + option = {} + option['forceEnglish'] = 0 + option['mo'] = 0 + option['po'] = 0 + option['verbose'] = 0 + option['domain'] = None + option['moTarget'] = None + optionKey = { + '-e': 'forceEnglish', + '-m': 'mo', + '-p': 'po', + '-v': 'verbose', + '--domain': 'domain', + '--moTarget': 'moTarget', + } + exit_code = 1 + try: + optionList, pargs = getopt.getopt(sys.argv[1:], validOptions, validLongOptions) + except getopt.GetoptError, e: + printUsage(e[0]) + sys.exit(1) + for (opt, val) in optionList: + if opt == '-h': + printUsage() + sys.exit(0) + option[optionKey[opt]] = 1 if val == '' else val + if len(pargs) == 0: + appDirPath = os.getcwd() + if option['verbose']: + print("No project directory given. Using current one: %s" % appDirPath) + elif len(pargs) == 1: + appDirPath = pargs[0] + else: + printUsage('Too many arguments (%u). Use double quotes if you have space in directory name' % len(pargs)) + sys.exit(1) + if option['domain'] is None: + # If no domain specified, use the name of the target directory + option['domain'] = fileBaseOf(appDirPath) + if option['verbose']: + print("Application domain used is: '%s'" % option['domain']) + if option['po']: + try: + makePO(appDirPath, option['domain'], option['verbose']) + exit_code = 0 + except IOError, e: + printUsage(e[1] + '\n You must write a file app.fil that contains the list of all files to parse.') + if option['mo']: + makeMO(appDirPath, option['moTarget'], option['domain'], option['verbose'], option['forceEnglish']) + exit_code = 0 + sys.exit(exit_code) + + +# ----------------------------------------------------------------------------- diff -r c1298e7ffe3a -r 8391c11477f4 images/Build.png Binary file images/Build.png has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/de_DE/LC_MESSAGES/Beremiz.mo Binary file locale/de_DE/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/es_ES/LC_MESSAGES/Beremiz.mo Binary file locale/es_ES/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/fr_FR/LC_MESSAGES/Beremiz.mo Binary file locale/fr_FR/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/hu_HU/LC_MESSAGES/Beremiz.mo Binary file locale/hu_HU/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/it_IT/LC_MESSAGES/Beremiz.mo Binary file locale/it_IT/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/ko_KR/LC_MESSAGES/Beremiz.mo Binary file locale/ko_KR/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/pt_BR/LC_MESSAGES/Beremiz.mo Binary file locale/pt_BR/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/pt_PT/LC_MESSAGES/Beremiz.mo Binary file locale/pt_PT/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/ru_RU/LC_MESSAGES/Beremiz.mo Binary file locale/ru_RU/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/sl_SI/LC_MESSAGES/Beremiz.mo Binary file locale/sl_SI/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 locale/zh_CN/LC_MESSAGES/Beremiz.mo Binary file locale/zh_CN/LC_MESSAGES/Beremiz.mo has changed diff -r c1298e7ffe3a -r 8391c11477f4 modbus/mb_runtime.c --- a/modbus/mb_runtime.c Fri Mar 24 12:07:47 2017 +0000 +++ b/modbus/mb_runtime.c Tue Jan 30 16:06:58 2018 +0100 @@ -1,615 +1,615 @@ -/* File generated by Beremiz (PlugGenerate_C method of Modbus plugin) */ - -/* - * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt) - * - * This file is part of the Modbus library for Beremiz and matiec. - * - * This Modbus library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this Modbus library. If not, see . - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - - -#include -#include /* required for memcpy() */ -#include "mb_slave_and_master.h" -#include "MB_%(locstr)s.h" - - -#define MAX_MODBUS_ERROR_CODE 11 -static const char *modbus_error_messages[MAX_MODBUS_ERROR_CODE+1] = { - /* 0 */ "", /* un-used -> no error! */ - /* 1 */ "illegal/unsuported function", - /* 2 */ "illegal data address", - /* 3 */ "illegal data value", - /* 4 */ "slave device failure", - /* 5 */ "acknowledge -> slave intends to reply later", - /* 6 */ "slave device busy", - /* 7 */ "negative acknowledge", - /* 8 */ "memory parity error", - /* 9 */ "", /* undefined by Modbus */ - /* 10*/ "gateway path unavalilable", - /* 11*/ "gateway target device failed to respond" -}; - - -/* Execute a modbus client transaction/request */ -static int __execute_mb_request(int request_id){ - switch (client_requests[request_id].mb_function){ - - case 1: /* read coils */ - return read_output_bits(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].count, - client_requests[request_id].coms_buffer, - (int) client_requests[request_id].count, - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 2: /* read discrete inputs */ - return read_input_bits( client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].count, - client_requests[request_id].coms_buffer, - (int) client_requests[request_id].count, - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 3: /* read holding registers */ - return read_output_words(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].count, - client_requests[request_id].coms_buffer, - (int) client_requests[request_id].count, - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 4: /* read input registers */ - return read_input_words(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].count, - client_requests[request_id].coms_buffer, - (int) client_requests[request_id].count, - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 5: /* write single coil */ - return write_output_bit(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].coms_buffer[0], - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 6: /* write single register */ - return write_output_word(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].coms_buffer[0], - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 7: break; /* function not yet supported */ - case 8: break; /* function not yet supported */ - case 9: break; /* function not yet supported */ - case 10: break; /* function not yet supported */ - case 11: break; /* function not yet supported */ - case 12: break; /* function not yet supported */ - case 13: break; /* function not yet supported */ - case 14: break; /* function not yet supported */ - - case 15: /* write multiple coils */ - return write_output_bits(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].count, - client_requests[request_id].coms_buffer, - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - case 16: /* write multiple registers */ - return write_output_words(client_requests[request_id].slave_id, - client_requests[request_id].address, - client_requests[request_id].count, - client_requests[request_id].coms_buffer, - client_nodes[client_requests[request_id].client_node_id].mb_nd, - client_requests[request_id].retries, - &(client_requests[request_id].error_code), - &(client_requests[request_id].resp_timeout), - &(client_requests[request_id].coms_buf_mutex)); - - default: break; /* should never occur, if file generation is correct */ - } - - fprintf(stderr, "Modbus plugin: Modbus function %%d not supported\n", request_id); /* should never occur, if file generation is correct */ - return -1; -} - - - -/* pack bits from unpacked_data to packed_data */ -static inline int __pack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count, u8 *packed_data) { - u8 bit; - u16 byte, coils_processed; - - if ((0 == bit_count) || (65535-start_addr < bit_count-1)) - return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ - - for( byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) { - packed_data[byte] = 0; - for( bit = 0x01; (bit & 0xFF) && (coils_processed < bit_count); bit <<= 1, coils_processed++ ) { - if(unpacked_data[start_addr + coils_processed]) - packed_data[byte] |= bit; /* set bit */ - else packed_data[byte] &= ~bit; /* reset bit */ - } - } - return 0; -} - - -/* unpack bits from packed_data to unpacked_data */ -static inline int __unpack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count, u8 *packed_data) { - u8 temp, bit; - u16 byte, coils_processed; - - if ((0 == bit_count) || (65535-start_addr < bit_count-1)) - return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ - - for(byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) { - temp = packed_data[byte] ; - for(bit = 0x01; (bit & 0xff) && (coils_processed < bit_count); bit <<= 1, coils_processed++) { - unpacked_data[start_addr + coils_processed] = (temp & bit)?1:0; - } - } - return 0; -} - - -static int __read_inbits (void *mem_map, u16 start_addr, u16 bit_count, u8 *data_bytes) - {return __pack_bits(((server_mem_t *)mem_map)->ro_bits, start_addr, bit_count, data_bytes);} -static int __read_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8 *data_bytes) - {return __pack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);} -static int __write_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8 *data_bytes) - {return __unpack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes); } - - - -static int __read_inwords (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) { - u16 count; - // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */ - - if ((start_addr + word_count) > MEM_AREA_SIZE) - return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ - - /* use memcpy() as it is more efficient... - for (count = 0; count < word_count ; count++) - data_words[count] = ((server_mem_t *)mem_map)->ro_words[count + start_addr]; - */ - memcpy(/* dest */ (void *)data_words, - /* src */ (void *)&(((server_mem_t *)mem_map)->ro_words[start_addr]), - /* size */ word_count * 2); - return 0; -} - - - -static int __read_outwords (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) { - u16 count; - // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */ - - if ((start_addr + word_count) > MEM_AREA_SIZE) - return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ - - /* use memcpy() as it is more efficient... - for (count = 0; count < word_count ; count++) - data_words[count] = ((server_mem_t *)mem_map)->rw_words[count + start_addr]; - */ - memcpy(/* dest */ (void *)data_words, - /* src */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]), - /* size */ word_count * 2); - return 0; -} - - - - -static int __write_outwords(void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) { - u16 count; - // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */ - - if ((start_addr + word_count) > MEM_AREA_SIZE) - return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ - - /* WARNING: The data returned in the data_words[] array is not guaranteed to be 16 bit aligned. - * It is not therefore safe to cast it to an u16 data type. - * The following code cannot be used. memcpy() is used instead. - */ - /* - for (count = 0; count < word_count ; count++) - ((server_mem_t *)mem_map)->rw_words[count + start_addr] = data_words[count]; - */ - memcpy(/* dest */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]), - /* src */ (void *)data_words, - /* size */ word_count * 2); - return 0; -} - - - - -#include - -static void *__mb_server_thread(void *_server_node) { - server_node_t *server_node = _server_node; - mb_slave_callback_t callbacks = { - &__read_inbits, - &__read_outbits, - &__write_outbits, - &__read_inwords, - &__read_outwords, - &__write_outwords, - (void *)&(server_node->mem_area) - }; - - // Enable thread cancelation. Enabled is default, but set it anyway to be safe. - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - - // mb_slave_run() should never return! - mb_slave_run(server_node->mb_nd /* nd */, callbacks, server_node->slave_id); - fprintf(stderr, "Modbus plugin: Modbus server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */ - return NULL; -} - - - -static void *__mb_client_thread(void *_index) { - int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast) - struct timespec next_cycle; - int period_sec = client_nodes[client_node_id].comm_period / 1000; /* comm_period is in ms */ - int period_nsec = (client_nodes[client_node_id].comm_period %%1000)*1000000; /* comm_period is in ms */ - - // Enable thread cancelation. Enabled is default, but set it anyway to be safe. - pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); - - // get the current time - clock_gettime(CLOCK_MONOTONIC, &next_cycle); - - // loop the communication with the client - while (1) { - /* - struct timespec cur_time; - clock_gettime(CLOCK_MONOTONIC, &cur_time); - fprintf(stderr, "Modbus client thread - new cycle (%%ld:%%ld)!\n", cur_time.tv_sec, cur_time.tv_nsec); - */ - int req; - for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){ - /*just do the requests belonging to the client */ - if (client_requests[req].client_node_id != client_node_id) - continue; - int res_tmp = __execute_mb_request(req); - switch (res_tmp) { - case PORT_FAILURE: { - if (res_tmp != client_nodes[client_node_id].prev_error) - fprintf(stderr, "Modbus plugin: Error connecting Modbus client %%s to remote server.\n", client_nodes[client_node_id].location); - client_nodes[client_node_id].prev_error = res_tmp; - break; - } - case INVALID_FRAME: { - if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error)) - fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s was unsuccesful. Server/slave returned an invalid/corrupted frame.\n", client_requests[req].location); - client_requests[req].prev_error = res_tmp; - break; - } - case TIMEOUT: { - if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error)) - fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s timed out waiting for reply from server.\n", client_requests[req].location); - client_requests[req].prev_error = res_tmp; - break; - } - case MODBUS_ERROR: { - if (client_requests[req].prev_error != client_requests[req].error_code) { - fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s was unsuccesful. Server/slave returned error code 0x%%2x", client_requests[req].location, client_requests[req].error_code); - if (client_requests[req].error_code <= MAX_MODBUS_ERROR_CODE ) { - fprintf(stderr, "(%%s)", modbus_error_messages[client_requests[req].error_code]); - fprintf(stderr, ".\n"); - } - } - client_requests[req].prev_error = client_requests[req].error_code; - break; - } - default: { - if ((res_tmp >= 0) && (client_nodes[client_node_id].prev_error != 0)) { - fprintf(stderr, "Modbus plugin: Modbus client %%s has reconnected to server/slave.\n", client_nodes[client_node_id].location); - } - if ((res_tmp >= 0) && (client_requests[req] .prev_error != 0)) { - fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s has succesfully resumed comunication.\n", client_requests[req].location); - } - client_nodes[client_node_id].prev_error = 0; - client_requests[req] .prev_error = 0; - break; - } - } - } - // Determine absolute time instant for starting the next cycle - // struct timespec prev_cycle; - // prev_cycle = next_cycle; - next_cycle.tv_sec += period_sec; - next_cycle.tv_nsec += period_nsec; - if (next_cycle.tv_nsec >= 1000000000) { - next_cycle.tv_sec ++; - next_cycle.tv_nsec -= 1000000000; - } - /* It probably does not make sense to check for overflow of timer. - * Even in 32 bit systems this will take at least 68 years since the computer booted - * (remember, we are using CLOCK_MONOTONIC, which should start counting from 0 - * every time the system boots). On 64 bit systems, it will take over - * 10^11 years to overflow. - */ - /* - if (next_cycle.tv_sec) < prev_cycle.tv_sec) { - // we will lose some precision by reading the time again, - // but it is better than the alternative... - clock_gettime(CLOCK_MONOTONIC, &next_cycle); - next_cycle.tv_sec += period_sec; - next_cycle.tv_nsec += period_nsec; - if (next_cycle.tv_nsec >= 1000000000) { - next_cycle.tv_sec ++; - next_cycle.tv_nsec -= 1000000000; - } - } - */ - clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_cycle, NULL); - } - - // humour the compiler. - return NULL; -} - - - -int __init_%(locstr)s (int argc, char **argv){ - int index; - - for (index=0; index < NUMBER_OF_CLIENT_NODES;index++) - client_nodes[index].mb_nd = -1; - for (index=0; index < NUMBER_OF_SERVER_NODES;index++) - // mb_nd with negative numbers indicate how far it has been initialised (or not) - // -2 --> no modbus node created; no thread created - // -1 --> modbus node created!; no thread created - // >=0 --> modbus node created!; thread created! - server_nodes[index].mb_nd = -2; - - /* modbus library init */ - /* Note that TOTAL_xxxNODE_COUNT are the nodes required by _ALL_ the instances of the modbus - * extension currently in the user's project. This file (MB_xx.c) is handling only one instance, - * but must initialize the library for all instances. Only the first call to mb_slave_and_master_init() - * will result in memory being allocated. All subsequent calls (by other MB_xx,c files) will be ignored - * by the mb_slave_and_master_init() funtion, as long as they are called with the same arguments. - */ - if (mb_slave_and_master_init(TOTAL_TCPNODE_COUNT, TOTAL_RTUNODE_COUNT, TOTAL_ASCNODE_COUNT) <0) { - fprintf(stderr, "Modbus plugin: Error starting modbus library\n"); - // return imediately. Do NOT goto error_exit, as we did not get to - // start the modbus library! - return -1; - } - - /* init the mutex for each client request */ - /* Must be done _before_ launching the client threads!! */ - for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){ - if (pthread_mutex_init(&(client_requests[index].coms_buf_mutex), NULL)) { - fprintf(stderr, "Modbus plugin: Error initializing request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location); - goto error_exit; - } - } - - /* init each client connection to remote modbus server, and launch thread */ - /* NOTE: All client_nodes[].init_state are initialised to 0 in the code - * generated by the modbus plugin - */ - for (index=0; index < NUMBER_OF_CLIENT_NODES;index++){ - /* establish client connection */ - client_nodes[index].mb_nd = mb_master_connect (client_nodes[index].node_address); - if (client_nodes[index].mb_nd < 0){ - fprintf(stderr, "Modbus plugin: Error creating modbus client node %%s\n", client_nodes[index].location); - goto error_exit; - } - client_nodes[index].init_state = 1; // we have created the node - - /* launch a thread to handle this client node */ - { - int res = 0; - pthread_attr_t attr; - res |= pthread_attr_init(&attr); - res |= pthread_create(&(client_nodes[index].thread_id), &attr, &__mb_client_thread, (void *)((char *)NULL + index)); - if (res != 0) { - fprintf(stderr, "Modbus plugin: Error starting modbus client thread for node %%s\n", client_nodes[index].location); - goto error_exit; - } - } - client_nodes[index].init_state = 2; // we have created the node and a thread - } - - /* init each local server */ - /* NOTE: All server_nodes[].init_state are initialised to 0 in the code - * generated by the modbus plugin - */ - for (index=0; index < NUMBER_OF_SERVER_NODES;index++){ - /* create the modbus server */ - server_nodes[index].mb_nd = mb_slave_new (server_nodes[index].node_address); - if (server_nodes[index].mb_nd < 0){ - fprintf(stderr, "Modbus plugin: Error creating modbus server node %%s\n", server_nodes[index].location); - goto error_exit; - } - server_nodes[index].init_state = 1; // we have created the node - - /* launch a thread to handle this server node */ - { - int res = 0; - pthread_attr_t attr; - res |= pthread_attr_init(&attr); - res |= pthread_create(&(server_nodes[index].thread_id), &attr, &__mb_server_thread, (void *)&(server_nodes[index])); - if (res != 0) { - fprintf(stderr, "Modbus plugin: Error starting modbus server thread for node %%s\n", server_nodes[index].location); - goto error_exit; - } - } - server_nodes[index].init_state = 2; // we have created the node and thread - } - - return 0; - -error_exit: - __cleanup_%(locstr)s (); - return -1; -} - - - - - -void __publish_%(locstr)s (){ - int index; - - for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){ - /*just do the output requests */ - if (client_requests[index].req_type == req_output){ - pthread_mutex_lock(&(client_requests[index].coms_buf_mutex)); - // copy from plcv_buffer to coms_buffer - memcpy((void *)client_requests[index].coms_buffer /* destination */, - (void *)client_requests[index].plcv_buffer /* source */, - REQ_BUF_SIZE * sizeof(u16) /* size in bytes */); - pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex)); - } - } -} - - - - - -void __retrieve_%(locstr)s (){ - int index; - - for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){ - /*just do the input requests */ - if (client_requests[index].req_type == req_input){ - pthread_mutex_lock(&(client_requests[index].coms_buf_mutex)); - // copy from coms_buffer to plcv_buffer - memcpy((void *)client_requests[index].plcv_buffer /* destination */, - (void *)client_requests[index].coms_buffer /* source */, - REQ_BUF_SIZE * sizeof(u16) /* size in bytes */); - pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex)); - } - } -} - - - - - -int __cleanup_%(locstr)s (){ - int index, close; - int res = 0; - - /* kill thread and close connections of each modbus client node */ - for (index=0; index < NUMBER_OF_CLIENT_NODES; index++) { - close = 0; - if (client_nodes[index].init_state >= 2) { - // thread was launched, so we try to cancel it! - close = pthread_cancel(client_nodes[index].thread_id); - close |= pthread_join (client_nodes[index].thread_id, NULL); - if (close < 0) - fprintf(stderr, "Modbus plugin: Error closing thread for modbus client %%s\n", client_nodes[index].location); - } - res |= close; - - close = 0; - if (client_nodes[index].init_state >= 1) { - // modbus client node was created, so we try to close it! - close = mb_master_close (client_nodes[index].mb_nd); - if (close < 0){ - fprintf(stderr, "Modbus plugin: Error closing modbus client node %%s\n", client_nodes[index].location); - // We try to shut down as much as possible, so we do not return noW! - } - client_nodes[index].mb_nd = -1; - } - res |= close; - client_nodes[index].init_state = 0; - } - - /* kill thread and close connections of each modbus server node */ - for (index=0; index < NUMBER_OF_SERVER_NODES; index++) { - close = 0; - if (server_nodes[index].init_state >= 2) { - // thread was launched, so we try to cancel it! - close = pthread_cancel(server_nodes[index].thread_id); - close |= pthread_join (server_nodes[index].thread_id, NULL); - if (close < 0) - fprintf(stderr, "Modbus plugin: Error closing thread for modbus server %%s\n", server_nodes[index].location); - } - res |= close; - - close = 0; - if (server_nodes[index].init_state >= 1) { - // modbus server node was created, so we try to close it! - close = mb_slave_close (server_nodes[index].mb_nd); - if (close < 0) { - fprintf(stderr, "Modbus plugin: Error closing node for modbus server %%s (%%d)\n", server_nodes[index].location, server_nodes[index].mb_nd); - // We try to shut down as much as possible, so we do not return noW! - } - server_nodes[index].mb_nd = -1; - } - res |= close; - server_nodes[index].init_state = 0; - } - - /* destroy the mutex of each client request */ - for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++) { - if (pthread_mutex_destroy(&(client_requests[index].coms_buf_mutex))) { - fprintf(stderr, "Modbus plugin: Error destroying request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location); - // We try to shut down as much as possible, so we do not return noW! - res |= -1; - } - } - - /* modbus library close */ - //fprintf(stderr, "Shutting down modbus library...\n"); - if (mb_slave_and_master_done()<0) { - fprintf(stderr, "Modbus plugin: Error shutting down modbus library\n"); - res |= -1; - } - - return res; -} - +/* File generated by Beremiz (PlugGenerate_C method of Modbus plugin) */ + +/* + * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt) + * + * This file is part of the Modbus library for Beremiz and matiec. + * + * This Modbus library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this Modbus library. If not, see . + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + + +#include +#include /* required for memcpy() */ +#include "mb_slave_and_master.h" +#include "MB_%(locstr)s.h" + + +#define MAX_MODBUS_ERROR_CODE 11 +static const char *modbus_error_messages[MAX_MODBUS_ERROR_CODE+1] = { + /* 0 */ "", /* un-used -> no error! */ + /* 1 */ "illegal/unsuported function", + /* 2 */ "illegal data address", + /* 3 */ "illegal data value", + /* 4 */ "slave device failure", + /* 5 */ "acknowledge -> slave intends to reply later", + /* 6 */ "slave device busy", + /* 7 */ "negative acknowledge", + /* 8 */ "memory parity error", + /* 9 */ "", /* undefined by Modbus */ + /* 10*/ "gateway path unavalilable", + /* 11*/ "gateway target device failed to respond" +}; + + +/* Execute a modbus client transaction/request */ +static int __execute_mb_request(int request_id){ + switch (client_requests[request_id].mb_function){ + + case 1: /* read coils */ + return read_output_bits(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].count, + client_requests[request_id].coms_buffer, + (int) client_requests[request_id].count, + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 2: /* read discrete inputs */ + return read_input_bits( client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].count, + client_requests[request_id].coms_buffer, + (int) client_requests[request_id].count, + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 3: /* read holding registers */ + return read_output_words(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].count, + client_requests[request_id].coms_buffer, + (int) client_requests[request_id].count, + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 4: /* read input registers */ + return read_input_words(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].count, + client_requests[request_id].coms_buffer, + (int) client_requests[request_id].count, + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 5: /* write single coil */ + return write_output_bit(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].coms_buffer[0], + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 6: /* write single register */ + return write_output_word(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].coms_buffer[0], + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 7: break; /* function not yet supported */ + case 8: break; /* function not yet supported */ + case 9: break; /* function not yet supported */ + case 10: break; /* function not yet supported */ + case 11: break; /* function not yet supported */ + case 12: break; /* function not yet supported */ + case 13: break; /* function not yet supported */ + case 14: break; /* function not yet supported */ + + case 15: /* write multiple coils */ + return write_output_bits(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].count, + client_requests[request_id].coms_buffer, + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + case 16: /* write multiple registers */ + return write_output_words(client_requests[request_id].slave_id, + client_requests[request_id].address, + client_requests[request_id].count, + client_requests[request_id].coms_buffer, + client_nodes[client_requests[request_id].client_node_id].mb_nd, + client_requests[request_id].retries, + &(client_requests[request_id].error_code), + &(client_requests[request_id].resp_timeout), + &(client_requests[request_id].coms_buf_mutex)); + + default: break; /* should never occur, if file generation is correct */ + } + + fprintf(stderr, "Modbus plugin: Modbus function %%d not supported\n", request_id); /* should never occur, if file generation is correct */ + return -1; +} + + + +/* pack bits from unpacked_data to packed_data */ +static inline int __pack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count, u8 *packed_data) { + u8 bit; + u16 byte, coils_processed; + + if ((0 == bit_count) || (65535-start_addr < bit_count-1)) + return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ + + for( byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) { + packed_data[byte] = 0; + for( bit = 0x01; (bit & 0xFF) && (coils_processed < bit_count); bit <<= 1, coils_processed++ ) { + if(unpacked_data[start_addr + coils_processed]) + packed_data[byte] |= bit; /* set bit */ + else packed_data[byte] &= ~bit; /* reset bit */ + } + } + return 0; +} + + +/* unpack bits from packed_data to unpacked_data */ +static inline int __unpack_bits(u16 *unpacked_data, u16 start_addr, u16 bit_count, u8 *packed_data) { + u8 temp, bit; + u16 byte, coils_processed; + + if ((0 == bit_count) || (65535-start_addr < bit_count-1)) + return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ + + for(byte = 0, coils_processed = 0; coils_processed < bit_count; byte++) { + temp = packed_data[byte] ; + for(bit = 0x01; (bit & 0xff) && (coils_processed < bit_count); bit <<= 1, coils_processed++) { + unpacked_data[start_addr + coils_processed] = (temp & bit)?1:0; + } + } + return 0; +} + + +static int __read_inbits (void *mem_map, u16 start_addr, u16 bit_count, u8 *data_bytes) + {return __pack_bits(((server_mem_t *)mem_map)->ro_bits, start_addr, bit_count, data_bytes);} +static int __read_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8 *data_bytes) + {return __pack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes);} +static int __write_outbits (void *mem_map, u16 start_addr, u16 bit_count, u8 *data_bytes) + {return __unpack_bits(((server_mem_t *)mem_map)->rw_bits, start_addr, bit_count, data_bytes); } + + + +static int __read_inwords (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) { + u16 count; + // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */ + + if ((start_addr + word_count) > MEM_AREA_SIZE) + return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ + + /* use memcpy() as it is more efficient... + for (count = 0; count < word_count ; count++) + data_words[count] = ((server_mem_t *)mem_map)->ro_words[count + start_addr]; + */ + memcpy(/* dest */ (void *)data_words, + /* src */ (void *)&(((server_mem_t *)mem_map)->ro_words[start_addr]), + /* size */ word_count * 2); + return 0; +} + + + +static int __read_outwords (void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) { + u16 count; + // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */ + + if ((start_addr + word_count) > MEM_AREA_SIZE) + return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ + + /* use memcpy() as it is more efficient... + for (count = 0; count < word_count ; count++) + data_words[count] = ((server_mem_t *)mem_map)->rw_words[count + start_addr]; + */ + memcpy(/* dest */ (void *)data_words, + /* src */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]), + /* size */ word_count * 2); + return 0; +} + + + + +static int __write_outwords(void *mem_map, u16 start_addr, u16 word_count, u16 *data_words) { + u16 count; + // return -ERR_ILLEGAL_FUNCTION; /* function not yet supported *//* ERR_ILLEGAL_FUNCTION defined in mb_util.h */ + + if ((start_addr + word_count) > MEM_AREA_SIZE) + return -ERR_ILLEGAL_DATA_ADDRESS; /* ERR_ILLEGAL_DATA_ADDRESS defined in mb_util.h */ + + /* WARNING: The data returned in the data_words[] array is not guaranteed to be 16 bit aligned. + * It is not therefore safe to cast it to an u16 data type. + * The following code cannot be used. memcpy() is used instead. + */ + /* + for (count = 0; count < word_count ; count++) + ((server_mem_t *)mem_map)->rw_words[count + start_addr] = data_words[count]; + */ + memcpy(/* dest */ (void *)&(((server_mem_t *)mem_map)->rw_words[start_addr]), + /* src */ (void *)data_words, + /* size */ word_count * 2); + return 0; +} + + + + +#include + +static void *__mb_server_thread(void *_server_node) { + server_node_t *server_node = _server_node; + mb_slave_callback_t callbacks = { + &__read_inbits, + &__read_outbits, + &__write_outbits, + &__read_inwords, + &__read_outwords, + &__write_outwords, + (void *)&(server_node->mem_area) + }; + + // Enable thread cancelation. Enabled is default, but set it anyway to be safe. + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + + // mb_slave_run() should never return! + mb_slave_run(server_node->mb_nd /* nd */, callbacks, server_node->slave_id); + fprintf(stderr, "Modbus plugin: Modbus server for node %%s died unexpectedly!\n", server_node->location); /* should never occur */ + return NULL; +} + + + +static void *__mb_client_thread(void *_index) { + int client_node_id = (char *)_index - (char *)NULL; // Use pointer arithmetic (more portable than cast) + struct timespec next_cycle; + int period_sec = client_nodes[client_node_id].comm_period / 1000; /* comm_period is in ms */ + int period_nsec = (client_nodes[client_node_id].comm_period %%1000)*1000000; /* comm_period is in ms */ + + // Enable thread cancelation. Enabled is default, but set it anyway to be safe. + pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); + + // get the current time + clock_gettime(CLOCK_MONOTONIC, &next_cycle); + + // loop the communication with the client + while (1) { + /* + struct timespec cur_time; + clock_gettime(CLOCK_MONOTONIC, &cur_time); + fprintf(stderr, "Modbus client thread - new cycle (%%ld:%%ld)!\n", cur_time.tv_sec, cur_time.tv_nsec); + */ + int req; + for (req=0; req < NUMBER_OF_CLIENT_REQTS; req ++){ + /*just do the requests belonging to the client */ + if (client_requests[req].client_node_id != client_node_id) + continue; + int res_tmp = __execute_mb_request(req); + switch (res_tmp) { + case PORT_FAILURE: { + if (res_tmp != client_nodes[client_node_id].prev_error) + fprintf(stderr, "Modbus plugin: Error connecting Modbus client %%s to remote server.\n", client_nodes[client_node_id].location); + client_nodes[client_node_id].prev_error = res_tmp; + break; + } + case INVALID_FRAME: { + if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error)) + fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s was unsuccesful. Server/slave returned an invalid/corrupted frame.\n", client_requests[req].location); + client_requests[req].prev_error = res_tmp; + break; + } + case TIMEOUT: { + if ((res_tmp != client_requests[req].prev_error) && (0 == client_nodes[client_node_id].prev_error)) + fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s timed out waiting for reply from server.\n", client_requests[req].location); + client_requests[req].prev_error = res_tmp; + break; + } + case MODBUS_ERROR: { + if (client_requests[req].prev_error != client_requests[req].error_code) { + fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s was unsuccesful. Server/slave returned error code 0x%%2x", client_requests[req].location, client_requests[req].error_code); + if (client_requests[req].error_code <= MAX_MODBUS_ERROR_CODE ) { + fprintf(stderr, "(%%s)", modbus_error_messages[client_requests[req].error_code]); + fprintf(stderr, ".\n"); + } + } + client_requests[req].prev_error = client_requests[req].error_code; + break; + } + default: { + if ((res_tmp >= 0) && (client_nodes[client_node_id].prev_error != 0)) { + fprintf(stderr, "Modbus plugin: Modbus client %%s has reconnected to server/slave.\n", client_nodes[client_node_id].location); + } + if ((res_tmp >= 0) && (client_requests[req] .prev_error != 0)) { + fprintf(stderr, "Modbus plugin: Modbus client request configured at location %%s has succesfully resumed comunication.\n", client_requests[req].location); + } + client_nodes[client_node_id].prev_error = 0; + client_requests[req] .prev_error = 0; + break; + } + } + } + // Determine absolute time instant for starting the next cycle + // struct timespec prev_cycle; + // prev_cycle = next_cycle; + next_cycle.tv_sec += period_sec; + next_cycle.tv_nsec += period_nsec; + if (next_cycle.tv_nsec >= 1000000000) { + next_cycle.tv_sec ++; + next_cycle.tv_nsec -= 1000000000; + } + /* It probably does not make sense to check for overflow of timer. + * Even in 32 bit systems this will take at least 68 years since the computer booted + * (remember, we are using CLOCK_MONOTONIC, which should start counting from 0 + * every time the system boots). On 64 bit systems, it will take over + * 10^11 years to overflow. + */ + /* + if (next_cycle.tv_sec) < prev_cycle.tv_sec) { + // we will lose some precision by reading the time again, + // but it is better than the alternative... + clock_gettime(CLOCK_MONOTONIC, &next_cycle); + next_cycle.tv_sec += period_sec; + next_cycle.tv_nsec += period_nsec; + if (next_cycle.tv_nsec >= 1000000000) { + next_cycle.tv_sec ++; + next_cycle.tv_nsec -= 1000000000; + } + } + */ + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &next_cycle, NULL); + } + + // humour the compiler. + return NULL; +} + + + +int __init_%(locstr)s (int argc, char **argv){ + int index; + + for (index=0; index < NUMBER_OF_CLIENT_NODES;index++) + client_nodes[index].mb_nd = -1; + for (index=0; index < NUMBER_OF_SERVER_NODES;index++) + // mb_nd with negative numbers indicate how far it has been initialised (or not) + // -2 --> no modbus node created; no thread created + // -1 --> modbus node created!; no thread created + // >=0 --> modbus node created!; thread created! + server_nodes[index].mb_nd = -2; + + /* modbus library init */ + /* Note that TOTAL_xxxNODE_COUNT are the nodes required by _ALL_ the instances of the modbus + * extension currently in the user's project. This file (MB_xx.c) is handling only one instance, + * but must initialize the library for all instances. Only the first call to mb_slave_and_master_init() + * will result in memory being allocated. All subsequent calls (by other MB_xx,c files) will be ignored + * by the mb_slave_and_master_init() funtion, as long as they are called with the same arguments. + */ + if (mb_slave_and_master_init(TOTAL_TCPNODE_COUNT, TOTAL_RTUNODE_COUNT, TOTAL_ASCNODE_COUNT) <0) { + fprintf(stderr, "Modbus plugin: Error starting modbus library\n"); + // return imediately. Do NOT goto error_exit, as we did not get to + // start the modbus library! + return -1; + } + + /* init the mutex for each client request */ + /* Must be done _before_ launching the client threads!! */ + for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){ + if (pthread_mutex_init(&(client_requests[index].coms_buf_mutex), NULL)) { + fprintf(stderr, "Modbus plugin: Error initializing request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location); + goto error_exit; + } + } + + /* init each client connection to remote modbus server, and launch thread */ + /* NOTE: All client_nodes[].init_state are initialised to 0 in the code + * generated by the modbus plugin + */ + for (index=0; index < NUMBER_OF_CLIENT_NODES;index++){ + /* establish client connection */ + client_nodes[index].mb_nd = mb_master_connect (client_nodes[index].node_address); + if (client_nodes[index].mb_nd < 0){ + fprintf(stderr, "Modbus plugin: Error creating modbus client node %%s\n", client_nodes[index].location); + goto error_exit; + } + client_nodes[index].init_state = 1; // we have created the node + + /* launch a thread to handle this client node */ + { + int res = 0; + pthread_attr_t attr; + res |= pthread_attr_init(&attr); + res |= pthread_create(&(client_nodes[index].thread_id), &attr, &__mb_client_thread, (void *)((char *)NULL + index)); + if (res != 0) { + fprintf(stderr, "Modbus plugin: Error starting modbus client thread for node %%s\n", client_nodes[index].location); + goto error_exit; + } + } + client_nodes[index].init_state = 2; // we have created the node and a thread + } + + /* init each local server */ + /* NOTE: All server_nodes[].init_state are initialised to 0 in the code + * generated by the modbus plugin + */ + for (index=0; index < NUMBER_OF_SERVER_NODES;index++){ + /* create the modbus server */ + server_nodes[index].mb_nd = mb_slave_new (server_nodes[index].node_address); + if (server_nodes[index].mb_nd < 0){ + fprintf(stderr, "Modbus plugin: Error creating modbus server node %%s\n", server_nodes[index].location); + goto error_exit; + } + server_nodes[index].init_state = 1; // we have created the node + + /* launch a thread to handle this server node */ + { + int res = 0; + pthread_attr_t attr; + res |= pthread_attr_init(&attr); + res |= pthread_create(&(server_nodes[index].thread_id), &attr, &__mb_server_thread, (void *)&(server_nodes[index])); + if (res != 0) { + fprintf(stderr, "Modbus plugin: Error starting modbus server thread for node %%s\n", server_nodes[index].location); + goto error_exit; + } + } + server_nodes[index].init_state = 2; // we have created the node and thread + } + + return 0; + +error_exit: + __cleanup_%(locstr)s (); + return -1; +} + + + + + +void __publish_%(locstr)s (){ + int index; + + for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){ + /*just do the output requests */ + if (client_requests[index].req_type == req_output){ + pthread_mutex_lock(&(client_requests[index].coms_buf_mutex)); + // copy from plcv_buffer to coms_buffer + memcpy((void *)client_requests[index].coms_buffer /* destination */, + (void *)client_requests[index].plcv_buffer /* source */, + REQ_BUF_SIZE * sizeof(u16) /* size in bytes */); + pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex)); + } + } +} + + + + + +void __retrieve_%(locstr)s (){ + int index; + + for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++){ + /*just do the input requests */ + if (client_requests[index].req_type == req_input){ + pthread_mutex_lock(&(client_requests[index].coms_buf_mutex)); + // copy from coms_buffer to plcv_buffer + memcpy((void *)client_requests[index].plcv_buffer /* destination */, + (void *)client_requests[index].coms_buffer /* source */, + REQ_BUF_SIZE * sizeof(u16) /* size in bytes */); + pthread_mutex_unlock(&(client_requests[index].coms_buf_mutex)); + } + } +} + + + + + +int __cleanup_%(locstr)s (){ + int index, close; + int res = 0; + + /* kill thread and close connections of each modbus client node */ + for (index=0; index < NUMBER_OF_CLIENT_NODES; index++) { + close = 0; + if (client_nodes[index].init_state >= 2) { + // thread was launched, so we try to cancel it! + close = pthread_cancel(client_nodes[index].thread_id); + close |= pthread_join (client_nodes[index].thread_id, NULL); + if (close < 0) + fprintf(stderr, "Modbus plugin: Error closing thread for modbus client %%s\n", client_nodes[index].location); + } + res |= close; + + close = 0; + if (client_nodes[index].init_state >= 1) { + // modbus client node was created, so we try to close it! + close = mb_master_close (client_nodes[index].mb_nd); + if (close < 0){ + fprintf(stderr, "Modbus plugin: Error closing modbus client node %%s\n", client_nodes[index].location); + // We try to shut down as much as possible, so we do not return noW! + } + client_nodes[index].mb_nd = -1; + } + res |= close; + client_nodes[index].init_state = 0; + } + + /* kill thread and close connections of each modbus server node */ + for (index=0; index < NUMBER_OF_SERVER_NODES; index++) { + close = 0; + if (server_nodes[index].init_state >= 2) { + // thread was launched, so we try to cancel it! + close = pthread_cancel(server_nodes[index].thread_id); + close |= pthread_join (server_nodes[index].thread_id, NULL); + if (close < 0) + fprintf(stderr, "Modbus plugin: Error closing thread for modbus server %%s\n", server_nodes[index].location); + } + res |= close; + + close = 0; + if (server_nodes[index].init_state >= 1) { + // modbus server node was created, so we try to close it! + close = mb_slave_close (server_nodes[index].mb_nd); + if (close < 0) { + fprintf(stderr, "Modbus plugin: Error closing node for modbus server %%s (%%d)\n", server_nodes[index].location, server_nodes[index].mb_nd); + // We try to shut down as much as possible, so we do not return noW! + } + server_nodes[index].mb_nd = -1; + } + res |= close; + server_nodes[index].init_state = 0; + } + + /* destroy the mutex of each client request */ + for (index=0; index < NUMBER_OF_CLIENT_REQTS; index ++) { + if (pthread_mutex_destroy(&(client_requests[index].coms_buf_mutex))) { + fprintf(stderr, "Modbus plugin: Error destroying request for modbus client node %%s\n", client_nodes[client_requests[index].client_node_id].location); + // We try to shut down as much as possible, so we do not return noW! + res |= -1; + } + } + + /* modbus library close */ + //fprintf(stderr, "Shutting down modbus library...\n"); + if (mb_slave_and_master_done()<0) { + fprintf(stderr, "Modbus plugin: Error shutting down modbus library\n"); + res |= -1; + } + + return res; +} + diff -r c1298e7ffe3a -r 8391c11477f4 modbus/mb_runtime.h --- a/modbus/mb_runtime.h Fri Mar 24 12:07:47 2017 +0000 +++ b/modbus/mb_runtime.h Tue Jan 30 16:06:58 2018 +0100 @@ -1,148 +1,148 @@ -/* File generated by Beremiz (PlugGenerate_C method of modbus Plugin instance) */ - -/* - * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt) - * - * This file is part of the Modbus library for Beremiz and matiec. - * - * This Modbus library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this Modbus library. If not, see . - * - * This code is made available on the understanding that it will not be - * used in safety-critical situations without a full and competent review. - */ - -#include "mb_addr.h" -#include "mb_tcp_private.h" -#include "mb_master_private.h" - - - -#define DEF_REQ_SEND_RETRIES 0 - - // Used by the Modbus server node -#define MEM_AREA_SIZE 65536 -typedef struct{ - u16 ro_bits [MEM_AREA_SIZE]; - u16 rw_bits [MEM_AREA_SIZE]; - u16 ro_words[MEM_AREA_SIZE]; - u16 rw_words[MEM_AREA_SIZE]; - } server_mem_t; - -typedef struct{ - const char *location; - u8 slave_id; - node_addr_t node_address; - int mb_nd; // modbus library node used for this server - int init_state; // store how far along the server's initialization has progressed - pthread_t thread_id; // thread handling this server - server_mem_t mem_area; - } server_node_t; - - - // Used by the Modbus client node -typedef struct{ - const char *location; - node_addr_t node_address; - int mb_nd; - int init_state; // store how far along the client's initialization has progressed - u64 comm_period; - int prev_error; // error code of the last printed error message (0 when no error) - pthread_t thread_id; // thread handling all communication with this client - } client_node_t; - - - // Used by the Modbus client plugin -typedef enum { - req_input, - req_output, - no_request /* just for tests to quickly disable a request */ - } iotype_t; - -#define REQ_BUF_SIZE 2000 -typedef struct{ - const char *location; - int client_node_id; - u8 slave_id; - iotype_t req_type; - u8 mb_function; - u16 address; - u16 count; - int retries; - u8 error_code; // modbus error code (if any) of current request - int prev_error; // error code of the last printed error message (0 when no error) - struct timespec resp_timeout; - // buffer used to store located PLC variables - u16 plcv_buffer[REQ_BUF_SIZE]; - // buffer used to store data coming from / going to server - u16 coms_buffer[REQ_BUF_SIZE]; - pthread_mutex_t coms_buf_mutex; // mutex to access coms_buffer[] - } client_request_t; - - -/* The total number of nodes, needed to support _all_ instances of the modbus plugin */ -#define TOTAL_TCPNODE_COUNT %(total_tcpnode_count)s -#define TOTAL_RTUNODE_COUNT %(total_rtunode_count)s -#define TOTAL_ASCNODE_COUNT %(total_ascnode_count)s - -/* Values for instance %(locstr)s of the modbus plugin */ -#define MAX_NUMBER_OF_TCPCLIENTS %(max_remote_tcpclient)s - -#define NUMBER_OF_TCPSERVER_NODES %(tcpserver_node_count)s -#define NUMBER_OF_TCPCLIENT_NODES %(tcpclient_node_count)s -#define NUMBER_OF_TCPCLIENT_REQTS %(tcpclient_reqs_count)s - -#define NUMBER_OF_RTUSERVER_NODES %(rtuserver_node_count)s -#define NUMBER_OF_RTUCLIENT_NODES %(rtuclient_node_count)s -#define NUMBER_OF_RTUCLIENT_REQTS %(rtuclient_reqs_count)s - -#define NUMBER_OF_ASCIISERVER_NODES %(ascserver_node_count)s -#define NUMBER_OF_ASCIICLIENT_NODES %(ascclient_node_count)s -#define NUMBER_OF_ASCIICLIENT_REQTS %(ascclient_reqs_count)s - -#define NUMBER_OF_SERVER_NODES (NUMBER_OF_TCPSERVER_NODES + \ - NUMBER_OF_RTUSERVER_NODES + \ - NUMBER_OF_ASCIISERVER_NODES) - -#define NUMBER_OF_CLIENT_NODES (NUMBER_OF_TCPCLIENT_NODES + \ - NUMBER_OF_RTUCLIENT_NODES + \ - NUMBER_OF_ASCIICLIENT_NODES) - -#define NUMBER_OF_CLIENT_REQTS (NUMBER_OF_TCPCLIENT_REQTS + \ - NUMBER_OF_RTUCLIENT_REQTS + \ - NUMBER_OF_ASCIICLIENT_REQTS) - - -/*initialization following all parameters given by user in application*/ - -static client_node_t client_nodes[NUMBER_OF_CLIENT_NODES] = { -%(client_nodes_params)s -}; - - -static client_request_t client_requests[NUMBER_OF_CLIENT_REQTS] = { -%(client_req_params)s -}; - - -static server_node_t server_nodes[NUMBER_OF_SERVER_NODES] = { -%(server_nodes_params)s -} -; - -/*******************/ -/*located variables*/ -/*******************/ - -%(loc_vars)s - +/* File generated by Beremiz (PlugGenerate_C method of modbus Plugin instance) */ + +/* + * Copyright (c) 2016 Mario de Sousa (msousa@fe.up.pt) + * + * This file is part of the Modbus library for Beremiz and matiec. + * + * This Modbus library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser + * General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this Modbus library. If not, see . + * + * This code is made available on the understanding that it will not be + * used in safety-critical situations without a full and competent review. + */ + +#include "mb_addr.h" +#include "mb_tcp_private.h" +#include "mb_master_private.h" + + + +#define DEF_REQ_SEND_RETRIES 0 + + // Used by the Modbus server node +#define MEM_AREA_SIZE 65536 +typedef struct{ + u16 ro_bits [MEM_AREA_SIZE]; + u16 rw_bits [MEM_AREA_SIZE]; + u16 ro_words[MEM_AREA_SIZE]; + u16 rw_words[MEM_AREA_SIZE]; + } server_mem_t; + +typedef struct{ + const char *location; + u8 slave_id; + node_addr_t node_address; + int mb_nd; // modbus library node used for this server + int init_state; // store how far along the server's initialization has progressed + pthread_t thread_id; // thread handling this server + server_mem_t mem_area; + } server_node_t; + + + // Used by the Modbus client node +typedef struct{ + const char *location; + node_addr_t node_address; + int mb_nd; + int init_state; // store how far along the client's initialization has progressed + u64 comm_period; + int prev_error; // error code of the last printed error message (0 when no error) + pthread_t thread_id; // thread handling all communication with this client + } client_node_t; + + + // Used by the Modbus client plugin +typedef enum { + req_input, + req_output, + no_request /* just for tests to quickly disable a request */ + } iotype_t; + +#define REQ_BUF_SIZE 2000 +typedef struct{ + const char *location; + int client_node_id; + u8 slave_id; + iotype_t req_type; + u8 mb_function; + u16 address; + u16 count; + int retries; + u8 error_code; // modbus error code (if any) of current request + int prev_error; // error code of the last printed error message (0 when no error) + struct timespec resp_timeout; + // buffer used to store located PLC variables + u16 plcv_buffer[REQ_BUF_SIZE]; + // buffer used to store data coming from / going to server + u16 coms_buffer[REQ_BUF_SIZE]; + pthread_mutex_t coms_buf_mutex; // mutex to access coms_buffer[] + } client_request_t; + + +/* The total number of nodes, needed to support _all_ instances of the modbus plugin */ +#define TOTAL_TCPNODE_COUNT %(total_tcpnode_count)s +#define TOTAL_RTUNODE_COUNT %(total_rtunode_count)s +#define TOTAL_ASCNODE_COUNT %(total_ascnode_count)s + +/* Values for instance %(locstr)s of the modbus plugin */ +#define MAX_NUMBER_OF_TCPCLIENTS %(max_remote_tcpclient)s + +#define NUMBER_OF_TCPSERVER_NODES %(tcpserver_node_count)s +#define NUMBER_OF_TCPCLIENT_NODES %(tcpclient_node_count)s +#define NUMBER_OF_TCPCLIENT_REQTS %(tcpclient_reqs_count)s + +#define NUMBER_OF_RTUSERVER_NODES %(rtuserver_node_count)s +#define NUMBER_OF_RTUCLIENT_NODES %(rtuclient_node_count)s +#define NUMBER_OF_RTUCLIENT_REQTS %(rtuclient_reqs_count)s + +#define NUMBER_OF_ASCIISERVER_NODES %(ascserver_node_count)s +#define NUMBER_OF_ASCIICLIENT_NODES %(ascclient_node_count)s +#define NUMBER_OF_ASCIICLIENT_REQTS %(ascclient_reqs_count)s + +#define NUMBER_OF_SERVER_NODES (NUMBER_OF_TCPSERVER_NODES + \ + NUMBER_OF_RTUSERVER_NODES + \ + NUMBER_OF_ASCIISERVER_NODES) + +#define NUMBER_OF_CLIENT_NODES (NUMBER_OF_TCPCLIENT_NODES + \ + NUMBER_OF_RTUCLIENT_NODES + \ + NUMBER_OF_ASCIICLIENT_NODES) + +#define NUMBER_OF_CLIENT_REQTS (NUMBER_OF_TCPCLIENT_REQTS + \ + NUMBER_OF_RTUCLIENT_REQTS + \ + NUMBER_OF_ASCIICLIENT_REQTS) + + +/*initialization following all parameters given by user in application*/ + +static client_node_t client_nodes[NUMBER_OF_CLIENT_NODES] = { +%(client_nodes_params)s +}; + + +static client_request_t client_requests[NUMBER_OF_CLIENT_REQTS] = { +%(client_req_params)s +}; + + +static server_node_t server_nodes[NUMBER_OF_SERVER_NODES] = { +%(server_nodes_params)s +} +; + +/*******************/ +/*located variables*/ +/*******************/ + +%(loc_vars)s + diff -r c1298e7ffe3a -r 8391c11477f4 plcopen/Standard_Function_Blocks.xml --- a/plcopen/Standard_Function_Blocks.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/plcopen/Standard_Function_Blocks.xml Tue Jan 30 16:06:58 2018 +0100 @@ -115,7 +115,7 @@ - + diff -r c1298e7ffe3a -r 8391c11477f4 plcopen/__init__.py --- a/plcopen/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/plcopen/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -26,7 +26,7 @@ # plcopen module dynamically creates its classes -from plcopen import PLCOpenParser, LoadProject, SaveProject, LoadPou, \ +from __future__ import absolute_import +from plcopen.plcopen import \ + PLCOpenParser, LoadProject, SaveProject, LoadPou, \ LoadPouInstances, VarOrder, QualifierList, rect - - diff -r c1298e7ffe3a -r 8391c11477f4 plcopen/definitions.py --- a/plcopen/definitions.py Fri Mar 24 12:07:47 2017 +0000 +++ b/plcopen/definitions.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -23,30 +24,34 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from os.path import join, split, realpath -sd = split(realpath(__file__))[0] +from __future__ import absolute_import +from os.path import join +import util.paths as paths +from util.TranslationCatalogs import NoTranslate +sd = paths.AbsDir(__file__) # Override gettext _ in this module # since we just want string to be added to dictionnary # but translation should happen here -_ = lambda x:x +_ = NoTranslate -LANGUAGES = ["IL","ST","FBD","LD","SFC"] +LANGUAGES = ["IL", "ST", "FBD", "LD", "SFC"] -LOCATIONDATATYPES = {"X" : ["BOOL"], - "B" : ["SINT", "USINT", "BYTE", "STRING"], - "W" : ["INT", "UINT", "WORD", "WSTRING"], - "D" : ["DINT", "UDINT", "REAL", "DWORD"], - "L" : ["LINT", "ULINT", "LREAL", "LWORD"]} +LOCATIONDATATYPES = {"X": ["BOOL"], + "B": ["SINT", "USINT", "BYTE", "STRING"], + "W": ["INT", "UINT", "WORD", "WSTRING"], + "D": ["DINT", "UDINT", "REAL", "DWORD"], + "L": ["LINT", "ULINT", "LREAL", "LWORD"]} -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Function Block Types definitions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -StdTC6Libs = [(_("Standard function blocks"), join(sd, "Standard_Function_Blocks.xml")), - (_("Additional function blocks"),join(sd, "Additional_Function_Blocks.xml"))] +StdTC6Libs = [(_("Standard function blocks"), join(sd, "Standard_Function_Blocks.xml")), + (_("Additional function blocks"), join(sd, "Additional_Function_Blocks.xml"))] -StdFuncsCSV = join(sd,"iec_std.csv") +StdFuncsCSV = join(sd, "iec_std.csv") + def GetBlockInfos(pou): infos = pou.getblockInfos() @@ -57,15 +62,15 @@ for var_name, var_type, var_modifier in infos["inputs"]] return infos -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Data Types definitions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- -""" -Ordored list of common data types defined in the IEC 61131-3 -Each type is associated to his direct parent type. It defines then a hierarchy -between type that permits to make a comparison of two types -""" + +#: Ordored list of common data types defined in the IEC 61131-3 +#: Each type is associated to his direct parent type. It defines then a hierarchy +#: between type that permits to make a comparison of two types + TypeHierarchy_list = [ ("ANY", None), ("ANY_DERIVED", "ANY"), @@ -100,45 +105,45 @@ ("WORD", "ANY_NBIT"), ("DWORD", "ANY_NBIT"), ("LWORD", "ANY_NBIT") - #("WSTRING", "ANY_STRING") # TODO + # ("WSTRING", "ANY_STRING") # TODO ] DefaultType = "DINT" DataTypeRange_list = [ - ("SINT", (-2**7, 2**7 - 1)), - ("INT", (-2**15, 2**15 - 1)), - ("DINT", (-2**31, 2**31 - 1)), - ("LINT", (-2**31, 2**31 - 1)), - ("USINT", (0, 2**8 - 1)), - ("UINT", (0, 2**16 - 1)), - ("UDINT", (0, 2**31 - 1)), - ("ULINT", (0, 2**31 - 1)) + ("SINT", (-2**7, 2**7 - 1)), + ("INT", (-2**15, 2**15 - 1)), + ("DINT", (-2**31, 2**31 - 1)), + ("LINT", (-2**63, 2**63 - 1)), + ("USINT", (0, 2**8 - 1)), + ("UINT", (0, 2**16 - 1)), + ("UDINT", (0, 2**32 - 1)), + ("ULINT", (0, 2**64 - 1)) ] ANY_TO_ANY_FILTERS = { - "ANY_TO_ANY":[ + "ANY_TO_ANY": [ # simple type conv are let as C cast - (("ANY_INT","ANY_BIT"),("ANY_NUM","ANY_BIT")), - (("ANY_REAL",),("ANY_REAL",)), + (("ANY_INT", "ANY_BIT"), ("ANY_NUM", "ANY_BIT")), + (("ANY_REAL",), ("ANY_REAL",)), # REAL_TO_INT - (("ANY_REAL",),("ANY_SINT",)), - (("ANY_REAL",),("ANY_UINT",)), - (("ANY_REAL",),("ANY_BIT",)), + (("ANY_REAL",), ("ANY_SINT",)), + (("ANY_REAL",), ("ANY_UINT",)), + (("ANY_REAL",), ("ANY_BIT",)), # TO_TIME - (("ANY_INT","ANY_BIT"),("ANY_DATE","TIME")), - (("ANY_REAL",),("ANY_DATE","TIME")), - (("ANY_STRING",), ("ANY_DATE","TIME")), + (("ANY_INT", "ANY_BIT"), ("ANY_DATE", "TIME")), + (("ANY_REAL",), ("ANY_DATE", "TIME")), + (("ANY_STRING",), ("ANY_DATE", "TIME")), # FROM_TIME - (("ANY_DATE","TIME"), ("ANY_REAL",)), - (("ANY_DATE","TIME"), ("ANY_INT","ANY_NBIT")), + (("ANY_DATE", "TIME"), ("ANY_REAL",)), + (("ANY_DATE", "TIME"), ("ANY_INT", "ANY_NBIT")), (("TIME",), ("ANY_STRING",)), (("DATE",), ("ANY_STRING",)), - (("TOD",), ("ANY_STRING",)), - (("DT",), ("ANY_STRING",)), + (("TOD",), ("ANY_STRING",)), + (("DT",), ("ANY_STRING",)), # TO_STRING - (("BOOL",), ("ANY_STRING",)), - (("ANY_BIT",), ("ANY_STRING",)), + (("BOOL",), ("ANY_STRING",)), + (("ANY_BIT",), ("ANY_STRING",)), (("ANY_REAL",), ("ANY_STRING",)), (("ANY_SINT",), ("ANY_STRING",)), (("ANY_UINT",), ("ANY_STRING",)), @@ -147,17 +152,20 @@ (("ANY_STRING",), ("ANY_BIT",)), (("ANY_STRING",), ("ANY_SINT",)), (("ANY_STRING",), ("ANY_UINT",)), - (("ANY_STRING",), ("ANY_REAL",))], - "BCD_TO_ANY":[ - (("BYTE",),("USINT",)), - (("WORD",),("UINT",)), - (("DWORD",),("UDINT",)), - (("LWORD",),("ULINT",))], - "ANY_TO_BCD":[ - (("USINT",),("BYTE",)), - (("UINT",),("WORD",)), - (("UDINT",),("DWORD",)), - (("ULINT",),("LWORD",))] + (("ANY_STRING",), ("ANY_REAL",)) + ], + "BCD_TO_ANY": [ + (("BYTE",), ("USINT",)), + (("WORD",), ("UINT",)), + (("DWORD",), ("UDINT",)), + (("LWORD",), ("ULINT",)) + ], + "ANY_TO_BCD": [ + (("USINT",), ("BYTE",)), + (("UINT",), ("WORD",)), + (("UDINT",), ("DWORD",)), + (("ULINT",), ("LWORD",)) + ] } # remove gettext override diff -r c1298e7ffe3a -r 8391c11477f4 plcopen/plcopen.py --- a/plcopen/plcopen.py Fri Mar 24 12:07:47 2017 +0000 +++ b/plcopen/plcopen.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,39 +23,62 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +from types import * +import re +from collections import OrderedDict + +from lxml import etree + from xmlclass import * -from types import * -import os, re -from lxml import etree -from collections import OrderedDict - -""" -Dictionary that makes the relation between var names in plcopen and displayed values -""" -VarTypes = {"Local" : "localVars", "Temp" : "tempVars", "Input" : "inputVars", - "Output" : "outputVars", "InOut" : "inOutVars", "External" : "externalVars", - "Global" : "globalVars", "Access" : "accessVars"} +import util.paths as paths + + +#: Dictionary that makes the relation between var names +#: in plcopen and displayed values + +VarTypes = { + "Local": "localVars", + "Temp": "tempVars", + "Input": "inputVars", + "Output": "outputVars", + "InOut": "inOutVars", + "External": "externalVars", + "Global": "globalVars", + "Access": "accessVars" +} searchResultVarTypes = { - "inputVars": "var_input", + "inputVars": "var_input", "outputVars": "var_output", - "inOutVars": "var_inout" + "inOutVars": "var_inout" } -""" -Define in which order var types must be displayed -""" -VarOrder = ["Local","Temp","Input","Output","InOut","External","Global","Access"] - -""" -Define which action qualifier must be associated with a duration -""" -QualifierList = OrderedDict([("N", False), ("R", False), ("S", False), - ("L", True), ("D", True), ("P", False), ("P0", False), - ("P1", False), ("SD", True), ("DS", True), ("SL", True)]) - - -FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)" + +#: Define in which order var types must be displayed + +VarOrder = ["Local", "Temp", "Input", "Output", "InOut", "External", "Global", "Access"] + + +#: Define which action qualifier must be associated with a duration + +QualifierList = OrderedDict([ + ("N", False), + ("R", False), + ("S", False), + ("L", True), + ("D", True), + ("P", False), + ("P0", False), + ("P1", False), + ("SD", True), + ("DS", True), + ("SL", True)]) + + +FILTER_ADDRESS_MODEL = "(%%[IQM](?:[XBWDL])?)(%s)((?:\.[0-9]+)*)" + def update_address(address, address_model, new_leading): result = address_model.match(address) @@ -63,6 +87,7 @@ groups = result.groups() return groups[0] + new_leading + groups[2] + def _init_and_compare(function, v1, v2): if v1 is None: return v2 @@ -70,11 +95,12 @@ return function(v1, v2) return v1 -""" -Helper class for bounding_box calculation -""" -class rect: - + +class rect(object): + """ + Helper class for bounding_box calculation + """ + def __init__(self, x=None, y=None, width=None, height=None): self.x_min = x self.x_max = None @@ -84,19 +110,19 @@ self.x_max = x + width if height is not None and y is not None: self.y_max = y + height - + def update(self, x, y): self.x_min = _init_and_compare(min, self.x_min, x) self.x_max = _init_and_compare(max, self.x_max, x) self.y_min = _init_and_compare(min, self.y_min, y) self.y_max = _init_and_compare(max, self.y_max, y) - + def union(self, rect): self.x_min = _init_and_compare(min, self.x_min, rect.x_min) self.x_max = _init_and_compare(max, self.x_max, rect.x_max) self.y_min = _init_and_compare(min, self.y_min, rect.y_min) self.y_max = _init_and_compare(max, self.y_max, rect.y_max) - + def bounding_box(self): width = height = None if self.x_min is not None and self.x_max is not None: @@ -105,12 +131,14 @@ height = self.y_max - self.y_min return self.x_min, self.y_min, width, height + def TextLenInRowColumn(text): if text == "": return (0, 0) lines = text.split("\n") return len(lines) - 1, len(lines[-1]) + def CompilePattern(criteria): flag = 0 if criteria["case_sensitive"] else re.IGNORECASE find_pattern = criteria["find_pattern"] @@ -118,29 +146,39 @@ find_pattern = re.escape(find_pattern) criteria["pattern"] = re.compile(find_pattern, flag) + def TestTextElement(text, criteria): lines = text.splitlines() test_result = [] result = criteria["pattern"].search(text) while result is not None: + prev_pos = result.endpos start = TextLenInRowColumn(text[:result.start()]) end = TextLenInRowColumn(text[:result.end() - 1]) test_result.append((start, end, "\n".join(lines[start[0]:end[0] + 1]))) result = criteria["pattern"].search(text, result.end()) + if result is not None and prev_pos == result.endpos: + break return test_result + def TextMatched(str1, str2): return str1 and str2 and (str1.upper() == str2.upper()) -PLCOpenParser = GenerateParserFromXSD(os.path.join(os.path.split(__file__)[0], "tc6_xml_v201.xsd")) -PLCOpen_XPath = lambda xpath: etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP) + +PLCOpenParser = GenerateParserFromXSD(paths.AbsNeighbourFile(__file__, "tc6_xml_v201.xsd")) + + +def PLCOpen_XPath(xpath): + return etree.XPath(xpath, namespaces=PLCOpenParser.NSMAP) + LOAD_POU_PROJECT_TEMPLATE = """ - - @@ -159,6 +197,7 @@ """ + def LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type): return LOAD_POU_PROJECT_TEMPLATE % """ @@ -167,12 +206,13 @@ """ % locals() -PLCOpen_v1_file = open(os.path.join(os.path.split(__file__)[0], "TC6_XML_V10_B.xsd")) + +PLCOpen_v1_file = open(paths.AbsNeighbourFile(__file__, "TC6_XML_V10_B.xsd")) PLCOpen_v1_xml = PLCOpen_v1_file.read() PLCOpen_v1_file.close() PLCOpen_v1_xml = PLCOpen_v1_xml.replace( - "http://www.plcopen.org/xml/tc6.xsd", - "http://www.plcopen.org/xml/tc6_0201") + "http://www.plcopen.org/xml/tc6.xsd", + "http://www.plcopen.org/xml/tc6_0201") PLCOpen_v1_xsd = etree.XMLSchema(etree.fromstring(PLCOpen_v1_xml)) # XPath for file compatibility process @@ -183,23 +223,24 @@ ActionBlocksXPath = PLCOpen_XPath("ppx:types/ppx:pous/ppx:pou/ppx:body/*/ppx:actionBlock") ActionBlocksConnectionPointOutXPath = PLCOpen_XPath("ppx:connectionPointOut") + def LoadProjectXML(project_xml): project_xml = project_xml.replace( - "http://www.plcopen.org/xml/tc6.xsd", + "http://www.plcopen.org/xml/tc6.xsd", "http://www.plcopen.org/xml/tc6_0201") for cre, repl in [ - (re.compile("(?)(?:)(?!)"), "]]>")]: + (re.compile("(?)(?:)(?!)"), "]]>")]: project_xml = cre.sub(repl, project_xml) - + try: tree, error = PLCOpenParser.LoadXMLString(project_xml) if error is None: return tree, None - + if PLCOpen_v1_xsd.validate(tree): # Make file compatible with PLCOpen v2 - + # Update resource interval value for resource in ProjectResourcesXPath(tree): for task in resource.gettask(): @@ -213,24 +254,24 @@ time_values.extend([int(seconds), int((seconds % 1) * 1000000)]) text = "T#" if time_values[0] != 0: - text += "%dh"%time_values[0] + text += "%dh" % time_values[0] if time_values[1] != 0: - text += "%dm"%time_values[1] + text += "%dm" % time_values[1] if time_values[2] != 0: - text += "%ds"%time_values[2] + text += "%ds" % time_values[2] if time_values[3] != 0: if time_values[3] % 1000 != 0: - text += "%.3fms"%(float(time_values[3]) / 1000) + text += "%.3fms" % (float(time_values[3]) / 1000) else: - text += "%dms"%(time_values[3] / 1000) + text += "%dms" % (time_values[3] / 1000) task.set("interval", text) - + # Update resources pou instance attributes for pouInstance in ResourceInstancesXpath(resource): type_name = pouInstance.attrib.pop("type") if type_name is not None: pouInstance.set("typeName", type_name) - + # Update transitions condition for transition_condition in TransitionsConditionXPath(tree): connections = ConditionConnectionsXPath(transition_condition) @@ -240,64 +281,75 @@ connectionPointIn.setrelPositionXY(0, 0) for connection in connections: connectionPointIn.append(connection) - + # Update actionBlocks for actionBlock in ActionBlocksXPath(tree): for connectionPointOut in ActionBlocksConnectionPointOutXPath(actionBlock): actionBlock.remove(connectionPointOut) - + for action in actionBlock.getaction(): action.set("localId", "0") relPosition = PLCOpenParser.CreateElement("relPosition", "action") relPosition.set("x", "0") relPosition.set("y", "0") action.setrelPosition(relPosition) - + return tree, None - + return tree, error - + except Exception, e: return None, e.message + def LoadProject(filepath): project_file = open(filepath) project_xml = project_file.read() project_file.close() return LoadProjectXML(project_xml) + project_pou_xpath = PLCOpen_XPath("/ppx:project/ppx:types/ppx:pous/ppx:pou") + + def LoadPou(xml_string): root, error = LoadProjectXML(LOAD_POU_PROJECT_TEMPLATE % xml_string) return project_pou_xpath(root)[0], error + project_pou_instances_xpath = { body_type: PLCOpen_XPath( "/ppx:project/ppx:types/ppx:pous/ppx:pou[@name='paste_pou']/ppx:body/ppx:%s/*" % body_type) for body_type in ["FBD", "LD", "SFC"]} + + def LoadPouInstances(xml_string, body_type): root, error = LoadProjectXML( LOAD_POU_INSTANCES_PROJECT_TEMPLATE(body_type) % xml_string) return project_pou_instances_xpath[body_type](root), error + def SaveProject(project, filepath): project_file = open(filepath, 'w') project_file.write(etree.tostring( - project, - pretty_print=True, - xml_declaration=True, + project, + pretty_print=True, + xml_declaration=True, encoding='utf-8')) project_file.close() -cls = PLCOpenParser.GetElementClass("formattedText") -if cls: + +# ---------------------------------------------------------------------- + + +def _updateFormattedTextClass(cls): def updateElementName(self, old_name, new_name): text = self.getanyText() pattern = re.compile('\\b' + old_name + '\\b', re.IGNORECASE) text = pattern.sub(new_name, text) self.setanyText(text) setattr(cls, "updateElementName", updateElementName) - + def updateElementAddress(self, address_model, new_leading): text = self.getanyText() startpos = 0 @@ -310,28 +362,35 @@ result = address_model.search(text, startpos) self.setanyText(text) setattr(cls, "updateElementAddress", updateElementAddress) - + def hasblock(self, block_type): - text = self.getanyText() + text = self.getanyText() pattern = re.compile('\\b' + block_type + '\\b', re.IGNORECASE) return pattern.search(text) is not None setattr(cls, "hasblock", hasblock) - + def Search(self, criteria, parent_infos): return [(tuple(parent_infos),) + result for result in TestTextElement(self.getanyText(), criteria)] setattr(cls, "Search", Search) - -cls = PLCOpenParser.GetElementClass("project") -if cls: - + + +cls = PLCOpenParser.GetElementClass("formattedText") +if cls: + _updateFormattedTextClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateProjectClass(cls): def setname(self, name): self.contentHeader.setname(name) setattr(cls, "setname", setname) - + def getname(self): return self.contentHeader.getname() setattr(cls, "getname", getname) - + def getfileHeader(self): fileheader_obj = self.fileHeader return { @@ -346,7 +405,7 @@ ("contentDescription", fileheader_obj.getcontentDescription())] } setattr(cls, "getfileHeader", getfileHeader) - + def setfileHeader(self, fileheader): fileheader_obj = self.fileHeader for attr in ["companyName", "companyURL", "productName", @@ -356,7 +415,7 @@ if value is not None: setattr(fileheader_obj, attr, value) setattr(cls, "setfileHeader", setfileHeader) - + def getcontentHeader(self): contentheader_obj = self.contentHeader contentheader = { @@ -373,7 +432,7 @@ contentheader["scaling"] = self.contentHeader.getscaling() return contentheader setattr(cls, "getcontentHeader", getcontentHeader) - + def setcontentHeader(self, contentheader): contentheader_obj = self.contentHeader for attr, value in contentheader.iteritems(): @@ -387,74 +446,79 @@ elif attr in ["modificationDateTime", "organization", "language"]: setattr(contentheader_obj, attr, value) setattr(cls, "setcontentHeader", setcontentHeader) - + def gettypeElementFunc(element_type): elements_xpath = PLCOpen_XPath( "ppx:types/ppx:%(element_type)ss/ppx:%(element_type)s[@name=$name]" % locals()) + def gettypeElement(self, name): elements = elements_xpath(self, name=name) if len(elements) == 1: return elements[0] return None return gettypeElement - + datatypes_xpath = PLCOpen_XPath("ppx:types/ppx:dataTypes/ppx:dataType") filtered_datatypes_xpath = PLCOpen_XPath( "ppx:types/ppx:dataTypes/ppx:dataType[@name!=$exclude]") + def getdataTypes(self, exclude=None): if exclude is not None: return filtered_datatypes_xpath(self, exclude=exclude) return datatypes_xpath(self) setattr(cls, "getdataTypes", getdataTypes) - + setattr(cls, "getdataType", gettypeElementFunc("dataType")) - + def appenddataType(self, name): if self.getdataType(name) is not None: - raise ValueError, "\"%s\" Data Type already exists !!!"%name + raise ValueError("\"%s\" Data Type already exists !!!" % name) self.types.appenddataTypeElement(name) setattr(cls, "appenddataType", appenddataType) - + def insertdataType(self, index, datatype): self.types.insertdataTypeElement(index, datatype) setattr(cls, "insertdataType", insertdataType) - + def removedataType(self, name): self.types.removedataTypeElement(name) setattr(cls, "removedataType", removedataType) - - def getpous(self, exclude=None, filter=[]): + + def getpous(self, exclude=None, filter=None): + filter = [] if filter is None else filter return self.xpath( - "ppx:types/ppx:pous/ppx:pou%s%s" % - (("[@name!='%s']" % exclude) if exclude is not None else '', - ("[%s]" % " or ".join( - map(lambda x: "@pouType='%s'" % x, filter))) - if len(filter) > 0 else ""), + "ppx:types/ppx:pous/ppx:pou%s%s" % + (("[@name!='%s']" % exclude) if exclude is not None else '', + ("[%s]" % " or ".join( + map(lambda x: "@pouType='%s'" % x, filter))) + if len(filter) > 0 else ""), namespaces=PLCOpenParser.NSMAP) setattr(cls, "getpous", getpous) - + setattr(cls, "getpou", gettypeElementFunc("pou")) - + def appendpou(self, name, pou_type, body_type): self.types.appendpouElement(name, pou_type, body_type) setattr(cls, "appendpou", appendpou) - + def insertpou(self, index, pou): self.types.insertpouElement(index, pou) setattr(cls, "insertpou", insertpou) - + def removepou(self, name): self.types.removepouElement(name) setattr(cls, "removepou", removepou) configurations_xpath = PLCOpen_XPath( "ppx:instances/ppx:configurations/ppx:configuration") + def getconfigurations(self): return configurations_xpath(self) setattr(cls, "getconfigurations", getconfigurations) configuration_xpath = PLCOpen_XPath( "ppx:instances/ppx:configurations/ppx:configuration[@name=$name]") + def getconfiguration(self, name): configurations = configuration_xpath(self, name=name) if len(configurations) == 1: @@ -464,21 +528,22 @@ def addconfiguration(self, name): if self.getconfiguration(name) is not None: - raise ValueError, _("\"%s\" configuration already exists !!!") % name + raise ValueError(_("\"%s\" configuration already exists !!!") % name) new_configuration = PLCOpenParser.CreateElement("configuration", "configurations") new_configuration.setname(name) self.instances.configurations.appendconfiguration(new_configuration) - setattr(cls, "addconfiguration", addconfiguration) + setattr(cls, "addconfiguration", addconfiguration) def removeconfiguration(self, name): configuration = self.getconfiguration(name) if configuration is None: - raise ValueError, ("\"%s\" configuration doesn't exist !!!") % name + raise ValueError(_("\"%s\" configuration doesn't exist !!!") % name) self.instances.configurations.remove(configuration) setattr(cls, "removeconfiguration", removeconfiguration) - + resources_xpath = PLCOpen_XPath( "ppx:instances/ppx:configurations/ppx:configuration[@name=$configname]/ppx:resource[@name=$name]") + def getconfigurationResource(self, config_name, name): resources = resources_xpath(self, configname=config_name, name=name) if len(resources) == 1: @@ -488,8 +553,10 @@ def addconfigurationResource(self, config_name, name): if self.getconfigurationResource(config_name, name) is not None: - msg = _("\"{a1}\" resource already exists in \"{a2}\" configuration !!!").format(a1 = name, a2 = config_name) - raise ValueError, msg + raise ValueError( + _("\"{a1}\" resource already exists in \"{a2}\" configuration !!!"). + format(a1=name, a2=config_name)) + configuration = self.getconfiguration(config_name) if configuration is not None: new_resource = PLCOpenParser.CreateElement("resource", "configuration") @@ -506,8 +573,10 @@ configuration.remove(resource) found = True if not found: - msg = _("\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!").format(a1 = name, a2 = config_name) - raise ValueError, msg + raise ValueError( + _("\"{a1}\" resource doesn't exist in \"{a2}\" configuration !!!"). + format(a1=name, a2=config_name)) + setattr(cls, "removeconfigurationResource", removeconfigurationResource) def updateElementName(self, old_name, new_name): @@ -544,24 +613,33 @@ enumerated_values_xpath = PLCOpen_XPath( "ppx:types/ppx:dataTypes/ppx:dataType/ppx:baseType/ppx:enum/ppx:values/ppx:value") + def GetEnumeratedDataTypeValues(self): return [value.getname() for value in enumerated_values_xpath(self)] setattr(cls, "GetEnumeratedDataTypeValues", GetEnumeratedDataTypeValues) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos result = self.types.Search(criteria, parent_infos) for configuration in self.instances.configurations.getconfiguration(): result.extend(configuration.Search(criteria, parent_infos)) return result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("contentHeader", "project") -if cls: - + +cls = PLCOpenParser.GetElementClass("project") +if cls: + _updateProjectClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateContentHeaderProjectClass(cls): def setpageSize(self, width, height): self.coordinateInfo.setpageSize(width, height) setattr(cls, "setpageSize", setpageSize) - + def getpageSize(self): return self.coordinateInfo.getpageSize() setattr(cls, "getpageSize", getpageSize) @@ -570,7 +648,7 @@ for language, (x, y) in scaling.items(): self.coordinateInfo.setscaling(language, x, y) setattr(cls, "setscaling", setscaling) - + def getscaling(self): scaling = {} scaling["FBD"] = self.coordinateInfo.getscaling("FBD") @@ -579,8 +657,16 @@ return scaling setattr(cls, "getscaling", getscaling) -cls = PLCOpenParser.GetElementClass("coordinateInfo", "contentHeader") -if cls: + +cls = PLCOpenParser.GetElementClass("contentHeader", "project") +if cls: + _updateContentHeaderProjectClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateCoordinateInfoContentHeaderClass(cls): def setpageSize(self, width, height): if width == 0 and height == 0: self.deletepageSize() @@ -590,7 +676,7 @@ self.pageSize.setx(width) self.pageSize.sety(height) setattr(cls, "setpageSize", setpageSize) - + def getpageSize(self): if self.pageSize is not None: return self.pageSize.getx(), self.pageSize.gety() @@ -608,7 +694,7 @@ self.sfc.scaling.setx(x) self.sfc.scaling.sety(y) setattr(cls, "setscaling", setscaling) - + def getscaling(self, language): if language == "FBD": return self.fbd.scaling.getx(), self.fbd.scaling.gety() @@ -619,6 +705,15 @@ return 0, 0 setattr(cls, "getscaling", getscaling) + +cls = PLCOpenParser.GetElementClass("coordinateInfo", "contentHeader") +if cls: + _updateCoordinateInfoContentHeaderClass(cls) + + +# ---------------------------------------------------------------------- + + def _Search(attributes, criteria, parent_infos): search_result = [] for attr, value in attributes: @@ -626,6 +721,7 @@ search_result.extend([(tuple(parent_infos + [attr]),) + result for result in TestTextElement(value, criteria)]) return search_result + def _updateConfigurationResourceElementName(self, old_name, new_name): for varlist in self.getglobalVars(): for var in varlist.getvariable(): @@ -636,6 +732,7 @@ if TextMatched(var.getname(), old_name): var.setname(new_name) + def _updateConfigurationResourceElementAddress(self, address_model, new_leading): for varlist in self.getglobalVars(): for var in varlist.getvariable(): @@ -643,6 +740,7 @@ if var_address is not None: var.setaddress(update_address(var_address, address_model, new_leading)) + def _removeConfigurationResourceVariableByAddress(self, address): for varlist in self.getglobalVars(): variables = varlist.getvariable() @@ -650,6 +748,7 @@ if variables[i].getaddress() == address: variables.remove(variables[i]) + def _removeConfigurationResourceVariableByFilter(self, address_model): for varlist in self.getglobalVars(): variables = varlist.getvariable() @@ -660,7 +759,9 @@ if result is not None: variables.remove(variables[i]) -def _SearchInConfigurationResource(self, criteria, parent_infos=[]): + +def _SearchInConfigurationResource(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = _Search([("name", self.getname())], criteria, parent_infos) var_number = 0 for varlist in self.getglobalVars(): @@ -678,9 +779,11 @@ var_number += 1 return search_result -cls = PLCOpenParser.GetElementClass("configuration", "configurations") -if cls: - + +# ---------------------------------------------------------------------- + + +def _updateConfigurationConfigurationsClass(cls): def addglobalVar(self, var_type, name, location="", description=""): globalvars = self.getglobalVars() if len(globalvars) == 0: @@ -696,7 +799,7 @@ var.setdocumentation(ft) globalvars[-1].appendvariable(var) setattr(cls, "addglobalVar", addglobalVar) - + def updateElementName(self, old_name, new_name): _updateConfigurationResourceElementName(self, old_name, new_name) for resource in self.getresource(): @@ -712,7 +815,8 @@ setattr(cls, "removeVariableByAddress", _removeConfigurationResourceVariableByAddress) setattr(cls, "removeVariableByFilter", _removeConfigurationResourceVariableByFilter) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = [] parent_infos = parent_infos + ["C::%s" % self.getname()] filter = criteria["filter"] @@ -722,9 +826,17 @@ search_result.extend(resource.Search(criteria, parent_infos)) return search_result setattr(cls, "Search", Search) - -cls = PLCOpenParser.GetElementClass("resource", "configuration") -if cls: + + +cls = PLCOpenParser.GetElementClass("configuration", "configurations") +if cls: + _updateConfigurationConfigurationsClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateResourceConfigurationClass(cls): def updateElementName(self, old_name, new_name): _updateConfigurationResourceElementName(self, old_name, new_name) for instance in self.getpouInstance(): @@ -742,7 +854,8 @@ setattr(cls, "removeVariableByAddress", _removeConfigurationResourceVariableByAddress) setattr(cls, "removeVariableByFilter", _removeConfigurationResourceVariableByFilter) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos parent_infos = parent_infos[:-1] + ["R::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] search_result = _SearchInConfigurationResource(self, criteria, parent_infos) task_number = 0 @@ -764,8 +877,16 @@ return search_result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("task", "resource") -if cls: + +cls = PLCOpenParser.GetElementClass("resource", "configuration") +if cls: + _updateResourceConfigurationClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateTaskResourceClass(cls): def updateElementName(self, old_name, new_name): if TextMatched(self.single, old_name): self.single = new_name @@ -782,28 +903,46 @@ self.interval = update_address(self.interval, address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def Search(self, criteria, parent_infos=[]): - return _Search([("single", self.getsingle()), + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos + return _Search([("single", self.getsingle()), ("interval", self.getinterval()), ("priority", str(self.getpriority()))], criteria, parent_infos) setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("pouInstance") -if cls: + +cls = PLCOpenParser.GetElementClass("task", "resource") +if cls: + _updateTaskResourceClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updatePouInstanceClass(cls): def updateElementName(self, old_name, new_name): if TextMatched(self.typeName, old_name): self.typeName = new_name setattr(cls, "updateElementName", updateElementName) - def Search(self, criteria, parent_infos=[]): - return _Search([("name", self.getname()), + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos + return _Search([("name", self.getname()), ("type", self.gettypeName())], criteria, parent_infos) setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("variable", "varListPlain") -if cls: + +cls = PLCOpenParser.GetElementClass("pouInstance") +if cls: + _updatePouInstanceClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateVariableVarListPlain(cls): def gettypeAsText(self): vartype_content = self.gettype().getcontent() vartype_content_name = vartype_content.getLocalTag() @@ -817,22 +956,23 @@ elif vartype_content_name == "array": base_type = vartype_content.baseType.getcontent() base_type_name = base_type.getLocalTag() - # Array derived directly from a user defined type + # Array derived directly from a user defined type if base_type_name == "derived": basetype_name = base_type.getname() - # Array derived directly from a string type + # Array derived directly from a string type elif base_type_name in ["string", "wstring"]: basetype_name = base_type_name.upper() - # Array derived directly from an elementary type + # Array derived directly from an elementary type else: basetype_name = base_type_name - return "ARRAY [%s] OF %s" % (",".join(map(lambda x : "%s..%s" % (x.getlower(), x.getupper()), vartype_content.getdimension())), basetype_name) + return "ARRAY [%s] OF %s" % (",".join(map(lambda x: "%s..%s" % (x.getlower(), x.getupper()), vartype_content.getdimension())), basetype_name) # Variable type is an elementary type return vartype_content_name setattr(cls, "gettypeAsText", gettypeAsText) - - def Search(self, criteria, parent_infos=[]): - search_result = _Search([("name", self.getname()), + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos + search_result = _Search([("name", self.getname()), ("type", self.gettypeAsText()), ("location", self.getaddress())], criteria, parent_infos) @@ -845,12 +985,20 @@ return search_result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("types", "project") -if cls: + +cls = PLCOpenParser.GetElementClass("variable", "varListPlain") +if cls: + _updateVariableVarListPlain(cls) + + +# ---------------------------------------------------------------------- + + +def _updateTypesProjectClass(cls): def getdataTypeElements(self): return self.dataTypes.getdataType() setattr(cls, "getdataTypeElements", getdataTypeElements) - + def getdataTypeElement(self, name): elements = self.dataTypes.getdataType() for element in elements: @@ -865,11 +1013,11 @@ new_datatype.setname(name) new_datatype.baseType.setcontent(PLCOpenParser.CreateElement("BOOL", "dataType")) setattr(cls, "appenddataTypeElement", appenddataTypeElement) - + def insertdataTypeElement(self, index, dataType): self.dataTypes.insertdataType(index, dataType) setattr(cls, "insertdataTypeElement", insertdataTypeElement) - + def removedataTypeElement(self, name): found = False for element in self.dataTypes.getdataType(): @@ -878,13 +1026,13 @@ found = True break if not found: - raise ValueError, _("\"%s\" Data Type doesn't exist !!!")%name + raise ValueError(_("\"%s\" Data Type doesn't exist !!!") % name) setattr(cls, "removedataTypeElement", removedataTypeElement) - + def getpouElements(self): return self.pous.getpou() setattr(cls, "getpouElements", getpouElements) - + def getpouElement(self, name): elements = self.pous.getpou() for element in elements: @@ -896,7 +1044,7 @@ def appendpouElement(self, name, pou_type, body_type): for element in self.pous.getpou(): if TextMatched(element.getname(), name): - raise ValueError, _("\"%s\" POU already exists !!!")%name + raise ValueError(_("\"%s\" POU already exists !!!") % name) new_pou = PLCOpenParser.CreateElement("pou", "pous") self.pous.appendpou(new_pou) new_pou.setname(name) @@ -904,11 +1052,11 @@ new_pou.appendbody(PLCOpenParser.CreateElement("body", "pou")) new_pou.setbodyType(body_type) setattr(cls, "appendpouElement", appendpouElement) - + def insertpouElement(self, index, pou): self.pous.insertpou(index, pou) setattr(cls, "insertpouElement", insertpouElement) - + def removepouElement(self, name): found = False for element in self.pous.getpou(): @@ -917,12 +1065,12 @@ found = True break if not found: - raise ValueError, _("\"%s\" POU doesn't exist !!!")%name + raise ValueError(_("\"%s\" POU doesn't exist !!!") % name) setattr(cls, "removepouElement", removepouElement) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = [] - filter = criteria["filter"] for datatype in self.dataTypes.getdataType(): search_result.extend(datatype.Search(criteria, parent_infos)) for pou in self.pous.getpou(): @@ -930,14 +1078,24 @@ return search_result setattr(cls, "Search", Search) + +cls = PLCOpenParser.GetElementClass("types", "project") +if cls: + _updateTypesProjectClass(cls) + + +# ---------------------------------------------------------------------- + + def _updateBaseTypeElementName(self, old_name, new_name): self.baseType.updateElementName(old_name, new_name) -cls = PLCOpenParser.GetElementClass("dataType", "dataTypes") -if cls: + +def _updateDataTypeDataTypesClass(cls): setattr(cls, "updateElementName", _updateBaseTypeElementName) - - def Search(self, criteria, parent_infos=[]): + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = [] filter = criteria["filter"] if filter == "all" or "datatype" in filter: @@ -949,19 +1107,27 @@ return search_result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("dataType") -if cls: - + +cls = PLCOpenParser.GetElementClass("dataType", "dataTypes") +if cls: + _updateDataTypeDataTypesClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateDataTypeClass(cls): def updateElementName(self, old_name, new_name): content_name = self.content.getLocalTag() if content_name in ["derived", "array", "subrangeSigned", "subrangeUnsigned"]: self.content.updateElementName(old_name, new_name) elif content_name == "struct": for element in self.content.getvariable(): - element_type = element.type.updateElementName(old_name, new_name) + element.type.updateElementName(old_name, new_name) setattr(cls, "updateElementName", updateElementName) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = [] content_name = self.content.getLocalTag() if content_name in ["derived", "array", "enum", "subrangeSigned", "subrangeUnsigned"]: @@ -976,22 +1142,40 @@ return search_result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("derived", "dataType") -if cls: + +cls = PLCOpenParser.GetElementClass("dataType") +if cls: + _updateDataTypeClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateDerivedDataTypeClass(cls): def updateElementName(self, old_name, new_name): if TextMatched(self.name, old_name): self.name = new_name setattr(cls, "updateElementName", updateElementName) - - def Search(self, criteria, parent_infos=[]): + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos return [(tuple(parent_infos),) + result for result in TestTextElement(self.name, criteria)] setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("array", "dataType") -if cls: + +cls = PLCOpenParser.GetElementClass("derived", "dataType") +if cls: + _updateDerivedDataTypeClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateArrayDataTypeClass(cls): setattr(cls, "updateElementName", _updateBaseTypeElementName) - - def Search(self, criteria, parent_infos=[]): + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = self.baseType.Search(criteria, parent_infos) for i, dimension in enumerate(self.getdimension()): search_result.extend(_Search([("lower", dimension.getlower()), @@ -1000,32 +1184,51 @@ return search_result setattr(cls, "Search", Search) -def _SearchInSubrange(self, criteria, parent_infos=[]): + +cls = PLCOpenParser.GetElementClass("array", "dataType") +if cls: + _updateArrayDataTypeClass(cls) + + +# ---------------------------------------------------------------------- + + +def _SearchInSubrange(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = self.baseType.Search(criteria, parent_infos) search_result.extend(_Search([("lower", self.range.getlower()), ("upper", self.range.getupper())], criteria, parent_infos)) return search_result + cls = PLCOpenParser.GetElementClass("subrangeSigned", "dataType") if cls: setattr(cls, "updateElementName", _updateBaseTypeElementName) setattr(cls, "Search", _SearchInSubrange) + +# ---------------------------------------------------------------------- + + cls = PLCOpenParser.GetElementClass("subrangeUnsigned", "dataType") if cls: setattr(cls, "updateElementName", _updateBaseTypeElementName) setattr(cls, "Search", _SearchInSubrange) -cls = PLCOpenParser.GetElementClass("enum", "dataType") -if cls: - + +# ---------------------------------------------------------------------- + + +def _updateEnumDataTypeClass(cls): def updateElementName(self, old_name, new_name): pass setattr(cls, "updateElementName", updateElementName) - + enumerated_datatype_values_xpath = PLCOpen_XPath("ppx:values/ppx:value") - def Search(self, criteria, parent_infos=[]): + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = [] for i, value in enumerate(enumerated_datatype_values_xpath(self)): for result in TestTextElement(value.getname(), criteria): @@ -1033,28 +1236,32 @@ return search_result setattr(cls, "Search", Search) + +cls = PLCOpenParser.GetElementClass("enum", "dataType") +if cls: + _updateEnumDataTypeClass(cls) + + +# ---------------------------------------------------------------------- + + def _getvariableTypeinfos(variable_type): type_content = variable_type.getcontent() type_content_type = type_content.getLocalTag() if type_content_type == "derived": return type_content.getname() return type_content_type.upper() - -cls = PLCOpenParser.GetElementClass("pou", "pous") -if cls: - - block_inputs_xpath = PLCOpen_XPath( - "ppx:interface/*[self::ppx:inputVars or self::ppx:inOutVars]/ppx:variable") - block_outputs_xpath = PLCOpen_XPath( - "ppx:interface/*[self::ppx:outputVars or self::ppx:inOutVars]/ppx:variable") - def getblockInfos(self): + + +def _updatePouPousClass(cls): + def getblockInfos(self): block_infos = { - "name" : self.getname(), - "type" : self.getpouType(), - "extensible" : False, - "inputs" : [], - "outputs" : [], - "comment" : self.getdescription()} + "name": self.getname(), + "type": self.getpouType(), + "extensible": False, + "inputs": [], + "outputs": [], + "comment": self.getdescription()} if self.interface is not None: return_type = self.interface.getreturnType() if return_type is not None: @@ -1066,15 +1273,15 @@ block_infos["outputs"].extend( [(var.getname(), _getvariableTypeinfos(var.type), "none") for var in block_outputs_xpath(self)]) - - block_infos["usage"] = ("\n (%s) => (%s)" % - (", ".join(["%s:%s" % (input[1], input[0]) - for input in block_infos["inputs"]]), - ", ".join(["%s:%s" % (output[1], output[0]) - for output in block_infos["outputs"]]))) + + block_infos["usage"] = ("\n (%s) => (%s)" % + (", ".join(["%s:%s" % (input[1], input[0]) + for input in block_infos["inputs"]]), + ", ".join(["%s:%s" % (output[1], output[0]) + for output in block_infos["outputs"]]))) return block_infos setattr(cls, "getblockInfos", getblockInfos) - + def setdescription(self, description): doc = self.getdocumentation() if doc is None: @@ -1082,81 +1289,81 @@ self.setdocumentation(doc) doc.setanyText(description) setattr(cls, "setdescription", setdescription) - + def getdescription(self): doc = self.getdocumentation() if doc is not None: return doc.getanyText() return "" setattr(cls, "getdescription", getdescription) - + def setbodyType(self, body_type): if len(self.body) > 0: if body_type in ["IL", "ST", "LD", "FBD", "SFC"]: self.body[0].setcontent(PLCOpenParser.CreateElement(body_type, "body")) else: - raise ValueError, "%s isn't a valid body type!"%type + raise ValueError("%s isn't a valid body type!" % type) setattr(cls, "setbodyType", setbodyType) - + def getbodyType(self): if len(self.body) > 0: return self.body[0].getcontent().getLocalTag() setattr(cls, "getbodyType", getbodyType) - + def resetexecutionOrder(self): if len(self.body) > 0: self.body[0].resetexecutionOrder() setattr(cls, "resetexecutionOrder", resetexecutionOrder) - + def compileexecutionOrder(self): if len(self.body) > 0: self.body[0].compileexecutionOrder() setattr(cls, "compileexecutionOrder", compileexecutionOrder) - + def setelementExecutionOrder(self, instance, new_executionOrder): if len(self.body) > 0: self.body[0].setelementExecutionOrder(instance, new_executionOrder) setattr(cls, "setelementExecutionOrder", setelementExecutionOrder) - + def addinstance(self, instance): if len(self.body) > 0: self.body[0].appendcontentInstance(instance) setattr(cls, "addinstance", addinstance) - + def getinstances(self): if len(self.body) > 0: return self.body[0].getcontentInstances() return [] setattr(cls, "getinstances", getinstances) - + def getinstance(self, id): if len(self.body) > 0: return self.body[0].getcontentInstance(id) return None setattr(cls, "getinstance", getinstance) - + def getinstancesIds(self): if len(self.body) > 0: return self.body[0].getcontentInstancesIds() return [] setattr(cls, "getinstancesIds", getinstancesIds) - + def getinstanceByName(self, name): if len(self.body) > 0: return self.body[0].getcontentInstanceByName(name) return None setattr(cls, "getinstanceByName", getinstanceByName) - + def removeinstance(self, id): if len(self.body) > 0: self.body[0].removecontentInstance(id) setattr(cls, "removeinstance", removeinstance) - + def settext(self, text): if len(self.body) > 0: self.body[0].settext(text) setattr(cls, "settext", settext) - + def gettext(self): if len(self.body) > 0: return self.body[0].gettext() @@ -1173,17 +1380,17 @@ vars.append((reverse_types[varlist.getLocalTag()], varlist)) return vars setattr(cls, "getvars", getvars) - + def setvars(self, vars): if self.interface is None: self.interface = PLCOpenParser.CreateElement("interface", "pou") self.interface.setcontent(vars) setattr(cls, "setvars", setvars) - + def addpouExternalVar(self, var_type, name): self.addpouVar(var_type, name, "externalVars") setattr(cls, "addpouExternalVar", addpouExternalVar) - + def addpouVar(self, var_type, name, var_class="localVars", location="", description="", initval=""): if self.interface is None: self.interface = PLCOpenParser.CreateElement("interface", "pou") @@ -1213,11 +1420,11 @@ el = PLCOpenParser.CreateElement("initialValue", "variable") el.setvalue(initval) var.setinitialValue(el) - + varlist.appendvariable(var) setattr(cls, "addpouVar", addpouVar) setattr(cls, "addpouLocalVar", addpouVar) - + def changepouVar(self, old_type, old_name, new_type, new_name): if self.interface is not None: content = self.interface.getcontent() @@ -1231,7 +1438,7 @@ vartype_content.setname(new_type) return setattr(cls, "changepouVar", changepouVar) - + def removepouVar(self, var_type, name): if self.interface is not None: content = self.interface.getcontent() @@ -1250,15 +1457,16 @@ if self.getbodyType() in ["SFC"]: for instance in self.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("step", "sfcObjects")) and TextMatched(instance.getname(), name): - return True + return True return False setattr(cls, "hasstep", hasstep) - + def hasblock(self, name=None, block_type=None): if self.getbodyType() in ["FBD", "LD", "SFC"]: for instance in self.getinstances(): - if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and - (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))): + if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) \ + and (TextMatched(instance.getinstanceName(), name) or + TextMatched(instance.gettypeName(), block_type)): return True if self.transitions: for transition in self.transitions.gettransition(): @@ -1274,7 +1482,7 @@ return self.body[0].hasblock(block_type) return False setattr(cls, "hasblock", hasblock) - + def addtransition(self, name, body_type): if self.transitions is None: self.addtransitions() @@ -1286,7 +1494,7 @@ if body_type == "ST": transition.settext(":= ;") setattr(cls, "addtransition", addtransition) - + def gettransition(self, name): if self.transitions is not None: for transition in self.transitions.gettransition(): @@ -1294,13 +1502,13 @@ return transition return None setattr(cls, "gettransition", gettransition) - + def gettransitionList(self): if self.transitions is not None: return self.transitions.gettransition() return [] setattr(cls, "gettransitionList", gettransitionList) - + def removetransition(self, name): if self.transitions is not None: removed = False @@ -1309,13 +1517,13 @@ if transition.getbodyType() in ["FBD", "LD", "SFC"]: for instance in transition.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")): - self.removepouVar(instance.gettypeName(), + self.removepouVar(instance.gettypeName(), instance.getinstanceName()) self.transitions.remove(transition) removed = True break if not removed: - raise ValueError, _("Transition with name %s doesn't exist!")%name + raise ValueError(_("Transition with name %s doesn't exist!") % name) setattr(cls, "removetransition", removetransition) def addaction(self, name, body_type): @@ -1327,7 +1535,7 @@ action.setname(name) action.setbodyType(body_type) setattr(cls, "addaction", addaction) - + def getaction(self, name): if self.actions is not None: for action in self.actions.getaction(): @@ -1335,13 +1543,13 @@ return action return None setattr(cls, "getaction", getaction) - + def getactionList(self): if self.actions is not None: return self.actions.getaction() return [] setattr(cls, "getactionList", getactionList) - + def removeaction(self, name): if self.actions is not None: removed = False @@ -1350,13 +1558,13 @@ if action.getbodyType() in ["FBD", "LD", "SFC"]: for instance in action.getinstances(): if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")): - self.removepouVar(instance.gettypeName(), + self.removepouVar(instance.gettypeName(), instance.getinstanceName()) self.actions.remove(action) removed = True break if not removed: - raise ValueError, _("Action with name %s doesn't exist!")%name + raise ValueError(_("Action with name %s doesn't exist!") % name) setattr(cls, "removeaction", removeaction) def updateElementName(self, old_name, new_name): @@ -1412,8 +1620,9 @@ if result is not None: content.remove(variable) setattr(cls, "removeVariableByFilter", removeVariableByFilter) - - def Search(self, criteria, parent_infos=[]): + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos search_result = [] filter = criteria["filter"] if filter == "all" or self.getpouType() in filter: @@ -1444,67 +1653,96 @@ return search_result setattr(cls, "Search", Search) + +cls = PLCOpenParser.GetElementClass("pou", "pous") +if cls: + block_inputs_xpath = PLCOpen_XPath( + "ppx:interface/*[self::ppx:inputVars or self::ppx:inOutVars]/ppx:variable") + block_outputs_xpath = PLCOpen_XPath( + "ppx:interface/*[self::ppx:outputVars or self::ppx:inOutVars]/ppx:variable") + _updatePouPousClass(cls) + + +# ---------------------------------------------------------------------- + + def setbodyType(self, body_type): if body_type in ["IL", "ST", "LD", "FBD", "SFC"]: self.body.setcontent(PLCOpenParser.CreateElement(body_type, "body")) else: - raise ValueError, "%s isn't a valid body type!"%type + raise ValueError("%s isn't a valid body type!" % type) + def getbodyType(self): return self.body.getcontent().getLocalTag() + def resetexecutionOrder(self): self.body.resetexecutionOrder() + def compileexecutionOrder(self): self.body.compileexecutionOrder() + def setelementExecutionOrder(self, instance, new_executionOrder): self.body.setelementExecutionOrder(instance, new_executionOrder) + def addinstance(self, instance): self.body.appendcontentInstance(instance) + def getinstances(self): return self.body.getcontentInstances() + def getinstance(self, id): return self.body.getcontentInstance(id) + def getrandomInstance(self, exclude): return self.body.getcontentRandomInstance(exclude) + def getinstanceByName(self, name): return self.body.getcontentInstanceByName(name) + def removeinstance(self, id): self.body.removecontentInstance(id) + def settext(self, text): self.body.settext(text) + def gettext(self): return self.body.gettext() + def hasblock(self, name=None, block_type=None): if self.getbodyType() in ["FBD", "LD", "SFC"]: for instance in self.getinstances(): - if (isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and - (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type))): + if isinstance(instance, PLCOpenParser.GetElementClass("block", "fbdObjects")) and \ + (TextMatched(instance.getinstanceName(), name) or TextMatched(instance.gettypeName(), block_type)): return True elif block_type is not None: return self.body.hasblock(block_type) return False + def updateElementName(self, old_name, new_name): self.body.updateElementName(old_name, new_name) + def updateElementAddress(self, address_model, new_leading): self.body.updateElementAddress(address_model, new_leading) - - -cls = PLCOpenParser.GetElementClass("transition", "transitions") -if cls: + + +# ---------------------------------------------------------------------- + +def _updateTransitionTransitionsClass(cls): setattr(cls, "setbodyType", setbodyType) setattr(cls, "getbodyType", getbodyType) setattr(cls, "resetexecutionOrder", resetexecutionOrder) @@ -1521,7 +1759,7 @@ setattr(cls, "hasblock", hasblock) setattr(cls, "updateElementName", updateElementName) setattr(cls, "updateElementAddress", updateElementAddress) - + def Search(self, criteria, parent_infos): search_result = [] parent_infos = parent_infos[:-1] + ["T::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] @@ -1531,8 +1769,16 @@ return search_result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("action", "actions") -if cls: + +cls = PLCOpenParser.GetElementClass("transition", "transitions") +if cls: + _updateTransitionTransitionsClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateActionActionsClass(cls): setattr(cls, "setbodyType", setbodyType) setattr(cls, "getbodyType", getbodyType) setattr(cls, "resetexecutionOrder", resetexecutionOrder) @@ -1549,7 +1795,7 @@ setattr(cls, "hasblock", hasblock) setattr(cls, "updateElementName", updateElementName) setattr(cls, "updateElementAddress", updateElementAddress) - + def Search(self, criteria, parent_infos): search_result = [] parent_infos = parent_infos[:-1] + ["A::%s::%s" % (parent_infos[-1].split("::")[1], self.getname())] @@ -1559,31 +1805,40 @@ return search_result setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("body") -if cls: + +cls = PLCOpenParser.GetElementClass("action", "actions") +if cls: + _updateActionActionsClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateBodyClass(cls): cls.currentExecutionOrderId = 0 cls.checkedBlocksDict = {} + def resetcurrentExecutionOrderId(self): object.__setattr__(self, "currentExecutionOrderId", 0) setattr(cls, "resetcurrentExecutionOrderId", resetcurrentExecutionOrderId) - + def getnewExecutionOrderId(self): object.__setattr__(self, "currentExecutionOrderId", self.currentExecutionOrderId + 1) return self.currentExecutionOrderId setattr(cls, "getnewExecutionOrderId", getnewExecutionOrderId) - + def resetexecutionOrder(self): if self.content.getLocalTag() == "FBD": for element in self.content.getcontent(): - if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"), - PLCOpenParser.GetElementClass("connector", "commonObjects"), + if not isinstance(element, (PLCOpenParser.GetElementClass("comment", "commonObjects"), + PLCOpenParser.GetElementClass("connector", "commonObjects"), PLCOpenParser.GetElementClass("continuation", "commonObjects"))): element.setexecutionOrderId(0) self.checkedBlocksDict.clear() else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "resetexecutionOrder", resetexecutionOrder) - + def compileexecutionOrder(self): if self.content.getLocalTag() == "FBD": self.resetexecutionOrder() @@ -1595,9 +1850,9 @@ self.compileelementExecutionOrder(connections[0]) element.setexecutionOrderId(self.getnewExecutionOrderId()) else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "compileexecutionOrder", compileexecutionOrder) - + def compileelementExecutionOrder(self, link): if self.content.getLocalTag() == "FBD": localid = link.getrefLocalId() @@ -1607,21 +1862,22 @@ for variable in instance.inputVariables.getvariable(): connections = variable.connectionPointIn.getconnections() if connections and len(connections) == 1: - if (self.checkedBlocksDict.has_key(connections[0].getrefLocalId()) == False): + if not connections[0].getrefLocalId() in self.checkedBlocksDict: self.compileelementExecutionOrder(connections[0]) if instance.getexecutionOrderId() == 0: instance.setexecutionOrderId(self.getnewExecutionOrderId()) elif isinstance(instance, PLCOpenParser.GetElementClass("continuation", "commonObjects")) and instance.getexecutionOrderId() == 0: for tmp_instance in self.getcontentInstances(): - if (isinstance(tmp_instance, PLCOpenParser.GetElementClass("connector", "commonObjects")) and - TextMatched(tmp_instance.getname(), instance.getname()) and tmp_instance.getexecutionOrderId() == 0): + if isinstance(tmp_instance, PLCOpenParser.GetElementClass("connector", "commonObjects")) and \ + TextMatched(tmp_instance.getname(), instance.getname()) and \ + tmp_instance.getexecutionOrderId() == 0: connections = tmp_instance.connectionPointIn.getconnections() if connections and len(connections) == 1: self.compileelementExecutionOrder(connections[0]) else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "compileelementExecutionOrder", compileelementExecutionOrder) - + def setelementExecutionOrder(self, instance, new_executionOrder): if self.content.getLocalTag() == "FBD": old_executionOrder = instance.getexecutionOrderId() @@ -1635,85 +1891,86 @@ element.setexecutionOrderId(element_executionOrder + 1) instance.setexecutionOrderId(new_executionOrder) else: - raise TypeError, _("Can only generate execution order on FBD networks!") + raise TypeError(_("Can only generate execution order on FBD networks!")) setattr(cls, "setelementExecutionOrder", setelementExecutionOrder) - + def appendcontentInstance(self, instance): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: self.content.appendcontent(instance) else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "appendcontentInstance", appendcontentInstance) - + def getcontentInstances(self): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: return self.content.getcontent() else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstances", getcontentInstances) - + instance_by_id_xpath = PLCOpen_XPath("*[@localId=$localId]") instance_by_name_xpath = PLCOpen_XPath("ppx:block[@instanceName=$name]") + def getcontentInstance(self, local_id): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: instance = instance_by_id_xpath(self.content, localId=local_id) if len(instance) > 0: return instance[0] return None else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstance", getcontentInstance) - + def getcontentInstancesIds(self): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: return OrderedDict([(instance.getlocalId(), True) for instance in self.content]) else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstancesIds", getcontentInstancesIds) - + def getcontentInstanceByName(self, name): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: instance = instance_by_name_xpath(self.content) if len(instance) > 0: return instance[0] return None else: - raise TypeError, _("%s body don't have instances!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "getcontentInstanceByName", getcontentInstanceByName) - + def removecontentInstance(self, local_id): - if self.content.getLocalTag() in ["LD","FBD","SFC"]: + if self.content.getLocalTag() in ["LD", "FBD", "SFC"]: instance = instance_by_id_xpath(self.content, localId=local_id) if len(instance) > 0: self.content.remove(instance[0]) else: - raise ValueError, _("Instance with id %d doesn't exist!")%id + raise ValueError(_("Instance with id %d doesn't exist!") % id) else: - raise TypeError, "%s body don't have instances!"%self.content.getLocalTag() + raise TypeError(_("%s body don't have instances!") % self.content.getLocalTag()) setattr(cls, "removecontentInstance", removecontentInstance) - + def settext(self, text): - if self.content.getLocalTag() in ["IL","ST"]: + if self.content.getLocalTag() in ["IL", "ST"]: self.content.setanyText(text) else: - raise TypeError, _("%s body don't have text!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have text!") % self.content.getLocalTag()) setattr(cls, "settext", settext) def gettext(self): - if self.content.getLocalTag() in ["IL","ST"]: + if self.content.getLocalTag() in ["IL", "ST"]: return self.content.getanyText() else: - raise TypeError, _("%s body don't have text!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have text!") % self.content.getLocalTag()) setattr(cls, "gettext", gettext) - + def hasblock(self, block_type): - if self.content.getLocalTag() in ["IL","ST"]: + if self.content.getLocalTag() in ["IL", "ST"]: return self.content.hasblock(block_type) else: - raise TypeError, _("%s body don't have text!")%self.content.getLocalTag() + raise TypeError(_("%s body don't have text!") % self.content.getLocalTag()) setattr(cls, "hasblock", hasblock) - + def updateElementName(self, old_name, new_name): if self.content.getLocalTag() in ["IL", "ST"]: self.content.updateElementName(old_name, new_name) @@ -1730,7 +1987,8 @@ element.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos if self.content.getLocalTag() in ["IL", "ST"]: search_result = self.content.Search(criteria, parent_infos + ["body", 0]) else: @@ -1740,21 +1998,35 @@ return search_result setattr(cls, "Search", Search) + +cls = PLCOpenParser.GetElementClass("body") +if cls: + _updateBodyClass(cls) + + +# ---------------------------------------------------------------------- + + def getx(self): return self.position.getx() + def gety(self): return self.position.gety() + def setx(self, x): self.position.setx(x) - + + def sety(self, y): self.position.sety(y) + def _getBoundingBox(self): return rect(self.getx(), self.gety(), self.getwidth(), self.getheight()) + def _getConnectionsBoundingBox(connectionPointIn): bbox = rect() connections = connectionPointIn.getconnections() @@ -1764,39 +2036,46 @@ bbox.update(x, y) return bbox + def _getBoundingBoxSingle(self): bbox = _getBoundingBox(self) if self.connectionPointIn is not None: bbox.union(_getConnectionsBoundingBox(self.connectionPointIn)) return bbox + def _getBoundingBoxMultiple(self): bbox = _getBoundingBox(self) for connectionPointIn in self.getconnectionPointIn(): bbox.union(_getConnectionsBoundingBox(connectionPointIn)) return bbox + def _filterConnections(connectionPointIn, localId, connections): in_connections = connectionPointIn.getconnections() if in_connections is not None: for connection in in_connections: connected = connection.getrefLocalId() - if not connections.has_key((localId, connected)) and \ - not connections.has_key((connected, localId)): + if not (localId, connected) in connections and \ + not (connected, localId) in connections: connectionPointIn.remove(connection) + def _filterConnectionsSingle(self, connections): if self.connectionPointIn is not None: _filterConnections(self.connectionPointIn, self.localId, connections) + def _filterConnectionsMultiple(self, connections): for connectionPointIn in self.getconnectionPointIn(): _filterConnections(connectionPointIn, self.localId, connections) + def _getconnectionsdefinition(instance, connections_end): local_id = instance.getlocalId() return dict([((local_id, end), True) for end in connections_end]) + def _updateConnectionsId(connectionPointIn, translation): connections_end = [] connections = connectionPointIn.getconnections() @@ -1808,22 +2087,26 @@ connections_end.append(new_reflocalId) return connections_end + def _updateConnectionsIdSingle(self, translation): connections_end = [] if self.connectionPointIn is not None: connections_end = _updateConnectionsId(self.connectionPointIn, translation) return _getconnectionsdefinition(self, connections_end) + def _updateConnectionsIdMultiple(self, translation): connections_end = [] for connectionPointIn in self.getconnectionPointIn(): connections_end.extend(_updateConnectionsId(connectionPointIn, translation)) return _getconnectionsdefinition(self, connections_end) + def _translate(self, dx, dy): self.setx(self.getx() + dx) self.sety(self.gety() + dy) - + + def _translateConnections(connectionPointIn, dx, dy): connections = connectionPointIn.getconnections() if connections is not None: @@ -1832,32 +2115,38 @@ position.setx(position.getx() + dx) position.sety(position.gety() + dy) + def _translateSingle(self, dx, dy): _translate(self, dx, dy) if self.connectionPointIn is not None: _translateConnections(self.connectionPointIn, dx, dy) + def _translateMultiple(self, dx, dy): _translate(self, dx, dy) for connectionPointIn in self.getconnectionPointIn(): _translateConnections(connectionPointIn, dx, dy) + def _updateElementName(self, old_name, new_name): pass + def _updateElementAddress(self, address_model, new_leading): pass -def _SearchInElement(self, criteria, parent_infos=[]): + +def _SearchInElement(self, criteria, parent_infos=None): return [] + _connectionsFunctions = { "bbox": {"none": _getBoundingBox, "single": _getBoundingBoxSingle, "multiple": _getBoundingBoxMultiple}, "translate": {"none": _translate, - "single": _translateSingle, - "multiple": _translateMultiple}, + "single": _translateSingle, + "multiple": _translateMultiple}, "filter": {"none": lambda self, connections: None, "single": _filterConnectionsSingle, "multiple": _filterConnectionsMultiple}, @@ -1866,6 +2155,7 @@ "multiple": _updateConnectionsIdMultiple}, } + def _initElementClass(name, parent, connectionPointInType="none"): cls = PLCOpenParser.GetElementClass(name, parent) if cls: @@ -1882,16 +2172,19 @@ setattr(cls, "Search", _SearchInElement) return cls -cls = _initElementClass("comment", "commonObjects") -if cls: + +# ---------------------------------------------------------------------- + + +def _updateCommentCommonObjectsClass(cls): def setcontentText(self, text): self.content.setanyText(text) setattr(cls, "setcontentText", setcontentText) - + def getcontentText(self): return self.content.getanyText() setattr(cls, "getcontentText", getcontentText) - + def updateElementName(self, old_name, new_name): self.content.updateElementName(old_name, new_name) setattr(cls, "updateElementName", updateElementName) @@ -1900,12 +2193,21 @@ self.content.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos return self.content.Search(criteria, parent_infos + ["comment", self.getlocalId(), "content"]) setattr(cls, "Search", Search) -cls = _initElementClass("block", "fbdObjects") -if cls: + +cls = _initElementClass("comment", "commonObjects") +if cls: + _updateCommentCommonObjectsClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateBlockFbdObjectsClass(cls): def getBoundingBox(self): bbox = _getBoundingBox(self) for input in self.inputVariables.getvariable(): @@ -1936,7 +2238,8 @@ _translateConnections(input.connectionPointIn, dx, dy) setattr(cls, "translate", translate) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos parent_infos = parent_infos + ["block", self.getlocalId()] search_result = _Search([("name", self.getinstanceName()), ("type", self.gettypeName())], @@ -1950,41 +2253,74 @@ return search_result setattr(cls, "Search", Search) + +cls = _initElementClass("block", "fbdObjects") +if cls: + _updateBlockFbdObjectsClass(cls) + + +# ---------------------------------------------------------------------- + + _initElementClass("leftPowerRail", "ldObjects") _initElementClass("rightPowerRail", "ldObjects", "multiple") + def _UpdateLDElementName(self, old_name, new_name): if TextMatched(self.variable, old_name): self.variable = new_name + def _UpdateLDElementAddress(self, address_model, new_leading): self.variable = update_address(self.variable, address_model, new_leading) + def _getSearchInLDElement(ld_element_type): - def SearchInLDElement(self, criteria, parent_infos=[]): + def SearchInLDElement(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos return _Search([("reference", self.variable)], criteria, parent_infos + [ld_element_type, self.getlocalId()]) return SearchInLDElement + +# ---------------------------------------------------------------------- + + cls = _initElementClass("contact", "ldObjects", "single") if cls: setattr(cls, "updateElementName", _UpdateLDElementName) setattr(cls, "updateElementAddress", _UpdateLDElementAddress) setattr(cls, "Search", _getSearchInLDElement("contact")) + +# ---------------------------------------------------------------------- + + cls = _initElementClass("coil", "ldObjects", "single") if cls: setattr(cls, "updateElementName", _UpdateLDElementName) setattr(cls, "updateElementAddress", _UpdateLDElementAddress) setattr(cls, "Search", _getSearchInLDElement("coil")) -cls = _initElementClass("step", "sfcObjects", "single") -if cls: - def Search(self, criteria, parent_infos=[]): + +# ---------------------------------------------------------------------- + + +def _updateStepSfcObjectSingleClass(cls): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos return _Search([("name", self.getname())], criteria, parent_infos + ["step", self.getlocalId()]) setattr(cls, "Search", Search) -cls = _initElementClass("transition", "sfcObjects") -if cls: + +cls = _initElementClass("step", "sfcObjects", "single") +if cls: + _updateStepSfcObjectSingleClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateTransitionSfcObjectsClass(cls): def setconditionContent(self, condition_type, value): if self.condition is None: self.addcondition() @@ -1999,11 +2335,11 @@ condition.setcontent(PLCOpenParser.CreateElement("ST", "inline")) condition.settext(value) setattr(cls, "setconditionContent", setconditionContent) - + def getconditionContent(self): if self.condition is not None: content = self.condition.getcontent() - values = {"type" : content.getLocalTag()} + values = {"type": content.getLocalTag()} if values["type"] == "reference": values["value"] = content.getname() elif values["type"] == "inline": @@ -2030,21 +2366,21 @@ bbox.union(_getConnectionsBoundingBox(condition_connection)) return bbox setattr(cls, "getBoundingBox", getBoundingBox) - + def translate(self, dx, dy): _translateSingle(self, dx, dy) condition_connection = self.getconditionConnection() if condition_connection is not None: _translateConnections(condition_connection, dx, dy) setattr(cls, "translate", translate) - + def filterConnections(self, connections): _filterConnectionsSingle(self, connections) condition_connection = self.getconditionConnection() if condition_connection is not None: _filterConnections(condition_connection, self.localId, connections) setattr(cls, "filterConnections", filterConnections) - + def updateConnectionsId(self, translation): connections_end = [] if self.connectionPointIn is not None: @@ -2082,8 +2418,9 @@ return condition_connection.getconnections() return None setattr(cls, "getconnections", getconnections) - - def Search(self, criteria, parent_infos=[]): + + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos parent_infos = parent_infos + ["transition", self.getlocalId()] search_result = [] content = self.condition.getcontent() @@ -2095,24 +2432,41 @@ return search_result setattr(cls, "Search", Search) + +cls = _initElementClass("transition", "sfcObjects") +if cls: + _updateTransitionSfcObjectsClass(cls) + + +# ---------------------------------------------------------------------- + + _initElementClass("selectionDivergence", "sfcObjects", "single") _initElementClass("selectionConvergence", "sfcObjects", "multiple") _initElementClass("simultaneousDivergence", "sfcObjects", "single") _initElementClass("simultaneousConvergence", "sfcObjects", "multiple") - -cls = _initElementClass("jumpStep", "sfcObjects", "single") -if cls: + + +def _updateJumpStepSfcObjectSingleClass(cls): def Search(self, criteria, parent_infos): return _Search([("target", self.gettargetName())], criteria, parent_infos + ["jump", self.getlocalId()]) setattr(cls, "Search", Search) -cls = PLCOpenParser.GetElementClass("action", "actionBlock") -if cls: + +cls = _initElementClass("jumpStep", "sfcObjects", "single") +if cls: + _updateJumpStepSfcObjectSingleClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateActionActionBlockClass(cls): def setreferenceName(self, name): if self.reference is not None: self.reference.setname(name) setattr(cls, "setreferenceName", setreferenceName) - + def getreferenceName(self): if self.reference is not None: return self.reference.getname() @@ -2124,7 +2478,7 @@ self.inline.setcontent(PLCOpenParser.CreateElement("ST", "inline")) self.inline.settext(content) setattr(cls, "setinlineContent", setinlineContent) - + def getinlineContent(self): if self.inline is not None: return self.inline.gettext() @@ -2145,20 +2499,29 @@ self.inline.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos qualifier = self.getqualifier() if qualifier is None: qualifier = "N" return _Search([("inline", self.getinlineContent()), - ("reference", self.getreferenceName()), + ("reference", self.getreferenceName()), ("qualifier", qualifier), ("duration", self.getduration()), ("indicator", self.getindicator())], criteria, parent_infos) setattr(cls, "Search", Search) -cls = _initElementClass("actionBlock", "commonObjects", "single") -if cls: + +cls = PLCOpenParser.GetElementClass("action", "actionBlock") +if cls: + _updateActionActionBlockClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateActionBlockCommonObjectsSingleClass(cls): def setactions(self, actions): self.action = [] for params in actions: @@ -2210,7 +2573,8 @@ action.updateElementAddress(address_model, new_leading) setattr(cls, "updateElementAddress", updateElementAddress) - def Search(self, criteria, parent_infos=[]): + def Search(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos parent_infos = parent_infos + ["action_block", self.getlocalId()] search_result = [] for idx, action in enumerate(self.action): @@ -2218,16 +2582,29 @@ return search_result setattr(cls, "Search", Search) -def _SearchInIOVariable(self, criteria, parent_infos=[]): + +cls = _initElementClass("actionBlock", "commonObjects", "single") +if cls: + _updateActionBlockCommonObjectsSingleClass(cls) + + +# ---------------------------------------------------------------------- + + +def _SearchInIOVariable(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos return _Search([("expression", self.expression)], criteria, parent_infos + ["io_variable", self.getlocalId()]) + def _UpdateIOElementName(self, old_name, new_name): if TextMatched(self.expression, old_name): self.expression = new_name + def _UpdateIOElementAddress(self, address_model, new_leading): self.expression = update_address(self.expression, address_model, new_leading) + cls = _initElementClass("inVariable", "fbdObjects") if cls: setattr(cls, "updateElementName", _UpdateIOElementName) @@ -2247,11 +2624,15 @@ setattr(cls, "Search", _SearchInIOVariable) -def _SearchInConnector(self, criteria, parent_infos=[]): +def _SearchInConnector(self, criteria, parent_infos=None): + parent_infos = [] if parent_infos is None else parent_infos return _Search([("name", self.getname())], criteria, parent_infos + ["connector", self.getlocalId()]) -cls = _initElementClass("continuation", "commonObjects") -if cls: + +# ---------------------------------------------------------------------- + + +def _updateContinuationCommonObjectsClass(cls): setattr(cls, "Search", _SearchInConnector) def updateElementName(self, old_name, new_name): @@ -2259,8 +2640,16 @@ self.name = new_name setattr(cls, "updateElementName", updateElementName) -cls = _initElementClass("connector", "commonObjects", "single") -if cls: + +cls = _initElementClass("continuation", "commonObjects") +if cls: + _updateContinuationCommonObjectsClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateConnectorCommonObjectsSingleClass(cls): setattr(cls, "Search", _SearchInConnector) def updateElementName(self, old_name, new_name): @@ -2268,8 +2657,16 @@ self.name = new_name setattr(cls, "updateElementName", updateElementName) -cls = PLCOpenParser.GetElementClass("connection") -if cls: + +cls = _initElementClass("connector", "commonObjects", "single") +if cls: + _updateConnectorCommonObjectsSingleClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateConnectionClass(cls): def setpoints(self, points): positions = [] for point in points: @@ -2283,12 +2680,20 @@ def getpoints(self): points = [] for position in self.position: - points.append((position.getx(),position.gety())) + points.append((position.getx(), position.gety())) return points setattr(cls, "getpoints", getpoints) -cls = PLCOpenParser.GetElementClass("connectionPointIn") -if cls: + +cls = PLCOpenParser.GetElementClass("connection") +if cls: + _updateConnectionClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateConnectionPointInClass(cls): def setrelPositionXY(self, x, y): self.relPosition = PLCOpenParser.CreateElement("relPosition", "connectionPointIn") self.relPosition.setx(x) @@ -2313,33 +2718,34 @@ def removeconnections(self): self.content = None setattr(cls, "removeconnections", removeconnections) - + connection_xpath = PLCOpen_XPath("ppx:connection") connection_by_position_xpath = PLCOpen_XPath("ppx:connection[position()=$pos]") + def getconnections(self): return connection_xpath(self) setattr(cls, "getconnections", getconnections) - + def getconnection(self, idx): connection = connection_by_position_xpath(self, pos=idx+1) if len(connection) > 0: return connection[0] return None setattr(cls, "getconnection", getconnection) - + def setconnectionId(self, idx, local_id): connection = self.getconnection(idx) if connection is not None: connection.setrefLocalId(local_id) setattr(cls, "setconnectionId", setconnectionId) - + def getconnectionId(self, idx): connection = self.getconnection(idx) if connection is not None: return connection.getrefLocalId() return None setattr(cls, "getconnectionId", getconnectionId) - + def setconnectionPoints(self, idx, points): connection = self.getconnection(idx) if connection is not None: @@ -2358,7 +2764,7 @@ if connection is not None: connection.setformalParameter(parameter) setattr(cls, "setconnectionParameter", setconnectionParameter) - + def getconnectionParameter(self, idx): connection = self.getconnection(idx) if connection is not None: @@ -2366,6 +2772,7 @@ return None setattr(cls, "getconnectionParameter", getconnectionParameter) + cls = PLCOpenParser.GetElementClass("connectionPointOut") if cls: def setrelPositionXY(self, x, y): @@ -2380,8 +2787,16 @@ return self.relPosition setattr(cls, "getrelPositionXY", getrelPositionXY) -cls = PLCOpenParser.GetElementClass("value") -if cls: + +cls = PLCOpenParser.GetElementClass("connectionPointIn") +if cls: + _updateConnectionPointInClass(cls) + + +# ---------------------------------------------------------------------- + + +def _updateValueClass(cls): def setvalue(self, value): value = value.strip() if value.startswith("[") and value.endswith("]"): @@ -2393,11 +2808,20 @@ content.setvalue(value) self.setcontent(content) setattr(cls, "setvalue", setvalue) - + def getvalue(self): return self.content.getvalue() setattr(cls, "getvalue", getvalue) + +cls = PLCOpenParser.GetElementClass("value") +if cls: + _updateValueClass(cls) + + +# ---------------------------------------------------------------------- + + def extractValues(values): items = values.split(",") i = 1 @@ -2409,13 +2833,11 @@ elif opened == closed: i += 1 else: - raise ValueError, _("\"%s\" is an invalid value!")%value + raise ValueError(_("\"%s\" is an invalid value!") % values) return items -cls = PLCOpenParser.GetElementClass("arrayValue", "value") -if cls: - arrayValue_model = re.compile("([0-9]*)\((.*)\)$") - + +def _updateArrayValueValueClass(cls): def setvalue(self, value): elements = [] for item in extractValues(value[1:-1]): @@ -2431,28 +2853,34 @@ elements.append(element) self.value = elements setattr(cls, "setvalue", setvalue) - + def getvalue(self): values = [] for element in self.value: try: repetition = int(element.getrepetitionValue()) - except: + except Exception: repetition = 1 if repetition > 1: value = element.getvalue() if value is None: value = "" - values.append("%s(%s)"%(repetition, value)) + values.append("%s(%s)" % (repetition, value)) else: values.append(element.getvalue()) - return "[%s]"%", ".join(values) + return "[%s]" % ", ".join(values) setattr(cls, "getvalue", getvalue) -cls = PLCOpenParser.GetElementClass("structValue", "value") -if cls: - structValue_model = re.compile("(.*):=(.*)") - + +cls = PLCOpenParser.GetElementClass("arrayValue", "value") +if cls: + arrayValue_model = re.compile("([0-9]+)\((.*)\)$") + _updateArrayValueValueClass(cls) + +# ---------------------------------------------------------------------- + + +def _updateStructValueValueClass(cls): def setvalue(self, value): elements = [] for item in extractValues(value[1:-1]): @@ -2465,11 +2893,16 @@ elements.append(element) self.value = elements setattr(cls, "setvalue", setvalue) - + def getvalue(self): values = [] for element in self.value: - values.append("%s := %s"%(element.getmember(), element.getvalue())) - return "(%s)"%", ".join(values) + values.append("%s := %s" % (element.getmember(), element.getvalue())) + return "(%s)" % ", ".join(values) setattr(cls, "getvalue", getvalue) + +cls = PLCOpenParser.GetElementClass("structValue", "value") +if cls: + structValue_model = re.compile("(.*):=(.*)") + _updateStructValueValueClass(cls) diff -r c1298e7ffe3a -r 8391c11477f4 plcopen/structures.py --- a/plcopen/structures.py Fri Mar 24 12:07:47 2017 +0000 +++ b/plcopen/structures.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,17 +22,22 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import string, re -from plcopen import LoadProject + +from __future__ import absolute_import +import string +import re from collections import OrderedDict -from definitions import * + +from plcopen.plcopen import LoadProject +from plcopen.definitions import * TypeHierarchy = dict(TypeHierarchy_list) -""" -returns true if the given data type is the same that "reference" meta-type or one of its types. -""" + def IsOfType(type, reference): + """ + Returns true if the given data type is the same that "reference" meta-type or one of its types. + """ if reference is None: return True elif type == reference: @@ -43,11 +48,13 @@ return IsOfType(parent_type, reference) return False -""" -returns list of all types that correspont to the ANY* meta type -""" + def GetSubTypes(type): - return [typename for typename, parenttype in TypeHierarchy.items() if not typename.startswith("ANY") and IsOfType(typename, type)] + """ + Returns list of all types that correspont to the ANY* meta type + """ + return [typename for typename, _parenttype in TypeHierarchy.items() if not typename.startswith("ANY") and IsOfType(typename, type)] + DataTypeRange = dict(DataTypeRange_list) @@ -67,76 +74,82 @@ - The default modifier which can be "none", "negated", "rising" or "falling" """ -StdBlckLibs = {libname : LoadProject(tc6fname)[0] - for libname, tc6fname in StdTC6Libs} -StdBlckLst = [{"name" : libname, "list": +StdBlckLibs = {libname: LoadProject(tc6fname)[0] + for libname, tc6fname in StdTC6Libs} +StdBlckLst = [{"name": libname, "list": [GetBlockInfos(pous) for pous in lib.getpous()]} - for libname, lib in StdBlckLibs.iteritems()] - -#------------------------------------------------------------------------------- + for libname, lib in StdBlckLibs.iteritems()] + +# ------------------------------------------------------------------------------- # Test identifier -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- IDENTIFIER_MODEL = re.compile( "(?:%(letter)s|_(?:%(letter)s|%(digit)s))(?:_?(?:%(letter)s|%(digit)s))*$" % {"letter": "[a-zA-Z]", "digit": "[0-9]"}) -# Test if identifier is valid + def TestIdentifier(identifier): - return IDENTIFIER_MODEL.match(identifier) is not None - -#------------------------------------------------------------------------------- + """ + Test if identifier is valid + """ + return IDENTIFIER_MODEL.match(identifier) is not None + +# ------------------------------------------------------------------------------- # Standard functions list generation -#------------------------------------------------------------------------------- - - -""" -take a .csv file and translate it it a "csv_table" -""" +# ------------------------------------------------------------------------------- + + def csv_file_to_table(file): - return [ map(string.strip,line.split(';')) for line in file.xreadlines()] - -""" -seek into the csv table to a section ( section_name match 1st field ) -return the matching row without first field -""" + """ + take a .csv file and translate it it a "csv_table" + """ + return [map(string.strip, line.split(';')) for line in file.xreadlines()] + + def find_section(section_name, table): + """ + seek into the csv table to a section ( section_name match 1st field ) + return the matching row without first field + """ fields = [None] - while(fields[0] != section_name): + while fields[0] != section_name: fields = table.pop(0) return fields[1:] -""" -extract the standard functions standard parameter names and types... -return a { ParameterName: Type, ...} -""" + def get_standard_funtions_input_variables(table): + """ + extract the standard functions standard parameter names and types... + return a { ParameterName: Type, ...} + """ variables = find_section("Standard_functions_variables_types", table) standard_funtions_input_variables = {} - fields = [True,True] - while(fields[1]): + fields = [True, True] + while fields[1]: fields = table.pop(0) - variable_from_csv = dict([(champ, val) for champ, val in zip(variables, fields[1:]) if champ!='']) + variable_from_csv = dict([(champ, val) for champ, val in zip(variables, fields[1:]) if champ != '']) standard_funtions_input_variables[variable_from_csv['name']] = variable_from_csv['type'] return standard_funtions_input_variables - -""" -translate .csv file input declaration into PLCOpenEditor interessting values -in : "(ANY_NUM, ANY_NUM)" and { ParameterName: Type, ...} -return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")] -""" + + def csv_input_translate(str_decl, variables, base): - decl = str_decl.replace('(','').replace(')','').replace(' ','').split(',') + """ + translate .csv file input declaration into PLCOpenEditor interessting values + in : "(ANY_NUM, ANY_NUM)" and { ParameterName: Type, ...} + return [("IN1","ANY_NUM","none"),("IN2","ANY_NUM","none")] + """ + decl = str_decl.replace('(', '').replace(')', '').replace(' ', '').split(',') params = [] - + len_of_not_predifined_variable = len([True for param_type in decl if param_type not in variables]) - + for param_type in decl: if param_type in variables.keys(): param_name = param_type param_type = variables[param_type] elif len_of_not_predifined_variable > 1: - param_name = "IN%d"%base + param_name = "IN%d" % base base += 1 else: param_name = "IN" @@ -144,10 +157,11 @@ return params -""" -Returns this kind of declaration for all standard functions - - [{"name" : "Numerical", 'list': [ { +def get_standard_funtions(table): + """ + Returns this kind of declaration for all standard functions + + [{"name" : "Numerical", 'list': [ { 'baseinputnumber': 1, 'comment': 'Addition', 'extensible': True, @@ -156,21 +170,20 @@ 'name': 'ADD', 'outputs': [('OUT', 'ANY_NUM', 'none')], 'type': 'function'}, ...... ] },.....] -""" -def get_standard_funtions(table): - + """ + variables = get_standard_funtions_input_variables(table) - - fonctions = find_section("Standard_functions_type",table) + + fonctions = find_section("Standard_functions_type", table) Standard_Functions_Decl = [] Current_section = None - + translate = { - "extensible" : lambda x: {"yes":True, "no":False}[x], - "inputs" : lambda x:csv_input_translate(x,variables,baseinputnumber), - "outputs":lambda x:[("OUT",x,"none")]} - + "extensible": lambda x: {"yes": True, "no": False}[x], + "inputs": lambda x: csv_input_translate(x, variables, baseinputnumber), + "outputs": lambda x: [("OUT", x, "none")]} + for fields in table: if fields[1]: # If function section name given @@ -180,82 +193,82 @@ section_name = words[1] else: section_name = fields[0] - Current_section = {"name" : section_name, "list" : []} + Current_section = {"name": section_name, "list": []} Standard_Functions_Decl.append(Current_section) - Function_decl_list = [] if Current_section: Function_decl = dict([(champ, val) for champ, val in zip(fonctions, fields[1:]) if champ]) - baseinputnumber = int(Function_decl.get("baseinputnumber",1)) + baseinputnumber = int(Function_decl.get("baseinputnumber", 1)) Function_decl["baseinputnumber"] = baseinputnumber for param, value in Function_decl.iteritems(): if param in translate: Function_decl[param] = translate[param](value) Function_decl["type"] = "function" - - if Function_decl["name"].startswith('*') or Function_decl["name"].endswith('*') : + + if Function_decl["name"].startswith('*') or Function_decl["name"].endswith('*'): input_ovrloading_types = GetSubTypes(Function_decl["inputs"][0][1]) output_types = GetSubTypes(Function_decl["outputs"][0][1]) else: input_ovrloading_types = [None] output_types = [None] - + funcdeclname_orig = Function_decl["name"] funcdeclname = Function_decl["name"].strip('*_') fdc = Function_decl["inputs"][:] for intype in input_ovrloading_types: - if intype != None: + if intype is not None: Function_decl["inputs"] = [] for decl_tpl in fdc: if IsOfType(intype, decl_tpl[1]): Function_decl["inputs"] += [(decl_tpl[0], intype, decl_tpl[2])] else: Function_decl["inputs"] += [(decl_tpl)] - + if funcdeclname_orig.startswith('*'): - funcdeclin = intype + '_' + funcdeclname + funcdeclin = intype + '_' + funcdeclname else: funcdeclin = funcdeclname else: funcdeclin = funcdeclname - + for outype in output_types: - if outype != None: + if outype is not None: decl_tpl = Function_decl["outputs"][0] - Function_decl["outputs"] = [ (decl_tpl[0] , outype, decl_tpl[2])] + Function_decl["outputs"] = [(decl_tpl[0], outype, decl_tpl[2])] if funcdeclname_orig.endswith('*'): - funcdeclout = funcdeclin + '_' + outype + funcdeclout = funcdeclin + '_' + outype else: - funcdeclout = funcdeclin + funcdeclout = funcdeclin else: - funcdeclout = funcdeclin + funcdeclout = funcdeclin Function_decl["name"] = funcdeclout # apply filter given in "filter" column filter_name = Function_decl["filter"] store = True - for (InTypes, OutTypes) in ANY_TO_ANY_FILTERS.get(filter_name,[]): - outs = reduce(lambda a,b: a or b, - map(lambda testtype : IsOfType( - Function_decl["outputs"][0][1], - testtype), OutTypes)) - inps = reduce(lambda a,b: a or b, - map(lambda testtype : IsOfType( - Function_decl["inputs"][0][1], - testtype), InTypes)) + for (InTypes, OutTypes) in ANY_TO_ANY_FILTERS.get(filter_name, []): + outs = reduce(lambda a, b: a or b, + map(lambda testtype: IsOfType( + Function_decl["outputs"][0][1], + testtype), OutTypes)) + inps = reduce(lambda a, b: a or b, + map(lambda testtype: IsOfType( + Function_decl["inputs"][0][1], + testtype), InTypes)) if inps and outs and Function_decl["outputs"][0][1] != Function_decl["inputs"][0][1]: store = True break else: store = False - if store : + if store: # create the copy of decl dict to be appended to section Function_decl_copy = Function_decl.copy() Current_section["list"].append(Function_decl_copy) else: raise "First function must be in a category" - + return Standard_Functions_Decl + StdBlckLst.extend(get_standard_funtions(csv_file_to_table(open(StdFuncsCSV)))) # Dictionary to speedup block type fetching by name @@ -266,17 +279,17 @@ words = desc["comment"].split('"') if len(words) > 1: desc["comment"] = words[1] - desc["usage"] = ("\n (%s) => (%s)" % - (", ".join(["%s:%s" % (input[1], input[0]) - for input in desc["inputs"]]), - ", ".join(["%s:%s" % (output[1], output[0]) - for output in desc["outputs"]]))) - BlkLst = StdBlckDct.setdefault(desc["name"],[]) + desc["usage"] = ("\n (%s) => (%s)" % + (", ".join(["%s:%s" % (input[1], input[0]) + for input in desc["inputs"]]), + ", ".join(["%s:%s" % (output[1], output[0]) + for output in desc["outputs"]]))) + BlkLst = StdBlckDct.setdefault(desc["name"], []) BlkLst.append((section["name"], desc)) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Languages Keywords -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Keywords for Pou Declaration POU_BLOCK_START_KEYWORDS = ["FUNCTION", "FUNCTION_BLOCK", "PROGRAM"] @@ -313,16 +326,20 @@ # Keywords for Instruction List -IL_KEYWORDS = ["TRUE", "FALSE", "LD", "LDN", "ST", "STN", "S", "R", "AND", "ANDN", "OR", "ORN", - "XOR", "XORN", "NOT", "ADD", "SUB", "MUL", "DIV", "MOD", "GT", "GE", "EQ", "NE", - "LE", "LT", "JMP", "JMPC", "JMPCN", "CAL", "CALC", "CALCN", "RET", "RETC", "RETCN"] +IL_KEYWORDS = [ + "TRUE", "FALSE", "LD", "LDN", "ST", "STN", "S", "R", "AND", "ANDN", "OR", "ORN", + "XOR", "XORN", "NOT", "ADD", "SUB", "MUL", "DIV", "MOD", "GT", "GE", "EQ", "NE", + "LE", "LT", "JMP", "JMPC", "JMPCN", "CAL", "CALC", "CALCN", "RET", "RETC", "RETCN" +] # Keywords for Structured Text ST_BLOCK_START_KEYWORDS = ["IF", "ELSIF", "ELSE", "CASE", "FOR", "WHILE", "REPEAT"] ST_BLOCK_END_KEYWORDS = ["END_IF", "END_CASE", "END_FOR", "END_WHILE", "END_REPEAT"] -ST_KEYWORDS = ["TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT", - "RETURN", "NOT", "MOD", "AND", "XOR", "OR"] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS +ST_KEYWORDS = [ + "TRUE", "FALSE", "THEN", "OF", "TO", "BY", "DO", "DO", "UNTIL", "EXIT", + "RETURN", "NOT", "MOD", "AND", "XOR", "OR" +] + ST_BLOCK_START_KEYWORDS + ST_BLOCK_END_KEYWORDS # All the keywords of IEC IEC_BLOCK_START_KEYWORDS = [] @@ -338,5 +355,3 @@ SFC_KEYWORDS, IL_KEYWORDS, ST_KEYWORDS])]: for keywords in keywords_list: all_keywords.extend([keyword for keyword in keywords if keyword not in all_keywords]) - - diff -r c1298e7ffe3a -r 8391c11477f4 py_ext/PythonEditor.py --- a/py_ext/PythonEditor.py Fri Mar 24 12:07:47 2017 +0000 +++ b/py_ext/PythonEditor.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,26 +22,29 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import keyword import wx.stc as stc from controls.CustomStyledTextCtrl import faces from editors.CodeFileEditor import CodeFileEditor, CodeEditor + class PythonCodeEditor(CodeEditor): KEYWORDS = keyword.kwlist COMMENT_HEADER = "#" - + def SetCodeLexer(self): self.SetLexer(stc.STC_LEX_PYTHON) - + # Line numbers in margin - self.StyleSetSpec(stc.STC_STYLE_LINENUMBER,'fore:#000000,back:#99A9C2,size:%(size)d' % faces) + self.StyleSetSpec(stc.STC_STYLE_LINENUMBER, 'fore:#000000,back:#99A9C2,size:%(size)d' % faces) # Highlighted brace - self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,'fore:#00009D,back:#FFFF00,size:%(size)d' % faces) + self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, 'fore:#00009D,back:#FFFF00,size:%(size)d' % faces) # Unmatched brace - self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,'fore:#00009D,back:#FF0000,size:%(size)d' % faces) + self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, 'fore:#00009D,back:#FF0000,size:%(size)d' % faces) # Indentation guide self.StyleSetSpec(stc.STC_STYLE_INDENTGUIDE, 'fore:#CDCDCD,size:%(size)d' % faces) @@ -69,15 +72,14 @@ # Identifiers. I leave this as not bold because everything seems # to be an identifier if it doesn't match the above criterae self.StyleSetSpec(stc.STC_P_IDENTIFIER, 'fore:#000000,size:%(size)d' % faces) - -#------------------------------------------------------------------------------- + +# ------------------------------------------------------------------------------- # CFileEditor Main Frame Class -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class PythonEditor(CodeFileEditor): - + CONFNODEEDITOR_TABS = [ (_("Python code"), "_create_CodePanel")] CODE_EDITOR = PythonCodeEditor - diff -r c1298e7ffe3a -r 8391c11477f4 py_ext/PythonFileCTNMixin.py --- a/py_ext/PythonFileCTNMixin.py Fri Mar 24 12:07:47 2017 +0000 +++ b/py_ext/PythonFileCTNMixin.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,13 +23,17 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re -from lxml import etree + +from __future__ import absolute_import +import os +import re +import util.paths as paths from xmlclass import GenerateParserFromXSD from CodeFileTreeNode import CodeFile -from PythonEditor import PythonEditor +from py_ext.PythonEditor import PythonEditor + class PythonFileCTNMixin(CodeFile): @@ -47,8 +52,7 @@ filepath = self.PythonFileName() if os.path.isfile(filepath): - PythonParser = GenerateParserFromXSD( - os.path.join(os.path.dirname(__file__), "py_ext_xsd.xsd")) + PythonParser = GenerateParserFromXSD(paths.AbsNeighbourFile(__file__, "py_ext_xsd.xsd")) xmlfile = open(filepath, 'r') pythonfile_xml = xmlfile.read() @@ -58,8 +62,8 @@ 'xmlns="http://www.w3.org/2001/XMLSchema"', 'xmlns:xhtml="http://www.w3.org/1999/xhtml"') for cre, repl in [ - (re.compile("(?)(?:)(?!)"), "]]>")]: + (re.compile("(?)(?:)(?!)"), "]]>")]: pythonfile_xml = cre.sub(repl, pythonfile_xml) try: @@ -84,34 +88,44 @@ PreSectionsTexts = {} PostSectionsTexts = {} - def GetSection(self,section): - return self.PreSectionsTexts.get(section,"") + "\n" + \ + + def GetSection(self, section): + return self.PreSectionsTexts.get(section, "") + "\n" + \ getattr(self.CodeFile, section).getanyText() + "\n" + \ - self.PostSectionsTexts.get(section,"") + self.PostSectionsTexts.get(section, "") def CTNGenerate_C(self, buildpath, locations): # location string for that CTN - location_str = "_".join(map(lambda x:str(x), - self.GetCurrentLocation())) + location_str = "_".join(map(str, self.GetCurrentLocation())) configname = self.GetCTRoot().GetProjectConfigNames()[0] + def _onchangecode(var): + return '"' + var.getonchange() + \ + "('" + var.getname() + "')\"" \ + if var.getonchange() else '""' + + def _onchange(var): + return repr(var.getonchange()) \ + if var.getonchange() else None + pyextname = self.CTNName() - varinfos = map(lambda variable : { - "name": variable.getname(), - "desc" : repr(variable.getdesc()), - "onchangecode" : '"'+variable.getonchange()+\ - "('"+variable.getname()+"')\"" \ - if variable.getonchange() else '""', - "onchange" : repr(variable.getonchange()) \ - if variable.getonchange() else None, - "opts" : repr(variable.getopts()), - "configname" : configname.upper(), - "uppername" : variable.getname().upper(), - "IECtype" : variable.gettype(), - "pyextname" :pyextname}, - self.CodeFile.variables.variable) + varinfos = map( + lambda variable: { + "name": variable.getname(), + "desc": repr(variable.getdesc()), + "onchangecode": _onchangecode(variable), + "onchange": _onchange(variable), + "opts": repr(variable.getopts()), + "configname": configname.upper(), + "uppername": variable.getname().upper(), + "IECtype": variable.gettype(), + "initial": repr(variable.getinitial()), + "pyextname": pyextname + }, + self.CodeFile.variables.variable) # python side PLC global variables access stub - globalstubs = "\n".join(["""\ + globalstubs = "\n".join([ + """\ _%(name)s_ctype, _%(name)s_unpack, _%(name)s_pack = \\ TypeTranslator["%(IECtype)s"] _PySafeGetPLCGlob_%(name)s = PLCBinary.__SafeGetPLCGlob_%(name)s @@ -123,11 +137,11 @@ _%(pyextname)sGlobalsDesc.append(( "%(name)s", "%(IECtype)s", + %(initial)s, %(desc)s, %(onchange)s, %(opts)s)) -""" % varinfo - for varinfo in varinfos]) +""" % varinfo for varinfo in varinfos]) # Runtime calls (start, stop, init, and cleanup) rtcalls = "" @@ -143,6 +157,13 @@ globalsection = self.GetSection("globals") + loc_dict = { + "pyextname": pyextname, + "globalstubs": globalstubs, + "globalsection": globalsection, + "rtcalls": rtcalls, + } + PyFileContent = """\ #!/usr/bin/env python # -*- coding: utf-8 -*- @@ -150,7 +171,7 @@ ## ## Code for PLC global variable access -from targets.typemapping import TypeTranslator +from runtime.typemapping import TypeTranslator import ctypes _%(pyextname)sGlobalsDesc = [] __ext_name__ = "%(pyextname)s" @@ -165,11 +186,11 @@ del __ext_name__ -""" % locals() +""" % loc_dict # write generated content to python file runtimefile_path = os.path.join(buildpath, - "runtime_%s.py"%location_str) + "runtime_%s.py" % location_str) runtimefile = open(runtimefile_path, 'w') runtimefile.write(PyFileContent.encode('utf-8')) runtimefile.close() @@ -233,15 +254,23 @@ __SET_VAR(__%(name)s_notifier->,CODE,,__STRING_LITERAL(%(onchangelen)d,%(onchangecode)s)); """ vardec = "\n".join([(vardecfmt + vardeconchangefmt - if varinfo["onchange"] else vardecfmt)% varinfo + if varinfo["onchange"] else vardecfmt) % varinfo for varinfo in varinfos]) varret = "\n".join([varretfmt % varinfo for varinfo in varinfos]) varpub = "\n".join([(varpubonchangefmt if varinfo["onchange"] else varpubfmt) % varinfo for varinfo in varinfos]) - varinit = "\n".join([varinitonchangefmt % dict( - onchangelen = len(varinfo["onchangecode"]),**varinfo) - for varinfo in varinfos if varinfo["onchange"]]) + varinit = "\n".join([varinitonchangefmt % + dict(onchangelen=len(varinfo["onchangecode"]), **varinfo) + for varinfo in varinfos if varinfo["onchange"]]) + + loc_dict = { + "vardec": vardec, + "varinit": varinit, + "varret": varret, + "varpub": varpub, + "location_str": location_str, + } # TODO : use config name obtained from model instead of default # "config.h". User cannot change config name, but project imported @@ -275,18 +304,17 @@ void __publish_%(location_str)s(void){ %(varpub)s } -""" % locals() - - Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c"%location_str) - pycfile = open(Gen_PyCfile_path,'w') +""" % loc_dict + + Gen_PyCfile_path = os.path.join(buildpath, "PyCFile_%s.c" % location_str) + pycfile = open(Gen_PyCfile_path, 'w') pycfile.write(PyCFileContent) pycfile.close() - matiec_CFLAGS = '"-I%s"'%os.path.abspath( + matiec_CFLAGS = '"-I%s"' % os.path.abspath( self.GetCTRoot().GetIECLibPath()) return ([(Gen_PyCfile_path, matiec_CFLAGS)], "", True, - ("runtime_%s.py"%location_str, file(runtimefile_path,"rb"))) - + ("runtime_%s.py" % location_str, file(runtimefile_path, "rb"))) diff -r c1298e7ffe3a -r 8391c11477f4 py_ext/__init__.py --- a/py_ext/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/py_ext/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,6 +22,7 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from py_ext import * -from PythonEditor import PythonEditor -from PythonFileCTNMixin import PythonFileCTNMixin +from __future__ import absolute_import +from .py_ext import * +from .PythonEditor import PythonEditor +from .PythonFileCTNMixin import PythonFileCTNMixin diff -r c1298e7ffe3a -r 8391c11477f4 py_ext/py_ext.py --- a/py_ext/py_ext.py Fri Mar 24 12:07:47 2017 +0000 +++ b/py_ext/py_ext.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,18 +23,21 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import os from POULibrary import POULibrary -from PythonFileCTNMixin import PythonFileCTNMixin +from py_ext.PythonFileCTNMixin import PythonFileCTNMixin +import util.paths as paths + class PythonLibrary(POULibrary): def GetLibraryPath(self): - return os.path.join(os.path.split(__file__)[0], "pous.xml") + return paths.AbsNeighbourFile(__file__, "pous.xml") def Generate_C(self, buildpath, varlist, IECCFLAGS): - - plc_python_filepath = os.path.join( - os.path.split(__file__)[0], "plc_python.c") + + plc_python_filepath = paths.AbsNeighbourFile(__file__, "plc_python.c") plc_python_file = open(plc_python_filepath, 'r') plc_python_code = plc_python_file.read() plc_python_file.close() @@ -43,21 +47,20 @@ "PYTHON_POLL"]: python_eval_fb_list.append(v) python_eval_fb_count = max(1, len(python_eval_fb_list)) - + # prepare python code plc_python_code = plc_python_code % { - "python_eval_fb_count": python_eval_fb_count } - + "python_eval_fb_count": python_eval_fb_count} + Gen_Pythonfile_path = os.path.join(buildpath, "py_ext.c") - pythonfile = open(Gen_Pythonfile_path,'w') + pythonfile = open(Gen_Pythonfile_path, 'w') pythonfile.write(plc_python_code) pythonfile.close() - + return (["py_ext"], [(Gen_Pythonfile_path, IECCFLAGS)], True), "" + class PythonFile(PythonFileCTNMixin): - + def GetIconName(self): return "Pyfile" - - diff -r c1298e7ffe3a -r 8391c11477f4 runtime/NevowServer.py --- a/runtime/NevowServer.py Fri Mar 24 12:07:47 2017 +0000 +++ b/runtime/NevowServer.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,31 +1,34 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# This file is part of Beremiz runtime. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # -# See COPYING file for copyrights details. +# See COPYING.Runtime file for copyrights details. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +from __future__ import absolute_import +from __future__ import print_function import os -from nevow import rend, appserver, inevow, tags, loaders, athena +import util.paths as paths +from nevow import appserver, inevow, tags, loaders, athena from nevow.page import renderer -from twisted.python import util from twisted.internet import reactor xhtml_header = ''' @@ -35,6 +38,7 @@ WorkingDir = None + class PLCHMI(athena.LiveElement): initialised = False @@ -45,22 +49,28 @@ def HMIinitialisation(self): self.HMIinitialised(None) + class DefaultPLCStartedHMI(PLCHMI): - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ - tags.h1["PLC IS NOW STARTED"], - ]) + docFactory = loaders.stan( + tags.div(render=tags.directive('liveElement'))[ + tags.h1["PLC IS NOW STARTED"], + ]) + class PLCStoppedHMI(PLCHMI): - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ - tags.h1["PLC IS STOPPED"], - ]) + docFactory = loaders.stan( + tags.div(render=tags.directive('liveElement'))[ + tags.h1["PLC IS STOPPED"], + ]) + class MainPage(athena.LiveElement): jsClass = u"WebInterface.PLC" - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ - tags.div(id='content')[ - tags.div(render = tags.directive('PLCElement')), - ]]) + docFactory = loaders.stan( + tags.div(render=tags.directive('liveElement'))[ + tags.div(id='content')[ + tags.div(render=tags.directive('PLCElement'))] + ]) def __init__(self, *a, **kw): athena.LiveElement.__init__(self, *a, **kw) @@ -84,7 +94,7 @@ def HMIexec(self, function, *args, **kwargs): if self.HMI is not None: - getattr(self.HMI, function, lambda:None)(*args, **kwargs) + getattr(self.HMI, function, lambda: None)(*args, **kwargs) athena.expose(HMIexec) def resetHMI(self): @@ -109,21 +119,22 @@ for child in self.liveFragmentChildren[:]: child.detach() + class WebInterface(athena.LivePage): docFactory = loaders.stan([tags.raw(xhtml_header), - tags.html(xmlns="http://www.w3.org/1999/xhtml")[ - tags.head(render=tags.directive('liveglue')), - tags.body[ - tags.div[ - tags.div( render = tags.directive( "MainPage" )) - ]]]]) + tags.html(xmlns="http://www.w3.org/1999/xhtml")[ + tags.head(render=tags.directive('liveglue')), + tags.body[ + tags.div[ + tags.div(render=tags.directive("MainPage")) + ]]]]) MainPage = MainPage() PLCHMI = PLCHMI def __init__(self, plcState=False, *a, **kw): super(WebInterface, self).__init__(*a, **kw) - self.jsModules.mapping[u'WebInterface'] = util.sibpath(__file__, 'webinterface.js') + self.jsModules.mapping[u'WebInterface'] = paths.AbsNeighbourFile(__file__, 'webinterface.js') self.plcState = plcState self.MainPage.setPLCState(plcState) @@ -150,7 +161,7 @@ """ Force content type to fit with SVG """ - req = inevow.IRequest(ctx) + req = ctx.locate(inevow.IRequest) req.setHeader('Content-type', 'application/xhtml+xml') return super(WebInterface, self).renderHTTP(ctx) @@ -169,19 +180,20 @@ def disconnected(self, reason): self.MainPage.resetHMI() - #print reason - #print "We will be called back when the client disconnects" + # print reason + # print "We will be called back when the client disconnects" + def RegisterWebsite(port): website = WebInterface() site = appserver.NevowSite(website) - listening = False reactor.listenTCP(port, site) - print _("HTTP interface port :"), port + print(_('HTTP interface port :'), port) return website -class statuslistener: + +class statuslistener(object): def __init__(self, site): self.oldstate = None self.site = site @@ -190,8 +202,10 @@ if state != self.oldstate: action = {'Started': self.site.PLCStarted, 'Stopped': self.site.PLCStopped}.get(state, None) - if action is not None: action () + if action is not None: + action() self.oldstate = state + def website_statuslistener_factory(site): return statuslistener(site).listen diff -r c1298e7ffe3a -r 8391c11477f4 runtime/PLCObject.py --- a/runtime/PLCObject.py Fri Mar 24 12:07:47 2017 +0000 +++ b/runtime/PLCObject.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,32 +1,39 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# This file is part of Beremiz runtime. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD # -# See COPYING file for copyrights details. +# See COPYING.Runtime file for copyrights details. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. + +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +from __future__ import absolute_import +from threading import Timer, Thread, Lock, Semaphore, Event +import ctypes +import os +import sys +import traceback +from time import time import Pyro.core as pyro -from threading import Timer, Thread, Lock, Semaphore, Event -import ctypes, os, commands, types, sys -from targets.typemapping import LogLevelsDefault, LogLevelsCount, TypeTranslator, UnpackDebugBuffer -from time import time + +from runtime.typemapping import TypeTranslator +from runtime.loglevels import LogLevelsDefault, LogLevelsCount if os.name in ("nt", "ce"): @@ -35,26 +42,29 @@ elif os.name == "posix": from _ctypes import dlopen, dlclose -import traceback + def get_last_traceback(tb): while tb.tb_next: tb = tb.tb_next return tb -lib_ext ={ - "linux2":".so", - "win32":".dll", - }.get(sys.platform, "") + +lib_ext = { + "linux2": ".so", + "win32": ".dll", +}.get(sys.platform, "") + def PLCprint(message): sys.stdout.write("PLCobject : "+message+"\n") sys.stdout.flush() + class PLCObject(pyro.ObjBase): def __init__(self, workingdir, daemon, argv, statuschange, evaluator, pyruntimevars): pyro.ObjBase.__init__(self) self.evaluator = evaluator - self.argv = [workingdir] + argv # force argv[0] to be "path" to exec... + self.argv = [workingdir] + argv # force argv[0] to be "path" to exec... self.workingdir = workingdir self.PLCStatus = "Empty" self.PLClibraryHandle = None @@ -76,14 +86,14 @@ def AutoLoad(self): # Get the last transfered PLC if connector must be restart try: - self.CurrentPLCFilename=open( - self._GetMD5FileName(), - "r").read().strip() + lib_ext + self.CurrentPLCFilename = open( + self._GetMD5FileName(), + "r").read().strip() + lib_ext if self.LoadPLC(): self.PLCStatus = "Stopped" - except Exception, e: + except Exception: self.PLCStatus = "Empty" - self.CurrentPLCFilename=None + self.CurrentPLCFilename = None def StatusChange(self): if self.statuschange is not None: @@ -96,16 +106,19 @@ else: level = LogLevelsDefault msg, = args - return self._LogMessage(level, msg, len(msg)) + PLCprint(msg) + if self._LogMessage is not None: + return self._LogMessage(level, msg, len(msg)) + return None def ResetLogCount(self): if self._ResetLogCount is not None: self._ResetLogCount() def GetLogCount(self, level): - if self._GetLogCount is not None : + if self._GetLogCount is not None: return int(self._GetLogCount(level)) - elif self._loading_error is not None and level==0: + elif self._loading_error is not None and level == 0: return 1 def GetLogMessage(self, level, msgid): @@ -115,23 +128,22 @@ if self._GetLogMessage is not None: maxsz = len(self._log_read_buffer)-1 sz = self._GetLogMessage(level, msgid, - self._log_read_buffer, maxsz, - ctypes.byref(tick), - ctypes.byref(tv_sec), - ctypes.byref(tv_nsec)) + self._log_read_buffer, maxsz, + ctypes.byref(tick), + ctypes.byref(tv_sec), + ctypes.byref(tv_nsec)) if sz and sz <= maxsz: self._log_read_buffer[sz] = '\x00' - return self._log_read_buffer.value,tick.value,tv_sec.value,tv_nsec.value - elif self._loading_error is not None and level==0: - return self._loading_error,0,0,0 + return self._log_read_buffer.value, tick.value, tv_sec.value, tv_nsec.value + elif self._loading_error is not None and level == 0: + return self._loading_error, 0, 0, 0 return None def _GetMD5FileName(self): return os.path.join(self.workingdir, "lasttransferedPLC.md5") def _GetLibFileName(self): - return os.path.join(self.workingdir,self.CurrentPLCFilename) - + return os.path.join(self.workingdir, self.CurrentPLCFilename) def LoadPLC(self): """ @@ -144,8 +156,8 @@ self.PLClibraryHandle = ctypes.CDLL(self.CurrentPLCFilename, handle=self._PLClibraryHandle) self.PLC_ID = ctypes.c_char_p.in_dll(self.PLClibraryHandle, "PLC_ID") - if len(md5) == 32 : - self.PLC_ID.value = md5 + if len(md5) == 32: + self.PLC_ID.value = md5 self._startPLC = self.PLClibraryHandle.startPLC self._startPLC.restype = ctypes.c_int @@ -164,6 +176,7 @@ # If python confnode is not enabled, we reuse _PythonIterator # as a call that block pythonthread until StopPLC self.PlcStopping = Event() + def PythonIterator(res, blkid): self.PlcStopping.clear() self.PlcStopping.wait() @@ -175,7 +188,6 @@ self.PlcStopping.set() self._stopPLC = __StopPLC - self._ResetDebugVariables = self.PLClibraryHandle.ResetDebugVariables self._ResetDebugVariables.restype = None @@ -208,7 +220,7 @@ self._LogMessage.restype = ctypes.c_int self._LogMessage.argtypes = [ctypes.c_uint8, ctypes.c_char_p, ctypes.c_uint32] - self._log_read_buffer = ctypes.create_string_buffer(1<<14) #16K + self._log_read_buffer = ctypes.create_string_buffer(1 << 14) # 16K self._GetLogMessage = self.PLClibraryHandle.GetLogMessage self._GetLogMessage.restype = ctypes.c_uint32 self._GetLogMessage.argtypes = [ctypes.c_uint8, ctypes.c_uint32, ctypes.c_char_p, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32), ctypes.POINTER(ctypes.c_uint32)] @@ -218,7 +230,7 @@ self.PythonRuntimeInit() return True - except: + except Exception: self._loading_error = traceback.format_exc() PLCprint(self._loading_error) return False @@ -234,22 +246,22 @@ """ self.PLClibraryLock.acquire() # Forget all refs to library - self._startPLC = lambda x,y:None - self._stopPLC = lambda:None - self._ResetDebugVariables = lambda:None - self._RegisterDebugVariable = lambda x, y:None - self._IterDebugData = lambda x,y:None - self._FreeDebugData = lambda:None - self._GetDebugData = lambda:-1 - self._suspendDebug = lambda x:-1 - self._resumeDebug = lambda:None - self._PythonIterator = lambda:"" + self._startPLC = lambda x, y: None + self._stopPLC = lambda: None + self._ResetDebugVariables = lambda: None + self._RegisterDebugVariable = lambda x, y: None + self._IterDebugData = lambda x, y: None + self._FreeDebugData = lambda: None + self._GetDebugData = lambda: -1 + self._suspendDebug = lambda x: -1 + self._resumeDebug = lambda: None + self._PythonIterator = lambda: "" self._GetLogCount = None - self._LogMessage = lambda l,m,s:PLCprint("OFF LOG :"+m) + self._LogMessage = None self._GetLogMessage = None self.PLClibraryHandle = None # Unload library explicitely - if getattr(self,"_PLClibraryHandle",None) is not None: + if getattr(self, "_PLClibraryHandle", None) is not None: dlclose(self._PLClibraryHandle) self._PLClibraryHandle = None @@ -261,42 +273,44 @@ Calls init, start, stop or cleanup method provided by runtime python files, loaded when new PLC uploaded """ - for method in self.python_runtime_vars.get("_runtime_%s"%methodname, []): - res,exp = self.evaluator(method) + for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []): + _res, exp = self.evaluator(method) if exp is not None: - self.LogMessage(0,'\n'.join(traceback.format_exception(*exp))) + self.LogMessage(0, '\n'.join(traceback.format_exception(*exp))) def PythonRuntimeInit(self): MethodNames = ["init", "start", "stop", "cleanup"] self.python_runtime_vars = globals().copy() self.python_runtime_vars.update(self.pyruntimevars) - - class PLCSafeGlobals: - def __getattr__(_self, name): - try : - t = self.python_runtime_vars["_"+name+"_ctype"] + parent = self + + class PLCSafeGlobals(object): + def __getattr__(self, name): + try: + t = parent.python_runtime_vars["_"+name+"_ctype"] except KeyError: - raise KeyError("Try to get unknown shared global variable : %s"%name) + raise KeyError("Try to get unknown shared global variable : %s" % name) v = t() - r = self.python_runtime_vars["_PySafeGetPLCGlob_"+name](ctypes.byref(v)) - return self.python_runtime_vars["_"+name+"_unpack"](v) - def __setattr__(_self, name, value): - try : - t = self.python_runtime_vars["_"+name+"_ctype"] + parent.python_runtime_vars["_PySafeGetPLCGlob_"+name](ctypes.byref(v)) + return parent.python_runtime_vars["_"+name+"_unpack"](v) + + def __setattr__(self, name, value): + try: + t = parent.python_runtime_vars["_"+name+"_ctype"] except KeyError: - raise KeyError("Try to set unknown shared global variable : %s"%name) - v = self.python_runtime_vars["_"+name+"_pack"](t,value) - self.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v)) + raise KeyError("Try to set unknown shared global variable : %s" % name) + v = parent.python_runtime_vars["_"+name+"_pack"](t, value) + parent.python_runtime_vars["_PySafeSetPLCGlob_"+name](ctypes.byref(v)) self.python_runtime_vars.update({ - "PLCGlobals" : PLCSafeGlobals(), - "WorkingDir" : self.workingdir, - "PLCObject" : self, - "PLCBinary" : self.PLClibraryHandle, - "PLCGlobalsDesc" : []}) - - for methodname in MethodNames : - self.python_runtime_vars["_runtime_%s"%methodname] = [] + "PLCGlobals": PLCSafeGlobals(), + "WorkingDir": self.workingdir, + "PLCObject": self, + "PLCBinary": self.PLClibraryHandle, + "PLCGlobalsDesc": []}) + + for methodname in MethodNames: + self.python_runtime_vars["_runtime_%s" % methodname] = [] try: filenames = os.listdir(self.workingdir) @@ -308,15 +322,13 @@ for methodname in MethodNames: method = self.python_runtime_vars.get("_%s_%s" % (name, methodname), None) if method is not None: - self.python_runtime_vars["_runtime_%s"%methodname].append(method) - except: - self.LogMessage(0,traceback.format_exc()) + self.python_runtime_vars["_runtime_%s" % methodname].append(method) + except Exception: + self.LogMessage(0, traceback.format_exc()) raise self.PythonRuntimeCall("init") - - def PythonRuntimeCleanup(self): if self.python_runtime_vars is not None: self.PythonRuntimeCall("cleanup") @@ -325,49 +337,48 @@ def PythonThreadProc(self): self.StartSem.release() - res,cmd,blkid = "None","None",ctypes.c_void_p() - compile_cache={} + res, cmd, blkid = "None", "None", ctypes.c_void_p() + compile_cache = {} while True: # print "_PythonIterator(", res, ")", - cmd = self._PythonIterator(res,blkid) + cmd = self._PythonIterator(res, blkid) FBID = blkid.value # print " -> ", cmd, blkid if cmd is None: break - try : - self.python_runtime_vars["FBID"]=FBID - ccmd,AST =compile_cache.get(FBID, (None,None)) - if ccmd is None or ccmd!=cmd: + try: + self.python_runtime_vars["FBID"] = FBID + ccmd, AST = compile_cache.get(FBID, (None, None)) + if ccmd is None or ccmd != cmd: AST = compile(cmd, '', 'eval') - compile_cache[FBID]=(cmd,AST) - result,exp = self.evaluator(eval,AST,self.python_runtime_vars) + compile_cache[FBID] = (cmd, AST) + result, exp = self.evaluator(eval, AST, self.python_runtime_vars) if exp is not None: res = "#EXCEPTION : "+str(exp[1]) - self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd, - '\n'.join(traceback.format_exception(*exp)))) + self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % ( + FBID, cmd, '\n'.join(traceback.format_exception(*exp)))) else: - res=str(result) - self.python_runtime_vars["FBID"]=None - except Exception,e: + res = str(result) + self.python_runtime_vars["FBID"] = None + except Exception, e: res = "#EXCEPTION : "+str(e) - self.LogMessage(1,('PyEval@0x%x(Code="%s") Exception "%s"')%(FBID,cmd,str(e))) + self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e))) def StartPLC(self): if self.CurrentPLCFilename is not None and self.PLCStatus == "Stopped": c_argv = ctypes.c_char_p * len(self.argv) - error = None - res = self._startPLC(len(self.argv),c_argv(*self.argv)) + res = self._startPLC(len(self.argv), c_argv(*self.argv)) if res == 0: self.PLCStatus = "Started" self.StatusChange() self.PythonRuntimeCall("start") - self.StartSem=Semaphore(0) + self.StartSem = Semaphore(0) self.PythonThread = Thread(target=self.PythonThreadProc) self.PythonThread.start() self.StartSem.acquire() self.LogMessage("PLC started") else: - self.LogMessage(0,_("Problem starting PLC : error %d" % res)) + self.LogMessage(0, _("Problem starting PLC : error %d" % res)) self.PLCStatus = "Broken" self.StatusChange() @@ -379,7 +390,7 @@ self.PLCStatus = "Stopped" self.StatusChange() self.PythonRuntimeCall("stop") - if self.TraceThread is not None : + if self.TraceThread is not None: self.TraceWakeup.set() self.TraceThread.join() self.TraceThread = None @@ -389,26 +400,26 @@ def _Reload(self): self.daemon.shutdown(True) self.daemon.sock.close() - os.execv(sys.executable,[sys.executable]+sys.argv[:]) + os.execv(sys.executable, [sys.executable]+sys.argv[:]) # never reached return 0 def ForceReload(self): # respawn python interpreter - Timer(0.1,self._Reload).start() + Timer(0.1, self._Reload).start() return True def GetPLCstatus(self): - return self.PLCStatus, map(self.GetLogCount,xrange(LogLevelsCount)) + return self.PLCStatus, map(self.GetLogCount, xrange(LogLevelsCount)) def NewPLC(self, md5sum, data, extrafiles): if self.PLCStatus in ["Stopped", "Empty", "Broken"]: NewFileName = md5sum + lib_ext - extra_files_log = os.path.join(self.workingdir,"extra_files.txt") + extra_files_log = os.path.join(self.workingdir, "extra_files.txt") self.UnLoadPLC() - self.LogMessage("NewPLC (%s)"%md5sum) + self.LogMessage("NewPLC (%s)" % md5sum) self.PLCStatus = "Empty" try: @@ -417,14 +428,14 @@ for filename in file(extra_files_log, "r").readlines() + [extra_files_log]: try: os.remove(os.path.join(self.workingdir, filename.strip())) - except: + except Exception: pass - except: + except Exception: pass try: # Create new PLC file - open(os.path.join(self.workingdir,NewFileName), + open(os.path.join(self.workingdir, NewFileName), 'wb').write(data) # Store new PLC filename based on md5 key @@ -432,14 +443,14 @@ # Then write the files log = file(extra_files_log, "w") - for fname,fdata in extrafiles: - fpath = os.path.join(self.workingdir,fname) + for fname, fdata in extrafiles: + fpath = os.path.join(self.workingdir, fname) open(fpath, "wb").write(fdata) log.write(fname+'\n') # Store new PLC filename self.CurrentPLCFilename = NewFileName - except: + except Exception: self.PLCStatus = "Broken" self.StatusChange() PLCprint(traceback.format_exc()) @@ -459,7 +470,7 @@ try: last_md5 = open(self._GetMD5FileName(), "r").read() return last_md5 == MD5 - except: + except Exception: pass return False @@ -473,12 +484,12 @@ if self._suspendDebug(False) == 0: # keep a copy of requested idx self._ResetDebugVariables() - for idx,iectype,force in idxs: - if force !=None: - c_type,unpack_func, pack_func = \ + for idx, iectype, force in idxs: + if force is not None: + c_type, _unpack_func, pack_func = \ TypeTranslator.get(iectype, - (None,None,None)) - force = ctypes.byref(pack_func(c_type,force)) + (None, None, None)) + force = ctypes.byref(pack_func(c_type, force)) self._RegisterDebugVariable(idx, force) self._TracesSwap() self._resumeDebug() @@ -488,7 +499,7 @@ def _TracesPush(self, trace): self.TraceLock.acquire() lT = len(self.Traces) - if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024 : + if lT != 0 and lT * len(self.Traces[0]) > 1024 * 1024: self.Traces.pop(0) self.Traces.append(trace) self.TraceLock.release() @@ -512,10 +523,10 @@ self.TraceLock.acquire() self.Traces = [] self.TraceLock.release() - self._suspendDebug(True) # Disable debugger + self._suspendDebug(True) # Disable debugger self.TraceWakeup.clear() self.TraceWakeup.wait() - self._resumeDebug() # Re-enable debugger + self._resumeDebug() # Re-enable debugger def _TracesFlush(self): self.TraceLock.acquire() @@ -529,7 +540,7 @@ """ Return a list of traces, corresponding to the list of required idx """ - while self.PLCStatus == "Started" : + while self.PLCStatus == "Started": tick = ctypes.c_uint32() size = ctypes.c_uint32() buff = ctypes.c_void_p() @@ -547,15 +558,12 @@ self._TracesAutoSuspend() self._TracesFlush() - def RemoteExec(self, script, *kwargs): try: exec script in kwargs - except: - e_type, e_value, e_traceback = sys.exc_info() + except Exception: + _e_type, e_value, e_traceback = sys.exc_info() line_no = traceback.tb_lineno(get_last_traceback(e_traceback)) return (-1, "RemoteExec script failed!\n\nLine %d: %s\n\t%s" % - (line_no, e_value, script.splitlines()[line_no - 1])) + (line_no, e_value, script.splitlines()[line_no - 1])) return (0, kwargs.get("returnVal", None)) - - diff -r c1298e7ffe3a -r 8391c11477f4 runtime/ServicePublisher.py --- a/runtime/ServicePublisher.py Fri Mar 24 12:07:47 2017 +0000 +++ b/runtime/ServicePublisher.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,94 +1,99 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# This file is part of Beremiz runtime. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD # -# See COPYING file for copyrights details. +# See COPYING.Runtime file for copyrights details. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. -import socket, threading -from util import Zeroconf +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +from __future__ import absolute_import +from __future__ import print_function +import socket +import threading +import zeroconf service_type = '_PYRO._tcp.local.' -class ServicePublisher(): + +class ServicePublisher(object): def __init__(self): # type: fully qualified service type name - self.serviceproperties = {'description':'Beremiz remote PLC'} - + self.serviceproperties = {'description': 'Beremiz remote PLC'} + self.name = None self.ip_32b = None self.port = None self.server = None self.service_name = None self.retrytimer = None - + def RegisterService(self, name, ip, port): try: self._RegisterService(name, ip, port) - except Exception,e: - self.retrytimer = threading.Timer(2,self.RegisterService,[name, ip, port]) - self.retrytimer.start() + except Exception: + self.retrytimer = threading.Timer(2, self.RegisterService, [name, ip, port]) + self.retrytimer.start() def _RegisterService(self, name, ip, port): # name: fully qualified service name - self.service_name = 'Beremiz_%s.%s'%(name,service_type) + self.service_name = 'Beremiz_%s.%s' % (name, service_type) self.name = name self.port = port - self.server = Zeroconf.Zeroconf(ip) - print "MDNS brodcasting on :"+ip + self.server = zeroconf.Zeroconf() + print("MDNS brodcasting on :" + ip) if ip == "0.0.0.0": ip = self.gethostaddr() - print "MDNS brodcasted service address :"+ip + print("MDNS brodcasted service address :" + ip) self.ip_32b = socket.inet_aton(ip) - self.server.registerService( - Zeroconf.ServiceInfo(service_type, - self.service_name, - self.ip_32b, - self.port, - properties = self.serviceproperties)) - self.retrytimer=None - + self.server.register_service( + zeroconf.ServiceInfo(service_type, + self.service_name, + self.ip_32b, + self.port, + properties=self.serviceproperties)) + self.retrytimer = None + def UnRegisterService(self): if self.retrytimer is not None: self.retrytimer.cancel() - self.server.unregisterService( - Zeroconf.ServiceInfo(service_type, - self.service_name, - self.ip_32b, - self.port, - properties = self.serviceproperties)) - self.server.close() - self.server = None - - def gethostaddr(self, dst = '224.0.1.41'): + if self.server is not None: + self.server.unregister_service( + zeroconf.ServiceInfo(service_type, + self.service_name, + self.ip_32b, + self.port, + properties=self.serviceproperties)) + self.server.close() + self.server = None + + def gethostaddr(self, dst='224.0.1.41'): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: s.connect((dst, 7)) - (host, port) = s.getsockname() + (host, _port) = s.getsockname() s.close() if host != '0.0.0.0': return host - except Exception,e: + except Exception: pass return socket.gethostbyname(socket.gethostname()) diff -r c1298e7ffe3a -r 8391c11477f4 runtime/WampClient.py --- a/runtime/WampClient.py Fri Mar 24 12:07:47 2017 +0000 +++ b/runtime/WampClient.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,78 +1,103 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# This file is part of Beremiz runtime. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD # -# See COPYING file for copyrights details. +# See COPYING.Runtime file for copyrights details. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. -import sys -#from twisted.python import log +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +from __future__ import absolute_import +from __future__ import print_function +import time +import json from autobahn.twisted import wamp from autobahn.twisted.websocket import WampWebSocketClientFactory, connectWS +from autobahn.wamp import types, auth +from autobahn.wamp.serializer import MsgPackSerializer from twisted.internet.defer import inlineCallbacks -from autobahn.wamp import types -from autobahn.wamp.serializer import MsgPackSerializer from twisted.internet.protocol import ReconnectingClientFactory -import json + _WampSession = None _PySrv = None -ExposedCalls = ["StartPLC", - "StopPLC", - "ForceReload", - "GetPLCstatus", - "NewPLC", - "MatchMD5", - "SetTraceVariablesList", - "GetTraceVariables", - "RemoteExec", - "GetLogMessage", - "ResetLogCount", - ] +ExposedCalls = [ + "StartPLC", + "StopPLC", + "ForceReload", + "GetPLCstatus", + "NewPLC", + "MatchMD5", + "SetTraceVariablesList", + "GetTraceVariables", + "RemoteExec", + "GetLogMessage", + "ResetLogCount", +] +# Those two lists are meant to be filled by customized runtime +# or User python code. + +""" crossbar Events to register to """ SubscribedEvents = [] +""" things to do on join (callables) """ DoOnJoin = [] + def GetCallee(name): """ Get Callee or Subscriber corresponding to '.' spearated object path """ - global _PySrv names = name.split('.') obj = _PySrv.plcobj - while names: obj = getattr(obj, names.pop(0)) + while names: + obj = getattr(obj, names.pop(0)) return obj + class WampSession(wamp.ApplicationSession): + def onConnect(self): + if "secret" in self.config.extra: + user = self.config.extra["ID"].encode('utf8') + self.join(u"Automation", [u"wampcra"], user) + else: + self.join(u"Automation") + + def onChallenge(self, challenge): + if challenge.method == u"wampcra": + secret = self.config.extra["secret"].encode('utf8') + signature = auth.compute_wcs(secret, challenge.extra['challenge'].encode('utf8')) + return signature.decode("ascii") + else: + raise Exception("don't know how to handle authmethod {}".format(challenge.method)) @inlineCallbacks def onJoin(self, details): global _WampSession _WampSession = self ID = self.config.extra["ID"] - print 'WAMP session joined by :', ID + print('WAMP session joined by :', ID) for name in ExposedCalls: - reg = yield self.register(GetCallee(name), '.'.join((ID,name))) + regoption = types.RegisterOptions(u'exact', u'last', None, None) + yield self.register(GetCallee(name), u'.'.join((ID, name)), regoption) for name in SubscribedEvents: - reg = yield self.subscribe(GetCallee(name), name) + yield self.subscribe(GetCallee(name), unicode(name)) for func in DoOnJoin: yield func(self) @@ -80,54 +105,78 @@ def onLeave(self, details): global _WampSession _WampSession = None - print 'WAMP session left' + print(_('WAMP session left')) + class ReconnectingWampWebSocketClientFactory(WampWebSocketClientFactory, ReconnectingClientFactory): def clientConnectionFailed(self, connector, reason): - print("WAMP Client connection failed .. retrying ..") - self.retry(connector) + print(_("WAMP Client connection failed (%s) .. retrying .." % time.ctime())) + ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) + def clientConnectionLost(self, connector, reason): - print("WAMP Client connection lost .. retrying ..") - self.retry(connector) + print(_("WAMP Client connection lost (%s) .. retrying .." % time.ctime())) + ReconnectingClientFactory.clientConnectionFailed(self, connector, reason) + def LoadWampClientConf(wampconf): + try: + WSClientConf = json.load(open(wampconf)) + return WSClientConf + except ValueError, ve: + print(_("WAMP load error: "), ve) + return None + except Exception: + return None - WSClientConf = json.load(open(wampconf)) - return WSClientConf -def RegisterWampClient(wampconf): +def LoadWampSecret(secretfname): + try: + WSClientWampSecret = open(secretfname, 'rb').read() + return WSClientWampSecret + except ValueError, ve: + print(_("Wamp secret load error:"), ve) + return None + except Exception: + return None + + +def RegisterWampClient(wampconf, secretfname): WSClientConf = LoadWampClientConf(wampconf) - ## start logging to console - # log.startLogging(sys.stdout) + if not WSClientConf: + print(_("WAMP client connection not established!")) + return + + WampSecret = LoadWampSecret(secretfname) + + if WampSecret is not None: + WSClientConf["secret"] = WampSecret # create a WAMP application session factory component_config = types.ComponentConfig( - realm = WSClientConf["realm"], - extra = {"ID":WSClientConf["ID"]}) + realm=WSClientConf["realm"], + extra=WSClientConf) session_factory = wamp.ApplicationSessionFactory( - config = component_config) + config=component_config) session_factory.session = WampSession # create a WAMP-over-WebSocket transport client factory transport_factory = ReconnectingWampWebSocketClientFactory( session_factory, - url = WSClientConf["url"], - serializers = [MsgPackSerializer()], - debug = False, - debug_wamp = False) + url=WSClientConf["url"], + serializers=[MsgPackSerializer()]) # start the client from a Twisted endpoint conn = connectWS(transport_factory) - print "WAMP client connecting to :",WSClientConf["url"] + print(_("WAMP client connecting to :"), WSClientConf["url"]) return conn + def GetSession(): - global _WampSession return _WampSession + def SetServer(pysrv): global _PySrv _PySrv = pysrv - diff -r c1298e7ffe3a -r 8391c11477f4 runtime/__init__.py --- a/runtime/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/runtime/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,30 +1,28 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# This file is part of Beremiz runtime. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD # -# See COPYING file for copyrights details. +# See COPYING.Runtime file for copyrights details. # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. + +# This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. -# module which import C files as strings +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +from __future__ import absolute_import import os -from PLCObject import PLCObject, PLCprint -import ServicePublisher +from runtime.PLCObject import PLCObject, PLCprint +import runtime.ServicePublisher diff -r c1298e7ffe3a -r 8391c11477f4 runtime/loglevels.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/loglevels.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,9 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# See COPYING.Runtime file for copyrights details. + +LogLevels = ["CRITICAL", "WARNING", "INFO", "DEBUG"] +LogLevelsCount = len(LogLevels) +LogLevelsDict = dict(zip(LogLevels, range(LogLevelsCount))) +LogLevelsDefault = LogLevelsDict["DEBUG"] diff -r c1298e7ffe3a -r 8391c11477f4 runtime/typemapping.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime/typemapping.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# See COPYING.Runtime file for copyrights details. +# + +from __future__ import absolute_import +import ctypes +from ctypes import * +from datetime import timedelta as td + +ctypes.pythonapi.PyString_AsString.argtypes = (ctypes.c_void_p,) +ctypes.pythonapi.PyString_AsString.restype = ctypes.POINTER(ctypes.c_char) + + +class IEC_STRING(Structure): + """ + Must be changed according to changes in iec_types.h + """ + _fields_ = [("len", c_uint8), + ("body", c_char * 126)] + + +class IEC_TIME(Structure): + """ + Must be changed according to changes in iec_types.h + """ + _fields_ = [("s", c_long), # tv_sec + ("ns", c_long)] # tv_nsec + + +def _t(t, u=lambda x: x.value, p=lambda t, x: t(x)): + return (t, u, p) + + +def _ttime(): + return (IEC_TIME, + lambda x: td(0, x.s, x.ns/1000), + lambda t, x: t(x.days * 24 * 3600 + x.seconds, x.microseconds*1000)) + + +SameEndianessTypeTranslator = { + "BOOL": _t(c_uint8, lambda x: x.value != 0), + "STEP": _t(c_uint8), + "TRANSITION": _t(c_uint8), + "ACTION": _t(c_uint8), + "SINT": _t(c_int8), + "USINT": _t(c_uint8), + "BYTE": _t(c_uint8), + "STRING": (IEC_STRING, + lambda x: x.body[:x.len], + lambda t, x: t(len(x), x)), + "INT": _t(c_int16), + "UINT": _t(c_uint16), + "WORD": _t(c_uint16), + "DINT": _t(c_int32), + "UDINT": _t(c_uint32), + "DWORD": _t(c_uint32), + "LINT": _t(c_int64), + "ULINT": _t(c_uint64), + "LWORD": _t(c_uint64), + "REAL": _t(c_float), + "LREAL": _t(c_double), + "TIME": _ttime(), + "TOD": _ttime(), + "DATE": _ttime(), + "DT": _ttime(), + } + +SwapedEndianessTypeTranslator = { + # TODO + } + +TypeTranslator = SameEndianessTypeTranslator + +# Construct debugger natively supported types +DebugTypesSize = dict([(key, sizeof(t)) for key, (t, p, u) in SameEndianessTypeTranslator.iteritems() if t is not None]) + + +def UnpackDebugBuffer(buff, indexes): + res = [] + buffoffset = 0 + buffsize = len(buff) + buffptr = cast(ctypes.pythonapi.PyString_AsString(id(buff)), c_void_p).value + for iectype in indexes: + c_type, unpack_func, _pack_func = \ + TypeTranslator.get(iectype, (None, None, None)) + if c_type is not None and buffoffset < buffsize: + cursor = c_void_p(buffptr + buffoffset) + value = unpack_func(cast(cursor, + POINTER(c_type)).contents) + buffoffset += sizeof(c_type) if iectype != "STRING" else len(value)+1 + res.append(value) + else: + break + if buffoffset and buffoffset == buffsize: + return res + return None diff -r c1298e7ffe3a -r 8391c11477f4 runtime_files.list --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/runtime_files.list Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,17 @@ +# those files are used in runtime +# licensed according to LGPL, see COPYING.runtime + +images/icostop24.png +images/icoplay24.png +images/brz.png +util/__init__.py +util/paths.py +runtime/WampClient.py +runtime/PLCObject.py +runtime/NevowServer.py +runtime/webinterface.js +runtime/__init__.py +runtime/ServicePublisher.py +runtime/typemapping.py +runtime/loglevels.py +Beremiz_service.py diff -r c1298e7ffe3a -r 8391c11477f4 svgui/__init__.py --- a/svgui/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,4 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from svgui import * +from __future__ import absolute_import +from svgui.svgui import * diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/__init__.py --- a/svgui/pyjs/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,2 +1,2 @@ -from pyjs import * - +from __future__ import absolute_import +from svgui.pyjs.pyjs import * diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/build.py --- a/svgui/pyjs/build.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/build.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,19 +1,19 @@ #!/usr/bin/env python + +from __future__ import absolute_import +from __future__ import print_function import sys +import shutil +import re import os -import shutil -from copy import copy -from os.path import join, dirname, basename, abspath, split, isfile, isdir +from os.path import join, basename, abspath, split, isfile, isdir +from hashlib import md5 from optparse import OptionParser -import pyjs from cStringIO import StringIO -try: - # Python 2.5 and above - from hashlib import md5 -except: - import md5 -import re + +from svgui.pyjs import pyjs + usage = """ usage: %prog [options] @@ -45,7 +45,7 @@ # .cache.html files produces look like this -CACHE_HTML_PAT=re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$') +CACHE_HTML_PAT = re.compile('^[a-z]*.[0-9a-f]{32}\.cache\.html$') # ok these are the three "default" library directories, containing # the builtins (str, List, Dict, ord, round, len, range etc.) @@ -63,6 +63,7 @@ def read_boilerplate(data_dir, filename): return open(join(data_dir, "builder/boilerplate", filename)).read() + def copy_boilerplate(data_dir, filename, output_dir): filename = join(data_dir, "builder/boilerplate", filename) shutil.copy(filename, output_dir) @@ -76,7 +77,7 @@ names = os.listdir(src) try: os.mkdir(dst) - except: + except Exception: pass errors = [] @@ -101,7 +102,8 @@ except (IOError, os.error), why: errors.append((srcname, dstname, why)) if errors: - print errors + print(errors) + def check_html_file(source_file, dest_path): """ Checks if a base HTML-file is available in the PyJamas @@ -136,19 +138,17 @@ """ - filename = os.path.split ( source_file )[1] - mod_name = os.path.splitext ( filename )[0] - file_name = os.path.join ( dest_path, mod_name + '.html' ) + filename = os.path.split(source_file)[1] + mod_name = os.path.splitext(filename)[0] + file_name = os.path.join(dest_path, mod_name + '.html') # if html file in output directory exists, leave it alone. - if os.path.exists ( file_name ): + if os.path.exists(file_name): return 0 - if os.path.exists ( - os.path.join ( dest_path, mod_name + '.css' ) ) : + if os.path.exists(os.path.join(dest_path, mod_name + '.css')): css = "" - elif os.path.exists ( - os.path.join ( dest_path, 'pyjamas_default.css' ) ) : + elif os.path.exists(os.path.join(dest_path, 'pyjamas_default.css')): css = "" else: @@ -158,9 +158,9 @@ base_html = base_html % {'modulename': mod_name, 'title': title, 'css': css} - fh = open (file_name, 'w') - fh.write (base_html) - fh.close () + fh = open(file_name, 'w') + fh.write(base_html) + fh.close() return 1 @@ -174,27 +174,27 @@ msg = "Building '%(app_name)s' to output directory '%(output)s'" % locals() if debug: msg += " with debugging statements" - print msg + print(msg) # check the output directory if os.path.exists(output) and not os.path.isdir(output): - print >>sys.stderr, "Output destination %s exists and is not a directory" % output + print("Output destination %s exists and is not a directory" % output, file=sys.stderr) return if not os.path.isdir(output): try: - print "Creating output directory" + print("Creating output directory") os.mkdir(output) except StandardError, e: - print >>sys.stderr, "Exception creating output directory %s: %s" % (output, e) - - ## public dir + print("Exception creating output directory %s: %s" % (output, e), file=sys.stderr) + + # public dir for p in pyjs.path: pub_dir = join(p, 'public') if isdir(pub_dir): - print "Copying: public directory of library %r" % p + print("Copying: public directory of library %r" % p) copytree_exists(pub_dir, output) - ## AppName.html - can be in current or public directory + # AppName.html - can be in current or public directory html_input_filename = app_name + ".html" html_output_filename = join(output, basename(html_input_filename)) if os.path.isfile(html_input_filename): @@ -203,28 +203,28 @@ os.path.getmtime(html_output_filename): try: shutil.copy(html_input_filename, html_output_filename) - except: - print >>sys.stderr, "Warning: Missing module HTML file %s" % html_input_filename - - print "Copying: %(html_input_filename)s" % locals() + except Exception: + print("Warning: Missing module HTML file %s" % html_input_filename, file=sys.stderr) + + print("Copying: %(html_input_filename)s" % locals()) if check_html_file(html_input_filename, output): - print >>sys.stderr, "Warning: Module HTML file %s has been auto-generated" % html_input_filename - - ## pygwt.js - - print "Copying: pygwt.js" + print("Warning: Module HTML file %s has been auto-generated" % html_input_filename, file=sys.stderr) + + # pygwt.js + + print("Copying: pygwt.js") pygwt_js_template = read_boilerplate(data_dir, "pygwt.js") pygwt_js_output = open(join(output, "pygwt.js"), "w") - print >>pygwt_js_output, pygwt_js_template + print(pygwt_js_template, file=pygwt_js_output) pygwt_js_output.close() - ## Images - - print "Copying: Images and History" + # Images + + print("Copying: Images and History") copy_boilerplate(data_dir, "corner_dialog_topleft_black.png", output) copy_boilerplate(data_dir, "corner_dialog_topright_black.png", output) copy_boilerplate(data_dir, "corner_dialog_bottomright_black.png", output) @@ -240,14 +240,13 @@ copy_boilerplate(data_dir, "tree_white.gif", output) copy_boilerplate(data_dir, "history.html", output) - - ## all.cache.html + # all.cache.html app_files = generateAppFiles(data_dir, js_includes, app_name, debug, output, dynamic, cache_buster, optimize) - ## AppName.nocache.html - - print "Creating: %(app_name)s.nocache.html" % locals() + # AppName.nocache.html + + print("Creating: %(app_name)s.nocache.html" % locals()) home_nocache_html_template = read_boilerplate(data_dir, "home.nocache.html") home_nocache_html_output = open(join(output, app_name + ".nocache.html"), @@ -258,16 +257,17 @@ script_selectors = StringIO() for platform, file_prefix in app_files: - print >> script_selectors, select_tmpl % (platform, file_prefix) - - print >>home_nocache_html_output, home_nocache_html_template % dict( - app_name = app_name, - script_selectors = script_selectors.getvalue(), - ) + print(select_tmpl % (platform, file_prefix), file=script_selectors) + + print( + home_nocache_html_template % dict( + app_name=app_name, + script_selectors=script_selectors.getvalue(), + ), file=home_nocache_html_output) home_nocache_html_output.close() - print "Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals() + print("Done. You can run your app by opening '%(html_output_filename)s' in a browser" % locals()) def generateAppFiles(data_dir, js_includes, app_name, debug, output, dynamic, @@ -280,15 +280,14 @@ for name in os.listdir(output): if CACHE_HTML_PAT.match(name): p = join(output, name) - print "Deleting existing app file %s" % p + print("Deleting existing app file %s" % p) os.unlink(p) app_files = [] - tmpl = read_boilerplate(data_dir, "all.cache.html") parser = pyjs.PlatformParser("platform") app_headers = '' - scripts = [''%script \ - for script in js_includes] + scripts = ['' % + script for script in js_includes] app_body = '\n'.join(scripts) mod_code = {} @@ -306,7 +305,7 @@ # Second, (dynamic only), post-analyse the places where modules # haven't changed # Third, write everything out. - + for platform in app_platforms: mod_code[platform] = {} @@ -324,24 +323,24 @@ app_translator = pyjs.AppTranslator( parser=parser, dynamic=dynamic, optimize=optimize) early_app_libs[platform], appcode = \ - app_translator.translate(None, is_app=False, - debug=debug, - library_modules=['dynamicajax.js', - '_pyjs.js', 'sys', - 'pyjslib']) + app_translator.translate(None, is_app=False, + debug=debug, + library_modules=['dynamicajax.js', + '_pyjs.js', 'sys', + 'pyjslib']) pover[platform].update(app_translator.overrides.items()) for mname, name in app_translator.overrides.items(): pd = overrides.setdefault(mname, {}) pd[platform] = name - print appcode - #mod_code[platform][app_name] = appcode - - # platform.Module.cache.js + print(appcode) + # mod_code[platform][app_name] = appcode + + # platform.Module.cache.js modules_done = ['pyjslib', 'sys', '_pyjs.js'] - #modules_to_do = [app_name] + app_translator.library_modules - modules_to_do = [app_name] + app_translator.library_modules + # modules_to_do = [app_name] + app_translator.library_modules + modules_to_do = [app_name] + app_translator.library_modules dependencies = {} @@ -350,13 +349,13 @@ sublist = add_subdeps(dependencies, d) modules_to_do += sublist deps = uniquify(deps) - #dependencies[app_name] = deps + # dependencies[app_name] = deps modules[platform] = modules_done + modules_to_do while modules_to_do: - #print "modules to do", modules_to_do + # print "modules to do", modules_to_do mn = modules_to_do.pop() mod_name = pyjs.strip_py(mn) @@ -371,9 +370,9 @@ parser.setPlatform(platform) mod_translator = pyjs.AppTranslator(parser=parser, optimize=optimize) mod_libs[platform][mod_name], mod_code[platform][mod_name] = \ - mod_translator.translate(mod_name, - is_app=False, - debug=debug) + mod_translator.translate(mod_name, + is_app=False, + debug=debug) pover[platform].update(mod_translator.overrides.items()) for mname, name in mod_translator.overrides.items(): pd = overrides.setdefault(mname, {}) @@ -390,22 +389,22 @@ while mod_name in deps: deps.remove(mod_name) - #print - #print - #print "modname preadd:", mod_name, deps - #print - #print + # print + # print + # print "modname preadd:", mod_name, deps + # print + # print for d in deps: sublist = add_subdeps(dependencies, d) modules_to_do += sublist modules_to_do += add_subdeps(dependencies, mod_name) - #print "modname:", mod_name, deps + # print "modname:", mod_name, deps deps = uniquify(deps) - #print "modname:", mod_name, deps + # print "modname:", mod_name, deps dependencies[mod_name] = deps - + # work out the dependency ordering of the modules - + mod_levels[platform] = make_deps(None, dependencies, modules_done) # now write everything out @@ -415,7 +414,7 @@ early_app_libs_ = early_app_libs[platform] app_libs_ = app_libs[platform] app_code_ = app_code[platform] - #modules_ = filter_mods(app_name, modules[platform]) + # modules_ = filter_mods(app_name, modules[platform]) mods = flattenlist(mod_levels[platform]) mods.reverse() modules_ = filter_mods(None, mods) @@ -427,12 +426,12 @@ mod_name = pyjs.strip_py(mod_name) override_name = "%s.%s" % (platform.lower(), mod_name) - if pover[platform].has_key(override_name): + if override_name in pover[platform]: mod_cache_name = "%s.cache.js" % (override_name) else: mod_cache_name = "%s.cache.js" % (mod_name) - print "Creating: " + mod_cache_name + print("Creating: " + mod_cache_name) modlevels = make_deps(None, dependencies, dependencies[mod_name]) @@ -456,15 +455,15 @@ else: mod_cache_html_output = StringIO() - print >>mod_cache_html_output, mod_cache_html_template % dict( - mod_name = mod_name, - app_name = app_name, - modnames = modnames, - overrides = overnames, - mod_libs = mod_libs[platform][mod_name], - dynamic = dynamic, - mod_code = mod_code_, - ) + print(mod_cache_html_template % dict( + mod_name=mod_name, + app_name=app_name, + modnames=modnames, + overrides=overnames, + mod_libs=mod_libs[platform][mod_name], + dynamic=dynamic, + mod_code=mod_code_, + ), file=mod_cache_html_output) if dynamic: mod_cache_html_output.close() @@ -473,7 +472,7 @@ app_libs_ += mod_cache_html_output.read() # write out the dependency ordering of the modules - + app_modnames = [] for md in mod_levels[platform]: @@ -489,83 +488,90 @@ overnames = map(lambda x: "'%s': '%s'" % x, pover[platform].items()) overnames = "new pyjslib.Dict({\n\t\t%s\n\t})" % ',\n\t\t'.join(overnames) - #print "platform names", platform, overnames - #print pover + # print "platform names", platform, overnames + # print pover # now write app.allcache including dependency-ordered list of # library modules file_contents = all_cache_html_template % dict( - app_name = app_name, - early_app_libs = early_app_libs_, - app_libs = app_libs_, - app_code = app_code_, - app_body = app_body, - overrides = overnames, - platform = platform.lower(), - dynamic = dynamic, - app_modnames = app_modnames, - app_headers = app_headers + app_name=app_name, + early_app_libs=early_app_libs_, + app_libs=app_libs_, + app_code=app_code_, + app_body=app_body, + overrides=overnames, + platform=platform.lower(), + dynamic=dynamic, + app_modnames=app_modnames, + app_headers=app_headers ) if cache_buster: digest = md5.new(file_contents).hexdigest() file_name = "%s.%s.%s" % (platform.lower(), app_name, digest) else: file_name = "%s.%s" % (platform.lower(), app_name) - file_name += ".cache.html" + file_name += ".cache.html" out_path = join(output, file_name) out_file = open(out_path, 'w') out_file.write(file_contents) out_file.close() app_files.append((platform.lower(), file_name)) - print "Created app file %s:%s: %s" % ( - app_name, platform, out_path) + print("Created app file %s:%s: %s" % ( + app_name, platform, out_path)) return app_files + def flattenlist(ll): res = [] for l in ll: res += l return res -# creates sub-dependencies e.g. pyjamas.ui.Widget -# creates pyjamas.ui.Widget, pyjamas.ui and pyjamas. + def subdeps(m): + """ + creates sub-dependencies e.g. pyjamas.ui.Widget + creates pyjamas.ui.Widget, pyjamas.ui and pyjamas. + """ d = [] m = m.split(".") for i in range(0, len(m)): d.append('.'.join(m[:i+1])) return d -import time def add_subdeps(deps, mod_name): sd = subdeps(mod_name) if len(sd) == 1: return [] - #print "subdeps", mod_name, sd - #print "deps", deps + # print "subdeps", mod_name, sd + # print "deps", deps res = [] for i in range(0, len(sd)-1): parent = sd[i] child = sd[i+1] - l = deps.get(child, []) - l.append(parent) - deps[child] = l + k = deps.get(child, []) + k.append(parent) + deps[child] = k if parent not in res: res.append(parent) - #print deps + # print deps return res -# makes unique and preserves list order + def uniquify(md): + """ + makes unique and preserves list order + """ res = [] for m in md: if m not in res: res.append(m) return res + def filter_mods(app_name, md): while 'sys' in md: md.remove('sys') @@ -578,6 +584,7 @@ return uniquify(md) + def filter_deps(app_name, deps): res = {} @@ -588,18 +595,20 @@ res[k] = mods return res + def has_nodeps(mod, deps): - if not deps.has_key(mod) or not deps[mod]: + if mod not in deps or not deps[mod]: return True return False + def nodeps_list(mod_list, deps): res = [] for mod in mod_list: if has_nodeps(mod, deps): res.append(mod) return res - + # this function takes a dictionary of dependent modules and # creates a list of lists. the first list will be modules # that have no dependencies; the second list will be those @@ -609,33 +618,33 @@ def make_deps(app_name, deps, mod_list): - print "Calculating Dependencies ..." + print("Calculating Dependencies ...") mod_list = filter_mods(app_name, mod_list) deps = filter_deps(app_name, deps) if not mod_list: return [] - #print mod_list - #print deps + # print mod_list + # print deps ordered_deps = [] last_len = -1 while deps: l_deps = len(deps) - #print l_deps - if l_deps==last_len: + # print l_deps + if l_deps == last_len: for m, dl in deps.items(): for d in dl: if m in deps.get(d, []): raise Exception('Circular Imports found: \n%s %s -> %s %s' % (m, dl, d, deps[d])) - #raise Exception('Could not calculate dependencies: \n%s' % deps) + # raise Exception('Could not calculate dependencies: \n%s' % deps) break last_len = l_deps - #print "modlist", mod_list + # print "modlist", mod_list nodeps = nodeps_list(mod_list, deps) - #print "nodeps", nodeps + # print "nodeps", nodeps mod_list = filter(lambda x: x not in nodeps, mod_list) newdeps = {} for k in deps.keys(): @@ -643,45 +652,86 @@ depslist = filter(lambda x: x not in nodeps, depslist) if depslist: newdeps[k] = depslist - #print "newdeps", newdeps + # print "newdeps", newdeps deps = newdeps ordered_deps.append(nodeps) - #time.sleep(0) + # time.sleep(0) if mod_list: - ordered_deps.append(mod_list) # last dependencies - usually the app(s) + ordered_deps.append(mod_list) # last dependencies - usually the app(s) ordered_deps.reverse() return ordered_deps + def main(): global app_platforms - parser = OptionParser(usage = usage, version = version) - parser.add_option("-o", "--output", dest="output", - help="directory to which the webapp should be written") - parser.add_option("-j", "--include-js", dest="js_includes", action="append", - help="javascripts to load into the same frame as the rest of the script") - parser.add_option("-I", "--library_dir", dest="library_dirs", - action="append", help="additional paths appended to PYJSPATH") - parser.add_option("-D", "--data_dir", dest="data_dir", - help="path for data directory") - parser.add_option("-m", "--dynamic-modules", action="store_true", - dest="dynamic", default=False, - help="Split output into separate dynamically-loaded modules (experimental)") - parser.add_option("-P", "--platforms", dest="platforms", - help="platforms to build for, comma-separated") - parser.add_option("-d", "--debug", action="store_true", dest="debug") - parser.add_option("-O", "--optimize", action="store_true", - dest="optimize", default=False, - help="Optimize generated code (removes all print statements)", - ) - parser.add_option("-c", "--cache_buster", action="store_true", - dest="cache_buster", - help="Enable browser cache-busting (MD5 hash added to output filenames)") - - parser.set_defaults(output = "output", js_includes=[], library_dirs=[], + parser = OptionParser(usage=usage, version=version) + parser.add_option( + "-o", + "--output", + dest="output", + help="directory to which the webapp should be written" + ) + parser.add_option( + "-j", + "--include-js", + dest="js_includes", + action="append", + help="javascripts to load into the same frame as the rest of the script" + ) + parser.add_option( + "-I", + "--library_dir", + dest="library_dirs", + action="append", + help="additional paths appended to PYJSPATH" + ) + parser.add_option( + "-D", + "--data_dir", + dest="data_dir", + help="path for data directory" + ) + parser.add_option( + "-m", + "--dynamic-modules", + action="store_true", + dest="dynamic", + default=False, + help="Split output into separate dynamically-loaded modules (experimental)" + ) + parser.add_option( + "-P", + "--platforms", + dest="platforms", + help="platforms to build for, comma-separated" + ) + parser.add_option( + "-d", + "--debug", + action="store_true", + dest="debug" + ) + parser.add_option( + "-O", + "--optimize", + action="store_true", + dest="optimize", + default=False, + help="Optimize generated code (removes all print statements)", + ) + parser.add_option( + "-c", + "--cache_buster", + action="store_true", + dest="cache_buster", + help="Enable browser cache-busting (MD5 hash added to output filenames)" + ) + + parser.set_defaults(output="output", js_includes=[], library_dirs=[], platforms=(','.join(app_platforms)), data_dir=os.path.join(sys.prefix, "share/pyjamas"), dynamic=False, @@ -710,7 +760,7 @@ pyjs.path.append(abspath(d)) if options.platforms: - app_platforms = options.platforms.split(',') + app_platforms = options.platforms.split(',') # this is mostly for getting boilerplate stuff data_dir = os.path.abspath(options.data_dir) @@ -719,6 +769,6 @@ options.debug, options.dynamic and 1 or 0, data_dir, options.cache_buster, options.optimize) + if __name__ == "__main__": main() - diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/jsonrpc/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/svgui/pyjs/jsonrpc/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,1 @@ +# module jsonrpc diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/jsonrpc/django/jsonrpc.py --- a/svgui/pyjs/jsonrpc/django/jsonrpc.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/jsonrpc/django/jsonrpc.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,11 +1,14 @@ # jsonrpc.py # original code: http://trac.pyworks.org/pyjamas/wiki/DjangoWithPyJamas # also from: http://www.pimentech.fr/technologies/outils -from django.utils import simplejson -from django.http import HttpResponse -import sys - -from pyjs.jsonrpc import JSONRPCServiceBase + +from __future__ import absolute_import +import datetime + +from django.core.serializers import serialize + + +from svgui.pyjs.jsonrpc.jsonrpc import JSONRPCServiceBase # JSONRPCService and jsonremote are used in combination to drastically # simplify the provision of JSONRPC services. use as follows: # @@ -18,14 +21,16 @@ # dump jsonservice into urlpatterns: # (r'^service1/$', 'djangoapp.views.jsonservice'), + class JSONRPCService(JSONRPCServiceBase): - + def __call__(self, request, extra=None): return self.process(request.raw_post_data) + def jsonremote(service): """Make JSONRPCService a decorator so that you can write : - + from jsonrpc import JSONRPCService chatservice = JSONRPCService() @@ -38,7 +43,7 @@ service.add_method(func.__name__, func) else: emsg = 'Service "%s" not found' % str(service.__name__) - raise NotImplementedError, emsg + raise NotImplementedError(emsg) return func return remotify @@ -62,7 +67,6 @@ # part of the app: # (r'^formsservice/$', 'djangoapp.views.processor'), -from django import forms def builderrors(form): d = {} @@ -76,20 +80,21 @@ # contains the list of arguments in each field field_names = { - 'CharField': ['max_length', 'min_length'], - 'IntegerField': ['max_value', 'min_value'], - 'FloatField': ['max_value', 'min_value'], - 'DecimalField': ['max_value', 'min_value', 'max_digits', 'decimal_places'], - 'DateField': ['input_formats'], - 'DateTimeField': ['input_formats'], - 'TimeField': ['input_formats'], - 'RegexField': ['max_length', 'min_length'], # sadly we can't get the expr - 'EmailField': ['max_length', 'min_length'], - 'URLField': ['max_length', 'min_length', 'verify_exists', 'user_agent'], - 'ChoiceField': ['choices'], - 'FilePathField': ['path', 'match', 'recursive', 'choices'], - 'IPAddressField': ['max_length', 'min_length'], - } + 'CharField': ['max_length', 'min_length'], + 'IntegerField': ['max_value', 'min_value'], + 'FloatField': ['max_value', 'min_value'], + 'DecimalField': ['max_value', 'min_value', 'max_digits', 'decimal_places'], + 'DateField': ['input_formats'], + 'DateTimeField': ['input_formats'], + 'TimeField': ['input_formats'], + 'RegexField': ['max_length', 'min_length'], # sadly we can't get the expr + 'EmailField': ['max_length', 'min_length'], + 'URLField': ['max_length', 'min_length', 'verify_exists', 'user_agent'], + 'ChoiceField': ['choices'], + 'FilePathField': ['path', 'match', 'recursive', 'choices'], + 'IPAddressField': ['max_length', 'min_length'], +} + def describe_field_errors(field): res = {} @@ -102,6 +107,7 @@ res['fields'] = map(describe_field, field.fields) return res + def describe_fields_errors(fields, field_names): res = {} if not field_names: @@ -111,16 +117,18 @@ res[name] = describe_field_errors(field) return res + def describe_field(field): res = {} field_type = field.__class__.__name__ - for fname in field_names.get(field_type, []) + \ - ['help_text', 'label', 'initial', 'required']: + for fname in (field_names.get(field_type, []) + + ['help_text', 'label', 'initial', 'required']): res[fname] = getattr(field, fname) if field_type in ['ComboField', 'MultiValueField', 'SplitDateTimeField']: res['fields'] = map(describe_field, field.fields) return res + def describe_fields(fields, field_names): res = {} if not field_names: @@ -130,13 +138,14 @@ res[name] = describe_field(field) return res + class FormProcessor(JSONRPCService): def __init__(self, forms, _formcls=None): if _formcls is None: JSONRPCService.__init__(self) for k in forms.keys(): - s = FormProcessor({}, forms[k]) + s = FormProcessor({}, forms[k]) self.add_method(k, s.__process) else: JSONRPCService.__init__(self, forms) @@ -146,33 +155,30 @@ f = self.formcls(params) - if command is None: # just validate + if command is None: # just validate if not f.is_valid(): - return {'success':False, 'errors': builderrors(f)} - return {'success':True} - - elif command.has_key('describe_errors'): + return {'success': False, 'errors': builderrors(f)} + return {'success': True} + + elif 'describe_errors' in command: field_names = command['describe_errors'] return describe_fields_errors(f.fields, field_names) - elif command.has_key('describe'): + elif 'describe' in command: field_names = command['describe'] return describe_fields(f.fields, field_names) - elif command.has_key('save'): + elif 'save' in command: if not f.is_valid(): - return {'success':False, 'errors': builderrors(f)} - instance = f.save() # XXX: if you want more, over-ride save. - return {'success': True, 'instance': json_convert(instance) } - - elif command.has_key('html'): + return {'success': False, 'errors': builderrors(f)} + instance = f.save() # XXX: if you want more, over-ride save. + return {'success': True, 'instance': json_convert(instance)} + + elif 'html' in command: return {'success': True, 'html': f.as_table()} return "unrecognised command" - - - # The following is incredibly convenient for saving vast amounts of # coding, avoiding doing silly things like this: # jsonresult = {'field1': djangoobject.field1, @@ -202,9 +208,6 @@ # list_some_model and list_another_model part of the django app: # (r'^service1/$', 'djangoapp.views.jsonservice'), -from django.core.serializers import serialize -import datetime -from datetime import date def dict_datetimeflatten(item): d = {} @@ -218,9 +221,9 @@ d[k] = v return d + def json_convert(l, fields=None): res = [] for item in serialize('python', l, fields=fields): res.append(dict_datetimeflatten(item)) return res - diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/jsonrpc/jsonrpc.py --- a/svgui/pyjs/jsonrpc/jsonrpc.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/jsonrpc/jsonrpc.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,23 +1,25 @@ +from __future__ import absolute_import +import sys import gluon.contrib.simplejson as simplejson -import types -import sys -class JSONRPCServiceBase: + +class JSONRPCServiceBase(object): def __init__(self): - self.methods={} + self.methods = {} def response(self, id, result): - return simplejson.dumps({'version': '1.1', 'id':id, - 'result':result, 'error':None}) + return simplejson.dumps({'version': '1.1', 'id': id, + 'result': result, 'error': None}) + def error(self, id, code, message): - return simplejson.dumps({'id': id, - 'version': '1.1', - 'error': {'name': 'JSONRPCError', - 'code': code, - 'message': message - } - }) + return simplejson.dumps({ + 'id': id, + 'version': '1.1', + 'error': {'name': 'JSONRPCError', + 'code': code, + 'message': message} + }) def add_method(self, name, method): self.methods[name] = method @@ -27,17 +29,16 @@ id, method, params = data["id"], data["method"], data["params"] if method in self.methods: try: - result =self.methods[method](*params) + result = self.methods[method](*params) return self.response(id, result) + except Exception: + etype, eval, _etb = sys.exc_info() + return self.error(id, 100, 'Exception %s: %s' % (etype, eval)) except BaseException: - etype, eval, etb = sys.exc_info() - return self.error(id, 100, '%s: %s' %(etype.__name__, eval)) - except: - etype, eval, etb = sys.exc_info() - return self.error(id, 100, 'Exception %s: %s' %(etype, eval)) + etype, eval, _etb = sys.exc_info() + return self.error(id, 100, '%s: %s' % (etype.__name__, eval)) else: return self.error(id, 100, 'method "%s" does not exist' % method) def listmethods(self): - return self.methods.keys() - + return self.methods.keys() diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/jsonrpc/web2py/jsonrpc.py --- a/svgui/pyjs/jsonrpc/web2py/jsonrpc.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/jsonrpc/web2py/jsonrpc.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,11 +1,14 @@ -from pyjs.jsonrpc import JSONRPCServiceBase +# pylint: disable=undefined-variable + +from __future__ import absolute_import +from svgui.pyjs.jsonrpc.jsonrpc import JSONRPCServiceBase + class JSONRPCService(JSONRPCServiceBase): def serve(self): return self.process(request.body.read()) - def __call__(self,func): - self.methods[func.__name__]=func + def __call__(self, func): + self.methods[func.__name__] = func return func - diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/lib/pyjslib.py --- a/svgui/pyjs/lib/pyjslib.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/lib/pyjslib.py Tue Jan 30 16:06:58 2018 +0100 @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +# pylint: disable=too-many-function-args,undefined-variable,no-absolute-import # iteration from Bob Ippolito's Iteration in JavaScript @@ -19,9 +20,8 @@ # must declare import _before_ importing sys + def import_module(path, parent_module, module_name, dynamic=1, async=False): - """ - """ JS(""" var cache_file; @@ -38,7 +38,7 @@ } var override_name = sys.platform + "." + module_name; - if (((sys.overrides != null) && + if (((sys.overrides != null) && (sys.overrides.has_key(override_name)))) { cache_file = sys.overrides.__getitem__(override_name) ; @@ -67,7 +67,7 @@ module_load_request[module_name] = 1; } - /* following a load, this first executes the script + /* following a load, this first executes the script * "preparation" function MODULENAME_loaded_fn() * and then sets up the loaded module in the namespace * of the parent. @@ -105,6 +105,7 @@ """) + JS(""" function import_wait(proceed_fn, parent_mod, dynamic) { @@ -184,11 +185,16 @@ } """) + +# pylint: disable=old-style-class class Object: - pass + def __init__(self): + pass + object = Object + class Modload: def __init__(self, path, app_modlist, app_imported_fn, dynamic, @@ -196,15 +202,15 @@ self.app_modlist = app_modlist self.app_imported_fn = app_imported_fn self.path = path - self.idx = 0; + self.idx = 0 self.dynamic = dynamic self.parent_mod = parent_mod def next(self): - + for i in range(len(self.app_modlist[self.idx])): app = self.app_modlist[self.idx][i] - import_module(self.path, self.parent_mod, app, self.dynamic, True); + import_module(self.path, self.parent_mod, app, self.dynamic, True) self.idx += 1 if self.idx >= len(self.app_modlist): @@ -212,18 +218,24 @@ else: import_wait(getattr(self, "next"), self.parent_mod, self.dynamic) + def get_module(module_name): - ev = "__mod = %s;" % module_name - JS("pyjs_eval(ev);") + _ev = "__mod = %s;" % module_name + JS("pyjs_eval(_ev);") return __mod + def preload_app_modules(path, app_modnames, app_imported_fn, dynamic, parent_mod=None): loader = Modload(path, app_modnames, app_imported_fn, dynamic, parent_mod) loader.next() -import sys + +# as comment on line 20 says +# import sys should be below +import sys # noqa # pylint: disable=wrong-import-order,unused-import,wrong-import-position + class BaseException: @@ -242,32 +254,37 @@ def toString(self): return str(self) + class Exception(BaseException): - name = "Exception" + class TypeError(BaseException): name = "TypeError" + class StandardError(Exception): name = "StandardError" + class LookupError(StandardError): name = "LookupError" def toString(self): return self.name + ": " + self.args[0] + class KeyError(LookupError): name = "KeyError" + class AttributeError(StandardError): - name = "AttributeError" def toString(self): return "AttributeError: %s of %s" % (self.args[1], self.args[0]) + JS(""" pyjslib.StopIteration = function () { }; pyjslib.StopIteration.prototype = new Error(); @@ -407,6 +424,7 @@ """) + class Class: def __init__(self, name): self.name = name @@ -414,7 +432,8 @@ def __str___(self): return self.name -def eq(a,b): + +def eq(a, b): JS(""" if (pyjslib.hasattr(a, "__cmp__")) { return a.__cmp__(b) == 0; @@ -424,7 +443,8 @@ return a == b; """) -def cmp(a,b): + +def cmp(a, b): if hasattr(a, "__cmp__"): return a.__cmp__(b) elif hasattr(b, "__cmp__"): @@ -436,6 +456,7 @@ else: return 0 + def bool(v): # this needs to stay in native code without any dependencies here, # because this is used by if and while, we need to prevent @@ -456,6 +477,7 @@ return Boolean(v); """) + class List: def __init__(self, data=None): JS(""" @@ -511,7 +533,7 @@ def insert(self, index, value): JS(""" var a = this.l; this.l=a.slice(0, index).concat(value, a.slice(index));""") - def pop(self, index = -1): + def pop(self, index=-1): JS(""" if (index<0) index = this.l.length + index; var a = this.l[index]; @@ -577,18 +599,17 @@ def sort(self, compareFunc=None, keyFunc=None, reverse=False): if not compareFunc: - global cmp compareFunc = cmp if keyFunc and reverse: - def thisSort1(a,b): + def thisSort1(a, b): return -compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort1) elif keyFunc: - def thisSort2(a,b): + def thisSort2(a, b): return compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort2) elif reverse: - def thisSort3(a,b): + def thisSort3(a, b): return -compareFunc(a, b) self.l.sort(thisSort3) else: @@ -603,8 +624,10 @@ def __str__(self): return repr(self) + list = List + class Tuple: def __init__(self, data=None): JS(""" @@ -660,7 +683,7 @@ def insert(self, index, value): JS(""" var a = this.l; this.l=a.slice(0, index).concat(value, a.slice(index));""") - def pop(self, index = -1): + def pop(self, index=-1): JS(""" if (index<0) index = this.l.length + index; var a = this.l[index]; @@ -726,18 +749,17 @@ def sort(self, compareFunc=None, keyFunc=None, reverse=False): if not compareFunc: - global cmp compareFunc = cmp if keyFunc and reverse: - def thisSort1(a,b): + def thisSort1(a, b): return -compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort1) elif keyFunc: - def thisSort2(a,b): + def thisSort2(a, b): return compareFunc(keyFunc(a), keyFunc(b)) self.l.sort(thisSort2) elif reverse: - def thisSort3(a,b): + def thisSort3(a, b): return -compareFunc(a, b) self.l.sort(thisSort3) else: @@ -752,6 +774,7 @@ def __str__(self): return repr(self) + tuple = Tuple @@ -866,22 +889,22 @@ return self.__iter__() def itervalues(self): - return self.values().__iter__(); + return self.values().__iter__() def iteritems(self): - return self.items().__iter__(); + return self.items().__iter__() def setdefault(self, key, default_value): - if not self.has_key(key): + if key not in self: self[key] = default_value def get(self, key, default_=None): - if not self.has_key(key): + if key not in self: return default_ return self[key] def update(self, d): - for k,v in d.iteritems(): + for k, v in d.iteritems(): self[k] = v def getObject(self): @@ -897,9 +920,12 @@ def __str__(self): return repr(self) + dict = Dict # taken from mochikit: range( [start,] stop[, step] ) + + def range(): JS(""" var start = 0; @@ -930,6 +956,7 @@ } """) + def slice(object, lower, upper): JS(""" if (pyjslib.isString(object)) { @@ -948,6 +975,7 @@ return null; """) + def str(text): JS(""" if (pyjslib.hasattr(text,"__str__")) { @@ -956,8 +984,9 @@ return String(text); """) + def ord(x): - if(isString(x) and len(x) is 1): + if isString(x) and len(x) is 1: JS(""" return x.charCodeAt(0); """) @@ -967,11 +996,13 @@ """) return None + def chr(x): JS(""" return String.fromCharCode(x) """) + def is_basetype(x): JS(""" var t = typeof(x); @@ -983,6 +1014,7 @@ ; """) + def get_pyjs_classtype(x): JS(""" if (pyjslib.hasattr(x, "__class__")) @@ -992,6 +1024,7 @@ return null; """) + def repr(x): """ Return the string representation of 'x'. """ @@ -1088,16 +1121,19 @@ return "<" + constructor + " object>"; """) + def float(text): JS(""" return parseFloat(text); """) + def int(text, radix=0): JS(""" return parseInt(text, radix); """) + def len(object): JS(""" if (object==null) return 0; @@ -1105,11 +1141,12 @@ return object.length; """) + def isinstance(object_, classinfo): if pyjslib.isUndefined(object_): return False if not pyjslib.isObject(object_): - + return False if _isinstance(classinfo, Tuple): for ci in classinfo: @@ -1119,6 +1156,7 @@ else: return _isinstance(object_, classinfo) + def _isinstance(object_, classinfo): if not pyjslib.isObject(object_): return False @@ -1130,7 +1168,8 @@ return false; """) -def getattr(obj, name, default_): + +def getattr(obj, name, default_=None): JS(""" if ((!pyjslib.isObject(obj))||(pyjslib.isUndefined(obj[name]))){ if (pyjslib.isUndefined(default_)){ @@ -1151,6 +1190,7 @@ return fnwrap; """) + def setattr(obj, name, value): JS(""" if (!pyjslib.isObject(obj)) return null; @@ -1159,6 +1199,7 @@ """) + def hasattr(obj, name): JS(""" if (!pyjslib.isObject(obj)) return false; @@ -1167,6 +1208,7 @@ return true; """) + def dir(obj): JS(""" var properties=new pyjslib.List(); @@ -1174,6 +1216,7 @@ return properties; """) + def filter(obj, method, sequence=None): # object context is LOST when a method is passed, hence object must be passed separately # to emulate python behaviour, should generate this code inline rather than as a function call @@ -1240,6 +1283,7 @@ next_hash_id = 0 + def hash(obj): JS(""" if (obj == null) return null; @@ -1259,41 +1303,49 @@ return (a != null && (typeof a == 'object')) || pyjslib.isFunction(a); """) + def isFunction(a): JS(""" return typeof a == 'function'; """) + def isString(a): JS(""" return typeof a == 'string'; """) + def isNull(a): JS(""" return typeof a == 'object' && !a; """) + def isArray(a): JS(""" return pyjslib.isObject(a) && a.constructor == Array; """) + def isUndefined(a): JS(""" return typeof a == 'undefined'; """) + def isIteratable(a): JS(""" return pyjslib.isString(a) || (pyjslib.isObject(a) && a.__iter__); """) + def isNumber(a): JS(""" return typeof a == 'number' && isFinite(a); """) + def toJSObjects(x): """ Convert the pyjs pythonic List and Dict objects into javascript Object and Array @@ -1337,6 +1389,7 @@ """) return x + def printFunc(objs): JS(""" if ($wnd.console==undefined) return; @@ -1348,6 +1401,7 @@ console.debug(s) """) + def type(clsname, bases=None, methods=None): """ creates a class, derived from bases, with methods and variables """ @@ -1355,11 +1409,10 @@ JS(" var mths = {}; ") if methods: for k in methods.keys(): - mth = methods[k] - JS(" mths[k] = mth; ") + _mth = methods[k] + JS(" mths[k] = _mth; ") JS(" var bss = null; ") if bases: JS("bss = bases.l;") JS(" return pyjs_type(clsname, bss, mths); ") - diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/lib/sys.py --- a/svgui/pyjs/lib/sys.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/lib/sys.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,32 +1,35 @@ # the platform name (PyV8, smjs, Mozilla, IE6, Opera, Safari etc.) -platform = '' # to be updated by app, on compile +platform = '' # to be updated by app, on compile # a dictionary of module override names (platform-specific) -overrides = None # to be updated by app, on compile +overrides = {} # to be updated by app, on compile # the remote path for loading modules -loadpath = None +loadpath = None -stacktrace = None +stacktrace = None -appname = None +appname = None + def setloadpath(lp): global loadpath loadpath = lp + def setappname(an): global appname appname = an + def getloadpath(): - global loadpath return loadpath + def addoverride(module_name, path): - global overrides overrides[module_name] = path + def addstack(linedebug): JS(""" if (pyjslib.bool((sys.stacktrace === null))) { @@ -34,11 +37,14 @@ } sys.stacktrace.append(linedebug); """) + + def popstack(): JS(""" sys.stacktrace.pop() """) + def printstack(): JS(""" var res = ''; diff -r c1298e7ffe3a -r 8391c11477f4 svgui/pyjs/pyjs.py --- a/svgui/pyjs/pyjs.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/pyjs/pyjs.py Tue Jan 30 16:06:58 2018 +0100 @@ -12,14 +12,17 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - - +# +# pylint: disable=no-absolute-import + +from __future__ import print_function import sys from types import StringType import compiler from compiler import ast import os import copy +import cStringIO # the standard location for builtins (e.g. pyjslib) can be # over-ridden by changing this. it defaults to sys.prefix @@ -31,7 +34,7 @@ prefix = sys.prefix -if os.environ.has_key('PYJSPREFIX'): +if 'PYJSPREFIX' in os.environ: prefix = os.environ['PYJSPREFIX'] # pyjs.path is the list of paths, just like sys.path, from which @@ -41,7 +44,7 @@ path = [os.path.abspath('')] -if os.environ.has_key('PYJSPATH'): +if 'PYJSPATH' in os.environ: for p in os.environ['PYJSPATH'].split(os.pathsep): p = os.path.abspath(p) if os.path.isdir(p): @@ -52,43 +55,43 @@ UU = "" -PYJSLIB_BUILTIN_FUNCTIONS=("cmp", - "map", - "filter", - "dir", - "getattr", - "setattr", - "hasattr", - "int", - "float", - "str", - "repr", - "range", - "len", - "hash", - "abs", - "ord", - "chr", - "enumerate", - "min", - "max", - "bool", - "type", - "isinstance") - -PYJSLIB_BUILTIN_CLASSES=("BaseException", - "Exception", - "StandardError", - "StopIteration", - "AttributeError", - "TypeError", - "KeyError", - "LookupError", - "list", - "dict", - "object", - "tuple", - ) +PYJSLIB_BUILTIN_FUNCTIONS = ("cmp", + "map", + "filter", + "dir", + "getattr", + "setattr", + "hasattr", + "int", + "float", + "str", + "repr", + "range", + "len", + "hash", + "abs", + "ord", + "chr", + "enumerate", + "min", + "max", + "bool", + "type", + "isinstance") + +PYJSLIB_BUILTIN_CLASSES = ("BaseException", + "Exception", + "StandardError", + "StopIteration", + "AttributeError", + "TypeError", + "KeyError", + "LookupError", + "list", + "dict", + "object", + "tuple") + def pyjs_builtin_remap(name): # XXX HACK! @@ -102,10 +105,11 @@ name = 'Tuple' return name + # XXX: this is a hack: these should be dealt with another way # however, console is currently the only global name which is causing # problems. -PYJS_GLOBAL_VARS=("console") +PYJS_GLOBAL_VARS = ("console") # This is taken from the django project. # Escape every ASCII character with a value less than 32. @@ -119,18 +123,21 @@ (';', r'\x3B') ) + tuple([('%c' % z, '\\x%02X' % z) for z in range(32)]) + def escapejs(value): """Hex encodes characters for use in JavaScript strings.""" for bad, good in JS_ESCAPES: value = value.replace(bad, good) return value + def uuprefix(name, leave_alone=0): name = name.split(".") name = name[:leave_alone] + map(lambda x: "__%s" % x, name[leave_alone:]) return '.'.join(name) -class Klass: + +class Klass(object): klasses = {} @@ -149,14 +156,17 @@ class TranslationError(Exception): def __init__(self, message, node): + Exception.__init__(self) self.message = "line %s:\n%s\n%s" % (node.lineno, message, node) def __str__(self): return self.message + def strip_py(name): return name + def mod_var_name_decl(raw_module_name): """ function to get the last component of the module e.g. pyjamas.ui.DOM into the "namespace". i.e. doing @@ -174,14 +184,16 @@ child_name = name[-1] return "var %s = %s;\n" % (child_name, raw_module_name) + def gen_mod_import(parentName, importName, dynamic=1): - #pyjs_ajax_eval("%(n)s.cache.js", null, true); + # pyjs_ajax_eval("%(n)s.cache.js", null, true); return """ pyjslib.import_module(sys.loadpath, '%(p)s', '%(n)s', %(d)d, false); """ % ({'p': parentName, 'd': dynamic, 'n': importName}) + \ - mod_var_name_decl(importName) - -class Translator: + mod_var_name_decl(importName) + + +class Translator(object): def __init__(self, mn, module_name, raw_module_name, src, debug, mod, output, dynamic=0, optimize=False, @@ -217,37 +229,36 @@ vdec = '' else: vdec = 'var ' - print >>self.output, UU+"%s%s = function (__mod_name__) {" % (vdec, module_name) - - print >>self.output, " if("+module_name+".__was_initialized__) return;" - print >>self.output, " "+UU+module_name+".__was_initialized__ = true;" - print >>self.output, UU+"if (__mod_name__ == null) __mod_name__ = '%s';" % (mn) - print >>self.output, UU+"%s.__name__ = __mod_name__;" % (raw_module_name) + self.printo(UU+"%s%s = function (__mod_name__) {" % (vdec, module_name)) + + self.printo(" if("+module_name+".__was_initialized__) return;") + self.printo(" "+UU+module_name+".__was_initialized__ = true;") + self.printo(UU+"if (__mod_name__ == null) __mod_name__ = '%s';" % (mn)) + self.printo(UU+"%s.__name__ = __mod_name__;" % (raw_module_name)) decl = mod_var_name_decl(raw_module_name) if decl: - print >>self.output, decl - + self.printo(decl) if self.debug: haltException = self.module_prefix + "HaltException" - print >>self.output, haltException + ' = function () {' - print >>self.output, ' this.message = "Program Halted";' - print >>self.output, ' this.name = "' + haltException + '";' - print >>self.output, '}' - print >>self.output, '' - print >>self.output, haltException + ".prototype.__str__ = function()" - print >>self.output, '{' - print >>self.output, 'return this.message ;' - print >>self.output, '}' - - print >>self.output, haltException + ".prototype.toString = function()" - print >>self.output, '{' - print >>self.output, 'return this.name + ": \\"" + this.message + "\\"";' - print >>self.output, '}' + self.printo(haltException + ' = function () {') + self.printo(' this.message = "Program Halted";') + self.printo(' this.name = "' + haltException + '";') + self.printo('}') + self.printo('') + self.printo(haltException + ".prototype.__str__ = function()") + self.printo('{') + self.printo('return this.message ;') + self.printo('}') + + self.printo(haltException + ".prototype.toString = function()") + self.printo('{') + self.printo('return this.name + ": \\"" + this.message + "\\"";') + self.printo('}') isHaltFunction = self.module_prefix + "IsHaltException" - print >>self.output, """ + self.printo(""") %s = function (s) { var suffix="HaltException"; if (s.length < suffix.length) { @@ -259,7 +270,7 @@ return ss == suffix; } } - """ % isHaltFunction + """ % isHaltFunction) for child in mod.node: if isinstance(child, ast.Function): self.top_level_functions.add(child.name) @@ -273,14 +284,14 @@ self._class(child) elif isinstance(child, ast.Import): importName = child.names[0][0] - if importName == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter + if importName == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter pass elif importName.endswith('.js'): - self.imported_js.add(importName) + self.imported_js.add(importName) else: - self.add_imported_module(strip_py(importName)) + self.add_imported_module(strip_py(importName)) elif isinstance(child, ast.From): - if child.modname == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter + if child.modname == '__pyjamas__': # special module to help make pyjamas modules loadable in the python interpreter pass else: self.add_imported_module(child.modname) @@ -302,9 +313,9 @@ elif isinstance(child, ast.Global): self._global(child, None) elif isinstance(child, ast.Printnl): - self._print(child, None) + self._print(child, None) elif isinstance(child, ast.Print): - self._print(child, None) + self._print(child, None) elif isinstance(child, ast.TryExcept): self._tryExcept(child, None) elif isinstance(child, ast.Raise): @@ -315,14 +326,17 @@ raise TranslationError("unsupported type (in __init__)", child) # Initialize all classes for this module - #print >> self.output, "__"+self.modpfx()+\ - # "classes_initialize = function() {\n" - #for className in self.top_level_classes: - # print >> self.output, "\t"+UU+self.modpfx()+"__"+className+"_initialize();" - #print >> self.output, "};\n" - - print >> self.output, "return this;\n" - print >> self.output, "}; /* end %s */ \n" % module_name + # self.printo("__"+self.modpfx()+\ + # "classes_initialize = function() {\n") + # for className in self.top_level_classes: + # self.printo("\t"+UU+self.modpfx()+"__"+className+"_initialize();") + # self.printo("};\n") + + self.printo("return this;\n") + self.printo("}; /* end %s */ \n" % module_name) + + def printo(self, *args): + print(*args, file=self.output) def module_imports(self): return self.imported_modules + self.imported_modules_as @@ -345,10 +359,10 @@ # added to the dependencies, and it's half way up the # module import directory structure! child_name = name[-1] - self.imported_modules_as.append(child_name) - print >> self.output, gen_mod_import(self.raw_module_name, - strip_py(importName), - self.dynamic) + self.imported_modules_as.append(child_name) + self.printo(gen_mod_import(self.raw_module_name, + strip_py(importName), + self.dynamic)) def _default_args_handler(self, node, arg_names, current_klass, output=None): @@ -369,45 +383,46 @@ default_name = arg_names[default_pos] default_pos += 1 - print >> output, " if (typeof %s == 'undefined') %s=%s;" % (default_name, default_name, default_value) + self.printo(" if (typeof %s == 'undefined') %s=%s;" % (default_name, default_name, default_value)) def _varargs_handler(self, node, varargname, arg_names, current_klass): - print >>self.output, " var", varargname, '= new pyjslib.Tuple();' - print >>self.output, " for(var __va_arg="+str(len(arg_names))+"; __va_arg < arguments.length; __va_arg++) {" - print >>self.output, " var __arg = arguments[__va_arg];" - print >>self.output, " "+varargname+".append(__arg);" - print >>self.output, " }" + self.printo(" var", varargname, '= new pyjslib.Tuple();') + self.printo(" for(var __va_arg="+str(len(arg_names))+"; __va_arg < arguments.length; __va_arg++) {") + self.printo(" var __arg = arguments[__va_arg];") + self.printo(" "+varargname+".append(__arg);") + self.printo(" }") def _kwargs_parser(self, node, function_name, arg_names, current_klass): if len(node.defaults) or node.kwargs: default_pos = len(arg_names) - len(node.defaults) if arg_names and arg_names[0] == self.method_self: default_pos -= 1 - print >>self.output, function_name+'.parse_kwargs = function (', ", ".join(["__kwargs"]+arg_names), ") {" - for default_node in node.defaults: - default_value = self.expr(default_node, current_klass) -# if isinstance(default_node, ast.Const): -# default_value = self._const(default_node) -# elif isinstance(default_node, ast.Name): -# default_value = self._name(default_node) -# elif isinstance(default_node, ast.UnarySub): -# default_value = self._unarysub(default_node, current_klass) -# else: -# raise TranslationError("unsupported type (in _method)", default_node) + self.printo(function_name+'.parse_kwargs = function (', ", ".join(["__kwargs"]+arg_names), ") {") + for _default_node in node.defaults: + # default_value = self.expr(default_node, current_klass) + # if isinstance(default_node, ast.Const): + # default_value = self._const(default_node) + # elif isinstance(default_node, ast.Name): + # default_value = self._name(default_node) + # elif isinstance(default_node, ast.UnarySub): + # default_value = self._unarysub(default_node, current_klass) + # else: + # raise TranslationError("unsupported type (in _method)", default_node) default_name = arg_names[default_pos] - print >>self.output, " if (typeof %s == 'undefined')"%(default_name) - print >>self.output, " %s=__kwargs.%s;"% (default_name, default_name) + self.printo(" if (typeof %s == 'undefined')" % (default_name)) + self.printo(" %s=__kwargs.%s;" % (default_name, default_name)) default_pos += 1 - #self._default_args_handler(node, arg_names, current_klass) - if node.kwargs: arg_names += ["pyjslib.Dict(__kwargs)"] - print >>self.output, " var __r = "+"".join(["[", ", ".join(arg_names), "]"])+";" + # self._default_args_handler(node, arg_names, current_klass) + if node.kwargs: + arg_names += ["pyjslib.Dict(__kwargs)"] + self.printo(" var __r = "+"".join(["[", ", ".join(arg_names), "]"])+";") if node.varargs: self._varargs_handler(node, "__args", arg_names, current_klass) - print >>self.output, " __r.push.apply(__r, __args.getArray())" - print >>self.output, " return __r;" - print >>self.output, "};" + self.printo(" __r.push.apply(__r, __args.getArray())") + self.printo(" return __r;") + self.printo("};") def _function(self, node, local=False): if local: @@ -418,16 +433,19 @@ arg_names = list(node.argnames) normal_arg_names = list(arg_names) - if node.kwargs: kwargname = normal_arg_names.pop() - if node.varargs: varargname = normal_arg_names.pop() + if node.kwargs: + kwargname = normal_arg_names.pop() + if node.varargs: + varargname = normal_arg_names.pop() declared_arg_names = list(normal_arg_names) - if node.kwargs: declared_arg_names.append(kwargname) + if node.kwargs: + declared_arg_names.append(kwargname) function_args = "(" + ", ".join(declared_arg_names) + ")" - print >>self.output, "%s = function%s {" % (function_name, function_args) + self.printo("%s = function%s {" % (function_name, function_args)) self._default_args_handler(node, normal_arg_names, None) - local_arg_names = normal_arg_names + declared_arg_names + local_arg_names = normal_arg_names + declared_arg_names if node.varargs: self._varargs_handler(node, varargname, declared_arg_names, None) @@ -446,29 +464,24 @@ lastStmt = [p for p in node.code][-1] if not isinstance(lastStmt, ast.Return): if not self._isNativeFunc(lastStmt): - print >>self.output, " return null;" - - print >>self.output, "};" - print >>self.output, "%s.__name__ = '%s';\n" % (function_name, node.name) - + self.printo(" return null;") + + self.printo("};") + self.printo("%s.__name__ = '%s';\n" % (function_name, node.name)) self._kwargs_parser(node, function_name, normal_arg_names, None) - def _return(self, node, current_klass): expr = self.expr(node.value, current_klass) # in python a function call always returns None, so we do it # here too - print >>self.output, " return " + expr + ";" - + self.printo(" return " + expr + ";") def _break(self, node, current_klass): - print >>self.output, " break;" - + self.printo(" break;") def _continue(self, node, current_klass): - print >>self.output, " continue;" - + self.printo(" continue;") def _callfunc(self, v, current_klass): @@ -477,7 +490,7 @@ call_name = self.modpfx() + v.node.name elif v.node.name in self.top_level_classes: call_name = self.modpfx() + v.node.name - elif self.imported_classes.has_key(v.node.name): + elif v.node.name in self.imported_classes: call_name = self.imported_classes[v.node.name] + '.' + v.node.name elif v.node.name in PYJSLIB_BUILTIN_FUNCTIONS: call_name = 'pyjslib.' + v.node.name @@ -535,18 +548,15 @@ if kwargs or star_arg_name: if not star_arg_name: star_arg_name = 'null' - try: call_this, method_name = call_name.rsplit(".", 1) + try: + call_this, method_name = call_name.rsplit(".", 1) except ValueError: # Must be a function call ... - return ("pyjs_kwargs_function_call("+call_name+", " - + star_arg_name - + ", ["+fn_args+"]" - + ")" ) - else: - return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', " - + star_arg_name - + ", ["+fn_args+"]" - + ")") + return ("pyjs_kwargs_function_call("+call_name+", " + + star_arg_name + ", ["+fn_args+"]" + ")") + else: + return ("pyjs_kwargs_method_call("+call_this+", '"+method_name+"', " + + star_arg_name + ", ["+fn_args+"]" + ")") else: return call_name + "(" + ", ".join(call_args) + ")" @@ -558,7 +568,7 @@ arg = self.expr(ch4, current_klass) call_args.append(arg) - print >>self.output, "pyjslib.printFunc([", ', '.join(call_args), "],", int(isinstance(node, ast.Printnl)), ");" + self.printo("pyjslib.printFunc([", ', '.join(call_args), "],", int(isinstance(node, ast.Printnl)), ");") def _tryExcept(self, node, current_klass): if len(node.handlers) != 1: @@ -576,28 +586,28 @@ # local scope, temporary to the function. oh dearie me. self.add_local_arg(errName) - print >>self.output, " try {" + self.printo(" try {") for stmt in node.body.nodes: self._stmt(stmt, current_klass) - print >> self.output, " } catch(%s) {" % errName + self.printo(" } catch(%s) {" % errName) if expr: - l = [] + k = [] if isinstance(expr, ast.Tuple): for x in expr.nodes: - l.append("(%(err)s.__name__ == %(expr)s.__name__)" % dict (err=errName, expr=self.expr(x, current_klass))) - else: - l = [ " (%(err)s.__name__ == %(expr)s.__name__) " % dict (err=errName, expr=self.expr(expr, current_klass)) ] - print >> self.output, " if(%s) {" % '||\n\t\t'.join(l) + k.append("(%(err)s.__name__ == %(expr)s.__name__)" % dict(err=errName, expr=self.expr(x, current_klass))) + else: + k = [" (%(err)s.__name__ == %(expr)s.__name__) " % dict(err=errName, expr=self.expr(expr, current_klass))] + self.printo(" if(%s) {" % '||\n\t\t'.join(k)) for stmt in node.handlers[0][2]: self._stmt(stmt, current_klass) if expr: - #print >> self.output, "} else { throw(%s); } " % errName - print >> self.output, "}" - if node.else_ != None: - print >>self.output, " } finally {" + # self.printo("} else { throw(%s); } " % errName) + self.printo("}") + if node.else_ is not None: + self.printo(" } finally {") for stmt in node.else_: self._stmt(stmt, current_klass) - print >>self.output, " }" + self.printo(" }") # XXX: change use_getattr to True to enable "strict" compilation # but incurring a 100% performance penalty. oops. @@ -605,13 +615,13 @@ attr_name = v.attrname if isinstance(v.expr, ast.Name): obj = self._name(v.expr, current_klass, return_none_for_module=True) - if obj == None and v.expr.name in self.module_imports(): + if obj is None and v.expr.name in self.module_imports(): # XXX TODO: distinguish between module import classes # and variables. right now, this is a hack to get # the sys module working. - #if v.expr.name == 'sys': + # if v.expr.name == 'sys': return v.expr.name+'.'+attr_name - #return v.expr.name+'.__'+attr_name+'.prototype.__class__' + # return v.expr.name+'.__'+attr_name+'.prototype.__class__' if not use_getattr or attr_name == '__class__' or \ attr_name == '__name__': return obj + "." + attr_name @@ -625,19 +635,18 @@ else: raise TranslationError("unsupported type (in _getattr)", v.expr) - def modpfx(self): return strip_py(self.module_prefix) - + def _name(self, v, current_klass, top_level=False, - return_none_for_module=False): + return_none_for_module=False): if v.name == 'ilikesillynamesfornicedebugcode': - print top_level, current_klass, repr(v) - print self.top_level_vars - print self.top_level_functions - print self.local_arg_stack - print "error..." + print(top_level, current_klass, repr(v)) + print(self.top_level_vars) + print(self.top_level_functions) + print(self.local_arg_stack) + print("error...") local_var_names = None las = len(self.local_arg_stack) @@ -662,14 +671,14 @@ return UU+self.modpfx() + v.name elif v.name in local_var_names: return v.name - elif self.imported_classes.has_key(v.name): + elif v.name in self.imported_classes: return UU+self.imported_classes[v.name] + '.__' + v.name + ".prototype.__class__" elif v.name in self.top_level_classes: return UU+self.modpfx() + "__" + v.name + ".prototype.__class__" elif v.name in self.module_imports() and return_none_for_module: return None elif v.name in PYJSLIB_BUILTIN_CLASSES: - return "pyjslib." + pyjs_builtin_remap( v.name ) + return "pyjslib." + pyjs_builtin_remap(v.name) elif current_klass: if v.name not in local_var_names and \ v.name not in self.top_level_vars and \ @@ -681,9 +690,8 @@ cls_name_ = cls_name.name_ cls_name = cls_name.name else: - cls_name_ = current_klass + "_" # XXX ??? - name = UU+cls_name_ + ".prototype.__class__." \ - + v.name + cls_name_ = current_klass + "_" # XXX ??? + name = UU+cls_name_ + ".prototype.__class__." + v.name if v.name == 'listener': name = 'listener+' + name return name @@ -695,33 +703,31 @@ if obj in self.method_imported_globals: call_name = UU+self.modpfx() + obj + "." + attr_name - elif self.imported_classes.has_key(obj): - #attr_str = "" - #if attr_name != "__init__": + elif obj in self.imported_classes: + # attr_str = "" + # if attr_name != "__init__": attr_str = ".prototype.__class__." + attr_name call_name = UU+self.imported_classes[obj] + '.__' + obj + attr_str elif obj in self.module_imports(): call_name = obj + "." + attr_name - elif obj[0] == obj[0].upper(): # XXX HACK ALERT + elif obj[0] == obj[0].upper(): # XXX HACK ALERT call_name = UU + self.modpfx() + "__" + obj + ".prototype.__class__." + attr_name else: call_name = UU+self._name(v, current_klass) + "." + attr_name return call_name - def _getattr2(self, v, current_klass, attr_name): if isinstance(v.expr, ast.Getattr): call_name = self._getattr2(v.expr, current_klass, v.attrname + "." + attr_name) elif isinstance(v.expr, ast.Name) and v.expr.name in self.module_imports(): - call_name = UU+v.expr.name + '.__' +v.attrname+".prototype.__class__."+attr_name + call_name = UU+v.expr.name + '.__' + v.attrname+".prototype.__class__."+attr_name else: obj = self.expr(v.expr, current_klass) call_name = obj + "." + v.attrname + "." + attr_name return call_name - def _class(self, node): """ Handle a class definition. @@ -761,12 +767,11 @@ if child.name == "__init__": init_method = child - if len(node.bases) == 0: base_class = "pyjslib.__Object" elif len(node.bases) == 1: if isinstance(node.bases[0], ast.Name): - if self.imported_classes.has_key(node.bases[0].name): + if node.bases[0].name in self.imported_classes: base_class_ = self.imported_classes[node.bases[0].name] + '.__' + node.bases[0].name base_class = self.imported_classes[node.bases[0].name] + '.' + node.bases[0].name else: @@ -777,8 +782,9 @@ # pass our class to self._name base_class_ = self._name(node.bases[0].expr, None) + \ ".__" + node.bases[0].attrname - base_class = self._name(node.bases[0].expr, None) + \ - "." + node.bases[0].attrname + base_class = \ + self._name(node.bases[0].expr, None) + \ + "." + node.bases[0].attrname else: raise TranslationError("unsupported type (in _class)", node.bases[0]) @@ -786,50 +792,50 @@ else: raise TranslationError("more than one base (in _class)", node) - print >>self.output, UU+class_name_ + " = function () {" + self.printo(UU+class_name_ + " = function () {") # call superconstructor - #if base_class: - # print >>self.output, " __" + base_class + ".call(this);" - print >>self.output, "}" + # if base_class: + # self.printo(" __" + base_class + ".call(this);") + self.printo("}") if not init_method: init_method = ast.Function([], "__init__", ["self"], [], 0, None, []) - #self._method(init_method, current_klass, class_name) + # self._method(init_method, current_klass, class_name) # Generate a function which constructs the object - clsfunc = ast.Function([], - node.name, - init_method.argnames[1:], - init_method.defaults, - init_method.flags, - None, - [ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const( -# I attempted lazy initialization, but then you can't access static class members -# " if(!__"+base_class+".__was_initialized__)"+ -# " __" + class_name + "_initialize();\n" + - " var instance = new " + UU + class_name_ + "();\n" + - " if(instance.__init__) instance.__init__.apply(instance, arguments);\n" + - " return instance;" + clsfunc = ast.Function( + [], node.name, + init_method.argnames[1:], + init_method.defaults, + init_method.flags, + None, + [ast.Discard(ast.CallFunc(ast.Name("JS"), [ast.Const( + # I attempted lazy initialization, but then you can't access static class members + # " if(!__"+base_class+".__was_initialized__)"+ + # " __" + class_name + "_initialize();\n" + + " var instance = new " + UU + class_name_ + "();\n" + + " if(instance.__init__) instance.__init__.apply(instance, arguments);\n" + + " return instance;" )]))]) self._function(clsfunc, False) - print >>self.output, UU+class_name_ + ".__initialize__ = function () {" - print >>self.output, " if("+UU+class_name_+".__was_initialized__) return;" - print >>self.output, " "+UU+class_name_+".__was_initialized__ = true;" + self.printo(UU+class_name_ + ".__initialize__ = function () {") + self.printo(" if("+UU+class_name_+".__was_initialized__) return;") + self.printo(" "+UU+class_name_+".__was_initialized__ = true;") cls_obj = UU+class_name_ + '.prototype.__class__' if class_name == "pyjslib.__Object": - print >>self.output, " "+cls_obj+" = {};" + self.printo(" "+cls_obj+" = {};") else: if base_class and base_class not in ("object", "pyjslib.__Object"): - print >>self.output, " if(!"+UU+base_class_+".__was_initialized__)" - print >>self.output, " "+UU+base_class_+".__initialize__();" - print >>self.output, " pyjs_extend(" + UU+class_name_ + ", "+UU+base_class_+");" - else: - print >>self.output, " pyjs_extend(" + UU+class_name_ + ", "+UU+"pyjslib.__Object);" - - print >>self.output, " "+cls_obj+".__new__ = "+UU+class_name+";" - print >>self.output, " "+cls_obj+".__name__ = '"+UU+node.name+"';" + self.printo(" if(!"+UU+base_class_+".__was_initialized__)") + self.printo(" "+UU+base_class_+".__initialize__();") + self.printo(" pyjs_extend(" + UU+class_name_ + ", "+UU+base_class_+");") + else: + self.printo(" pyjs_extend(" + UU+class_name_ + ", "+UU+"pyjslib.__Object);") + + self.printo(" "+cls_obj+".__new__ = "+UU+class_name+";") + self.printo(" "+cls_obj+".__name__ = '"+UU+node.name+"';") for child in node.code: if isinstance(child, ast.Pass): @@ -843,10 +849,9 @@ pass else: raise TranslationError("unsupported type (in _class)", child) - print >>self.output, "}" - - print >> self.output, class_name_+".__initialize__();" - + self.printo("}") + + self.printo(class_name_+".__initialize__();") def classattr(self, node, current_klass): self._assign(node, current_klass, True) @@ -855,8 +860,8 @@ if node.expr2: raise TranslationError("More than one expression unsupported", node) - print >> self.output, "throw (%s);" % self.expr( - node.expr1, current_klass) + self.printo("throw (%s);" % self.expr( + node.expr1, current_klass)) def _method(self, node, current_klass, class_name, class_name_): # reset global var scope @@ -876,22 +881,25 @@ if staticmethod: staticfunc = ast.Function([], class_name_+"."+node.name, node.argnames, node.defaults, node.flags, node.doc, node.code, node.lineno) self._function(staticfunc, True) - print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + " = " + class_name_+"."+node.name+";"; - print >>self.output, " " + UU+class_name_ + ".prototype.__class__." + node.name + ".static_method = true;"; + self.printo(" " + UU+class_name_ + ".prototype.__class__." + node.name + " = " + class_name_+"."+node.name+";") + self.printo(" " + UU+class_name_ + ".prototype.__class__." + node.name + ".static_method = true;") return else: if len(arg_names) == 0: raise TranslationError("methods must take an argument 'self' (in _method)", node) self.method_self = arg_names[0] - #if not classmethod and arg_names[0] != "self": + # if not classmethod and arg_names[0] != "self": # raise TranslationError("first arg not 'self' (in _method)", node) normal_arg_names = arg_names[1:] - if node.kwargs: kwargname = normal_arg_names.pop() - if node.varargs: varargname = normal_arg_names.pop() + if node.kwargs: + kwargname = normal_arg_names.pop() + if node.varargs: + varargname = normal_arg_names.pop() declared_arg_names = list(normal_arg_names) - if node.kwargs: declared_arg_names.append(kwargname) + if node.kwargs: + declared_arg_names.append(kwargname) function_args = "(" + ", ".join(declared_arg_names) + ")" @@ -899,18 +907,17 @@ fexpr = UU + class_name_ + ".prototype.__class__." + node.name else: fexpr = UU + class_name_ + ".prototype." + node.name - print >>self.output, " "+fexpr + " = function" + function_args + " {" + self.printo(" "+fexpr + " = function" + function_args + " {") # default arguments self._default_args_handler(node, normal_arg_names, current_klass) - local_arg_names = normal_arg_names + declared_arg_names + local_arg_names = normal_arg_names + declared_arg_names if node.varargs: self._varargs_handler(node, varargname, declared_arg_names, current_klass) local_arg_names.append(varargname) - # stack of local variable names for this function call self.local_arg_stack.append(local_arg_names) @@ -920,7 +927,7 @@ # remove the top local arg names self.local_arg_stack.pop() - print >>self.output, " };" + self.printo(" };") self._kwargs_parser(node, fexpr, normal_arg_names, current_klass) @@ -928,26 +935,26 @@ # Have to create a version on the instances which automatically passes the # class as "self" altexpr = UU + class_name_ + ".prototype." + node.name - print >>self.output, " "+altexpr + " = function() {" - print >>self.output, " return " + fexpr + ".apply(this.__class__, arguments);" - print >>self.output, " };" - print >>self.output, " "+fexpr+".class_method = true;" - print >>self.output, " "+altexpr+".instance_method = true;" + self.printo(" "+altexpr + " = function() {") + self.printo(" return " + fexpr + ".apply(this.__class__, arguments);") + self.printo(" };") + self.printo(" "+fexpr+".class_method = true;") + self.printo(" "+altexpr+".instance_method = true;") else: # For instance methods, we need an unbound version in the class object altexpr = UU + class_name_ + ".prototype.__class__." + node.name - print >>self.output, " "+altexpr + " = function() {" - print >>self.output, " return " + fexpr + ".call.apply("+fexpr+", arguments);" - print >>self.output, " };" - print >>self.output, " "+altexpr+".unbound_method = true;" - print >>self.output, " "+fexpr+".instance_method = true;" - print >>self.output, " "+altexpr+".__name__ = '%s';" % node.name - - print >>self.output, UU + class_name_ + ".prototype.%s.__name__ = '%s';" % \ - (node.name, node.name) + self.printo(" "+altexpr + " = function() {") + self.printo(" return " + fexpr + ".call.apply("+fexpr+", arguments);") + self.printo(" };") + self.printo(" "+altexpr+".unbound_method = true;") + self.printo(" "+fexpr+".instance_method = true;") + self.printo(" "+altexpr+".__name__ = '%s';" % node.name) + + self.printo(UU + class_name_ + ".prototype.%s.__name__ = '%s';" % + (node.name, node.name)) if node.kwargs or len(node.defaults): - print >>self.output, " "+altexpr + ".parse_kwargs = " + fexpr + ".parse_kwargs;" + self.printo(" "+altexpr + ".parse_kwargs = " + fexpr + ".parse_kwargs;") self.method_self = None self.method_imported_globals = set() @@ -963,7 +970,7 @@ def _stmt(self, node, current_klass): debugStmt = self.debug and not self._isNativeFunc(node) if debugStmt: - print >>self.output, ' try {' + self.printo(' try {') if isinstance(node, ast.Return): self._return(node, current_klass) @@ -992,9 +999,9 @@ elif isinstance(node, ast.Function): self._function(node, True) elif isinstance(node, ast.Printnl): - self._print(node, current_klass) + self._print(node, current_klass) elif isinstance(node, ast.Print): - self._print(node, current_klass) + self._print(node, current_klass) elif isinstance(node, ast.TryExcept): self._tryExcept(node, current_klass) elif isinstance(node, ast.Raise): @@ -1005,34 +1012,30 @@ if debugStmt: lt = self.get_line_trace(node) - - haltException = self.module_prefix + "HaltException" isHaltFunction = self.module_prefix + "IsHaltException" - print >>self.output, ' } catch (__err) {' - print >>self.output, ' if (' + isHaltFunction + '(__err.name)) {' - print >>self.output, ' throw __err;' - print >>self.output, ' } else {' - print >>self.output, " st = sys.printstack() + "\ - + '"%s"' % lt + "+ '\\n' ;" - print >>self.output, ' alert("' + "Error in " \ - + lt + '"' \ - + '+"\\n"+__err.name+": "+__err.message'\ - + '+"\\n\\nStack trace:\\n"' \ - + '+st' \ - + ');' - print >>self.output, ' debugger;' - - print >>self.output, ' throw new ' + self.module_prefix + "HaltException();" - print >>self.output, ' }' - print >>self.output, ' }' - + out = ( + ' } catch (__err) {', + ' if (' + isHaltFunction + '(__err.name)) {', + ' throw __err;', + ' } else {', + ' st = sys.printstack() + ' + '"%s"' % lt + "+ '\\n' ;" + ' alert("' + 'Error in ' + lt + '"' + + '+"\\n"+__err.name+": "+__err.message' + + '+"\\n\\nStack trace:\\n"' + '+st' + ');', + ' debugger;', + ' throw new ' + self.module_prefix + 'HaltException();', + ' }', + ' }' + ) + for s in out: + self.printo(s) def get_line_trace(self, node): lineNum = "Unknown" srcLine = "" if hasattr(node, "lineno"): - if node.lineno != None: + if node.lineno is not None: lineNum = node.lineno srcLine = self.src[min(lineNum, len(self.src))-1] srcLine = srcLine.replace('\\', '\\\\') @@ -1040,9 +1043,9 @@ srcLine = srcLine.replace("'", "\\'") return self.raw_module_name + ".py, line " \ - + str(lineNum) + ":"\ - + "\\n" \ - + " " + srcLine + + str(lineNum) + ":"\ + + "\\n" \ + + " " + srcLine def _augassign(self, node, current_klass): v = node.node @@ -1054,17 +1057,16 @@ lhs = self._name(node.node, current_klass) op = node.op rhs = self.expr(node.expr, current_klass) - print >>self.output, " " + lhs + " " + op + " " + rhs + ";" - - - def _assign(self, node, current_klass, top_level = False): + self.printo(" " + lhs + " " + op + " " + rhs + ";") + + def _assign(self, node, current_klass, top_level=False): if len(node.nodes) != 1: tempvar = '__temp'+str(node.lineno) tnode = ast.Assign([ast.AssName(tempvar, "OP_ASSIGN", node.lineno)], node.expr, node.lineno) self._assign(tnode, current_klass, top_level) for v in node.nodes: - tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno) - self._assign(tnode2, current_klass, top_level) + tnode2 = ast.Assign([v], ast.Name(tempvar, node.lineno), node.lineno) + self._assign(tnode2, current_klass, top_level) return local_var_names = None @@ -1074,7 +1076,6 @@ def _lhsFromAttr(v, current_klass): attr_name = v.attrname if isinstance(v.expr, ast.Name): - obj = v.expr.name lhs = self._name(v.expr, current_klass) + "." + attr_name elif isinstance(v.expr, ast.Getattr): lhs = self._getattr(v, current_klass) @@ -1093,7 +1094,7 @@ self.top_level_vars.add(v.name) vname = self.modpfx() + v.name if not self.modpfx() and v.name not in\ - self.method_imported_globals: + self.method_imported_globals: lhs = "var " + vname else: lhs = UU + vname @@ -1130,7 +1131,7 @@ raise TranslationError("must have one sub (in _assign)", v) idx = self.expr(v.subs[0], current_klass) value = self.expr(node.expr, current_klass) - print >>self.output, " " + obj + ".__setitem__(" + idx + ", " + value + ");" + self.printo(" " + obj + ".__setitem__(" + idx + ", " + value + ");") return else: raise TranslationError("unsupported flag (in _assign)", v) @@ -1138,9 +1139,8 @@ uniqueID = self.nextTupleAssignID self.nextTupleAssignID += 1 tempName = "__tupleassign" + str(uniqueID) + "__" - print >>self.output, " var " + tempName + " = " + \ - self.expr(node.expr, current_klass) + ";" - for index,child in enumerate(v.getChildNodes()): + self.printo(" var " + tempName + " = " + self.expr(node.expr, current_klass) + ";") + for index, child in enumerate(v.getChildNodes()): rhs = tempName + ".__getitem__(" + str(index) + ")" if isinstance(child, ast.AssAttr): @@ -1155,51 +1155,48 @@ "(in _assign)", child) idx = self.expr(child.subs[0], current_klass) value = self.expr(node.expr, current_klass) - print >>self.output, " " + obj + ".__setitem__(" \ - + idx + ", " + rhs + ");" + self.printo(" " + obj + ".__setitem__(" + idx + ", " + rhs + ");") continue - print >>self.output, " " + lhs + " = " + rhs + ";" + self.printo(" " + lhs + " = " + rhs + ";") return else: raise TranslationError("unsupported type (in _assign)", v) rhs = self.expr(node.expr, current_klass) if dbg: - print "b", repr(node.expr), rhs - print >>self.output, " " + lhs + " " + op + " " + rhs + ";" - + print("b", repr(node.expr), rhs) + self.printo(" " + lhs + " " + op + " " + rhs + ";") def _discard(self, node, current_klass): - + if isinstance(node.expr, ast.CallFunc): debugStmt = self.debug and not self._isNativeFunc(node) if debugStmt and isinstance(node.expr.node, ast.Name) and \ node.expr.node.name == 'import_wait': - debugStmt = False + debugStmt = False if debugStmt: st = self.get_line_trace(node) - print >>self.output, "sys.addstack('%s');\n" % st + self.printo("sys.addstack('%s');\n" % st) if isinstance(node.expr.node, ast.Name) and node.expr.node.name == NATIVE_JS_FUNC_NAME: if len(node.expr.args) != 1: raise TranslationError("native javascript function %s must have one arg" % NATIVE_JS_FUNC_NAME, node.expr) if not isinstance(node.expr.args[0], ast.Const): raise TranslationError("native javascript function %s must have constant arg" % NATIVE_JS_FUNC_NAME, node.expr) raw_js = node.expr.args[0].value - print >>self.output, raw_js + self.printo(raw_js) else: expr = self._callfunc(node.expr, current_klass) - print >>self.output, " " + expr + ";" + self.printo(" " + expr + ";") if debugStmt: - print >>self.output, "sys.popstack();\n" + self.printo("sys.popstack();\n") elif isinstance(node.expr, ast.Const): - if node.expr.value is not None: # Empty statements generate ignore None - print >>self.output, self._const(node.expr) + if node.expr.value is not None: # Empty statements generate ignore None + self.printo(self._const(node.expr)) else: raise TranslationError("unsupported type (in _discard)", node.expr) - def _if(self, node, current_klass): for i in range(len(node.tests)): test, consequence = node.tests[i] @@ -1217,14 +1214,13 @@ self._if_test(keyword, test, consequence, current_klass) - def _if_test(self, keyword, test, consequence, current_klass): if test: expr = self.expr(test, current_klass) - print >>self.output, " " + keyword + " (pyjslib.bool(" + expr + ")) {" - else: - print >>self.output, " " + keyword + " {" + self.printo(" " + keyword + " (pyjslib.bool(" + expr + ")) {") + else: + self.printo(" " + keyword + " {") if isinstance(consequence, ast.Stmt): for child in consequence.nodes: @@ -1232,8 +1228,7 @@ else: raise TranslationError("unsupported type (in _if_test)", consequence) - print >>self.output, " }" - + self.printo(" }") def _from(self, node): for name in node.names: @@ -1248,7 +1243,6 @@ else: self.imported_classes[name[0]] = node.modname - def _compare(self, node, current_klass): lhs = self.expr(node.expr, current_klass) @@ -1272,7 +1266,6 @@ return "(" + lhs + " " + op + " " + rhs + ")" - def _not(self, node, current_klass): expr = self.expr(node.expr, current_klass) @@ -1323,35 +1316,41 @@ lhs = "var " + assign_name iterator_name = "__" + assign_name - print >>self.output, """ + loc_dict = { + "iterator_name": iterator_name, + "list_expr": list_expr, + "lhs": lhs, + "op": op, + "assign_tuple": assign_tuple, + } + + self.printo(""" var %(iterator_name)s = %(list_expr)s.__iter__(); try { while (true) { %(lhs)s %(op)s %(iterator_name)s.next(); %(assign_tuple)s - """ % locals() + """ % loc_dict) for node in node.body.nodes: self._stmt(node, current_klass) - print >>self.output, """ + self.printo(""" } } catch (e) { if (e.__name__ != pyjslib.StopIteration.__name__) { throw e; } } - """ % locals() - + """) def _while(self, node, current_klass): test = self.expr(node.test, current_klass) - print >>self.output, " while (pyjslib.bool(" + test + ")) {" + self.printo(" while (pyjslib.bool(" + test + ")) {") if isinstance(node.body, ast.Stmt): for child in node.body.nodes: self._stmt(child, current_klass) else: raise TranslationError("unsupported type (in _while)", node.body) - print >>self.output, " }" - + self.printo(" }") def _const(self, node): if isinstance(node.value, int): @@ -1362,7 +1361,7 @@ v = node.value if isinstance(node.value, unicode): v = v.encode('utf-8') - return "String('%s')" % escapejs(v) + return "String('%s')" % escapejs(v) elif node.value is None: return "null" else: @@ -1388,8 +1387,8 @@ def _mod(self, node, current_klass): if isinstance(node.left, ast.Const) and isinstance(node.left.value, StringType): - self.imported_js.add("sprintf.js") # Include the sprintf functionality if it is used - return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")" + self.imported_js.add("sprintf.js") # Include the sprintf functionality if it is used + return "sprintf("+self.expr(node.left, current_klass) + ", " + self.expr(node.right, current_klass)+")" return self.expr(node.left, current_klass) + " % " + self.expr(node.right, current_klass) def _invert(self, node, current_klass): @@ -1404,7 +1403,7 @@ def _bitshiftright(self, node, current_klass): return self.expr(node.left, current_klass) + " >>> " + self.expr(node.right, current_klass) - def _bitxor(self,node, current_klass): + def _bitxor(self, node, current_klass): return " ^ ".join([self.expr(child, current_klass) for child in node.nodes]) def _bitor(self, node, current_klass): @@ -1421,7 +1420,7 @@ def _subscript_stmt(self, node, current_klass): if node.flags == "OP_DELETE": - print >>self.output, " " + self.expr(node.expr, current_klass) + ".__delitem__(" + self.expr(node.subs[0], current_klass) + ");" + self.printo(" " + self.expr(node.expr, current_klass) + ".__delitem__(" + self.expr(node.subs[0], current_klass) + ");") else: raise TranslationError("unsupported flag (in _subscript)", node) @@ -1449,21 +1448,21 @@ function_args = ", ".join(arg_names) for child in node.getChildNodes(): expr = self.expr(child, None) - print >> res, "function (%s){" % function_args + print("function (%s){" % function_args, file=res) self._default_args_handler(node, arg_names, None, output=res) - print >> res, 'return %s;}' % expr + print('return %s;}' % expr, file=res) return res.getvalue() def _slice(self, node, current_klass): if node.flags == "OP_APPLY": lower = "null" upper = "null" - if node.lower != None: + if node.lower is not None: lower = self.expr(node.lower, current_klass) - if node.upper != None: + if node.upper is not None: upper = self.expr(node.upper, current_klass) - return "pyjslib.slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")" + return "pyjslib.slice(" + self.expr(node.expr, current_klass) + ", " + lower + ", " + upper + ")" else: raise TranslationError("unsupported flag (in _slice)", node) @@ -1499,7 +1498,7 @@ return self._invert(node, current_klass) elif isinstance(node, ast.Bitand): return "("+self._bitand(node, current_klass)+")" - elif isinstance(node,ast.LeftShift): + elif isinstance(node, ast.LeftShift): return self._bitshiftleft(node, current_klass) elif isinstance(node, ast.RightShift): return self._bitshiftright(node, current_klass) @@ -1531,21 +1530,18 @@ raise TranslationError("unsupported type (in expr)", node) - -import cStringIO - def translate(file_name, module_name, debug=False): f = file(file_name, "r") src = f.read() f.close() output = cStringIO.StringIO() mod = compiler.parseFile(file_name) - t = Translator(module_name, module_name, module_name, src, debug, mod, output) + Translator(module_name, module_name, module_name, src, debug, mod, output) return output.getvalue() -class PlatformParser: - def __init__(self, platform_dir = "", verbose=True): +class PlatformParser(object): + def __init__(self, platform_dir="", verbose=True): self.platform_dir = platform_dir self.parse_cache = {} self.platform = "" @@ -1557,7 +1553,7 @@ def parseModule(self, module_name, file_name): importing = False - if not self.parse_cache.has_key(file_name): + if file_name not in self.parse_cache: importing = True mod = compiler.parseFile(file_name) self.parse_cache[file_name] = mod @@ -1574,9 +1570,9 @@ if self.verbose: if override: - print "Importing %s (Platform %s)" % (module_name, self.platform) + print("Importing %s (Platform %s)" % (module_name, self.platform)) elif importing: - print "Importing %s" % (module_name) + print("Importing %s" % (module_name)) return mod, override @@ -1631,20 +1627,23 @@ target.code = source.code target.argnames = source.argnames target.defaults = source.defaults - target.doc = source.doc # @@@ not sure we need to do this any more + target.doc = source.doc # @@@ not sure we need to do this any more + def dotreplace(fname): path, ext = os.path.splitext(fname) return path.replace(".", "/") + ext -class AppTranslator: - - def __init__(self, library_dirs=[], parser=None, dynamic=False, + +class AppTranslator(object): + + def __init__(self, library_dirs=None, parser=None, dynamic=False, optimize=False, verbose=True): self.extension = ".py" self.optimize = optimize self.library_modules = [] self.overrides = {} + library_dirs = [] if library_dirs is None else library_dirs self.library_dirs = path + library_dirs self.dynamic = dynamic self.verbose = verbose @@ -1667,7 +1666,7 @@ if os.path.isfile(full_file_name): return full_file_name - fnameinit, ext = os.path.splitext(file_name) + fnameinit, _ext = os.path.splitext(file_name) fnameinit = fnameinit + "/__init__.py" full_file_name = os.path.join( @@ -1678,7 +1677,7 @@ raise Exception("file not found: " + file_name) def _translate(self, module_name, is_app=True, debug=False, - imported_js=set()): + imported_js=None): if module_name not in self.library_modules: self.library_modules.append(module_name) @@ -1693,7 +1692,7 @@ mod, override = self.parser.parseModule(module_name, file_name) if override: override_name = "%s.%s" % (self.parser.platform.lower(), - module_name) + module_name) self.overrides[override_name] = override_name if is_app: mn = '__main__' @@ -1704,74 +1703,77 @@ self.findFile) module_str = output.getvalue() + if imported_js is None: + imported_js = set() imported_js.update(set(t.imported_js)) imported_modules_str = "" for module in t.imported_modules: if module not in self.library_modules: self.library_modules.append(module) - #imported_js.update(set(t.imported_js)) - #imported_modules_str += self._translate( + # imported_js.update(set(t.imported_js)) + # imported_modules_str += self._translate( # module, False, debug=debug, imported_js=imported_js) return imported_modules_str + module_str - def translate(self, module_name, is_app=True, debug=False, - library_modules=[]): + library_modules=None): app_code = cStringIO.StringIO() lib_code = cStringIO.StringIO() imported_js = set() self.library_modules = [] self.overrides = {} - for library in library_modules: - if library.endswith(".js"): - imported_js.add(library) - continue - self.library_modules.append(library) - if self.verbose: - print 'Including LIB', library - print >> lib_code, '\n//\n// BEGIN LIB '+library+'\n//\n' - print >> lib_code, self._translate( - library, False, debug=debug, imported_js=imported_js) - - print >> lib_code, "/* initialize static library */" - print >> lib_code, "%s%s();\n" % (UU, library) - - print >> lib_code, '\n//\n// END LIB '+library+'\n//\n' + if library_modules is not None: + for library in library_modules: + if library.endswith(".js"): + imported_js.add(library) + continue + self.library_modules.append(library) + if self.verbose: + print('Including LIB', library) + print('\n//\n// BEGIN LIB '+library+'\n//\n', file=lib_code) + print(self._translate(library, False, debug=debug, imported_js=imported_js), + file=lib_code) + + print("/* initialize static library */", file=lib_code) + print("%s%s();\n" % (UU, library), file=lib_code) + + print('\n//\n// END LIB '+library+'\n//\n', file=lib_code) if module_name: - print >> app_code, self._translate( - module_name, is_app, debug=debug, imported_js=imported_js) + print(self._translate(module_name, is_app, debug=debug, imported_js=imported_js), + file=app_code) for js in imported_js: - path = self.findFile(js) - if os.path.isfile(path): - if self.verbose: - print 'Including JS', js - print >> lib_code, '\n//\n// BEGIN JS '+js+'\n//\n' - print >> lib_code, file(path).read() - print >> lib_code, '\n//\n// END JS '+js+'\n//\n' - else: - print >>sys.stderr, 'Warning: Unable to find imported javascript:', js + path = self.findFile(js) + if os.path.isfile(path): + if self.verbose: + print('Including JS', js) + print('\n//\n// BEGIN JS '+js+'\n//\n', file=lib_code) + print(file(path).read(), file=lib_code) + print('\n//\n// END JS '+js+'\n//\n', file=lib_code) + else: + print('Warning: Unable to find imported javascript:', js, file=sys.stderr) return lib_code.getvalue(), app_code.getvalue() + usage = """ usage: %s file_name [module_name] """ + def main(): - import sys - if len(sys.argv)<2: - print >> sys.stderr, usage % sys.argv[0] + if len(sys.argv) < 2: + print(usage % sys.argv[0], file=sys.stderr) sys.exit(1) file_name = os.path.abspath(sys.argv[1]) if not os.path.isfile(file_name): - print >> sys.stderr, "File not found %s" % file_name + print("File not found %s" % file_name, file=sys.stderr) sys.exit(1) if len(sys.argv) > 2: module_name = sys.argv[2] else: module_name = None - print translate(file_name, module_name), + print(translate(file_name, module_name), end="") + if __name__ == "__main__": main() - diff -r c1298e7ffe3a -r 8391c11477f4 svgui/svgui.py --- a/svgui/svgui.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/svgui.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,34 +23,44 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import +import os +import shutil + import wx -import os, sys, shutil +from svgui.pyjs import translate -from pyjs import translate - +import util.paths as paths from POULibrary import POULibrary from docutil import open_svg from py_ext import PythonFileCTNMixin + class SVGUILibrary(POULibrary): def GetLibraryPath(self): - return os.path.join(os.path.split(__file__)[0], "pous.xml") + return paths.AbsNeighbourFile(__file__, "pous.xml") + class SVGUI(PythonFileCTNMixin): ConfNodeMethods = [ - {"bitmap" : "ImportSVG", - "name" : _("Import SVG"), - "tooltip" : _("Import SVG"), - "method" : "_ImportSVG"}, - {"bitmap" : "ImportSVG", # should be something different - "name" : _("Inkscape"), - "tooltip" : _("Create HMI"), - "method" : "_StartInkscape"}, + { + "bitmap": "ImportSVG", + "name": _("Import SVG"), + "tooltip": _("Import SVG"), + "method": "_ImportSVG" + }, + { + "bitmap": "ImportSVG", # should be something different + "name": _("Inkscape"), + "tooltip": _("Create HMI"), + "method": "_StartInkscape" + }, ] def ConfNodePath(self): - return os.path.join(os.path.dirname(__file__)) + return paths.AbsDir(__file__) def _getSVGpath(self, project_path=None): if project_path is None: @@ -58,7 +69,7 @@ return os.path.join(project_path, "gui.svg") def _getSVGUIserverpath(self): - return os.path.join(os.path.dirname(__file__), "svgui_server.py") + return paths.AbsNeighbourFile(__file__, "svgui_server.py") def OnCTNSave(self, from_project_path=None): if from_project_path is not None: @@ -68,21 +79,21 @@ def CTNGenerate_C(self, buildpath, locations): """ - Return C code generated by iec2c compiler + Return C code generated by iec2c compiler when _generate_softPLC have been called @param locations: ignored @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND """ - + current_location = self.GetCurrentLocation() # define a unique name for the generated C file - location_str = "_".join(map(lambda x:str(x), current_location)) - + location_str = "_".join(map(str, current_location)) + res = ([], "", False) - - svgfile=self._getSVGpath() + + svgfile = self._getSVGpath() if os.path.exists(svgfile): - res += (("gui.svg", file(svgfile,"rb")),) + res += (("gui.svg", file(svgfile, "rb")),) svguiserverfile = open(self._getSVGUIserverpath(), 'r') svguiservercode = svguiserverfile.read() @@ -90,35 +101,35 @@ svguilibpath = os.path.join(self._getBuildPath(), "svguilib.js") svguilibfile = open(svguilibpath, 'w') - svguilibfile.write(translate(os.path.join(os.path.dirname(__file__), "pyjs", "lib", "sys.py"), "sys")) - svguilibfile.write(open(os.path.join(os.path.dirname(__file__), "pyjs", "lib", "_pyjs.js"), 'r').read()) - svguilibfile.write(translate(os.path.join(os.path.dirname(__file__), "pyjs", "lib", "pyjslib.py"), "pyjslib")) - svguilibfile.write(translate(os.path.join(os.path.dirname(__file__), "svguilib.py"), "svguilib")) + fpath = paths.AbsDir(__file__) + svguilibfile.write(translate(os.path.join(fpath, "pyjs", "lib", "sys.py"), "sys")) + svguilibfile.write(open(os.path.join(fpath, "pyjs", "lib", "_pyjs.js"), 'r').read()) + svguilibfile.write(translate(os.path.join(fpath, "pyjs", "lib", "pyjslib.py"), "pyjslib")) + svguilibfile.write(translate(os.path.join(fpath, "svguilib.py"), "svguilib")) svguilibfile.write("pyjslib();\nsvguilib();\n") - svguilibfile.write(open(os.path.join(os.path.dirname(__file__), "pyjs", "lib", "json.js"), 'r').read()) - svguilibfile.write(open(os.path.join(os.path.dirname(__file__), "livesvg.js"), 'r').read()) + svguilibfile.write(open(os.path.join(fpath, "pyjs", "lib", "json.js"), 'r').read()) + svguilibfile.write(open(os.path.join(fpath, "livesvg.js"), 'r').read()) svguilibfile.close() jsmodules = {"LiveSVGPage": "svguilib.js"} - res += (("svguilib.js", file(svguilibpath,"rb")),) - - runtimefile_path = os.path.join(buildpath, "runtime_%s.py"%location_str) + res += (("svguilib.js", file(svguilibpath, "rb")),) + + runtimefile_path = os.path.join(buildpath, "runtime_%s.py" % location_str) runtimefile = open(runtimefile_path, 'w') - runtimefile.write(svguiservercode % {"svgfile" : "gui.svg"}) + runtimefile.write(svguiservercode % {"svgfile": "gui.svg"}) runtimefile.write(""" def _runtime_%(location)s_start(): website.LoadHMI(%(svgui_class)s, %(jsmodules)s) - + def _runtime_%(location)s_stop(): website.UnLoadHMI() - -""" % {"location": location_str, - "svgui_class": "SVGUI_HMI", - "jsmodules" : str(jsmodules), - }) + + """ % {"location": location_str, + "svgui_class": "SVGUI_HMI", + "jsmodules": str(jsmodules)}) runtimefile.close() - - res += (("runtime_%s.py"%location_str, file(runtimefile_path,"rb")),) - + + res += (("runtime_%s.py" % location_str, file(runtimefile_path, "rb")),) + return res def _ImportSVG(self): @@ -128,8 +139,8 @@ if os.path.isfile(svgpath): shutil.copy(svgpath, self._getSVGpath()) else: - self.GetCTRoot().logger.write_error(_("No such SVG file: %s\n")%svgpath) - dialog.Destroy() + self.GetCTRoot().logger.write_error(_("No such SVG file: %s\n") % svgpath) + dialog.Destroy() def _StartInkscape(self): svgfile = self._getSVGpath() @@ -138,7 +149,7 @@ dialog = wx.MessageDialog(self.GetCTRoot().AppFrame, _("You don't have write permissions.\nOpen Inkscape anyway ?"), _("Open Inkscape"), - wx.YES_NO|wx.ICON_QUESTION) + wx.YES_NO | wx.ICON_QUESTION) open_inkscape = dialog.ShowModal() == wx.ID_YES dialog.Destroy() if open_inkscape: diff -r c1298e7ffe3a -r 8391c11477f4 svgui/svgui_server.py --- a/svgui/svgui_server.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/svgui_server.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,23 +22,29 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import os -from nevow import rend, appserver, inevow, tags, loaders, athena +from nevow import tags, loaders import simplejson as json +import runtime.NevowServer as NS svgfile = '%(svgfile)s' svguiWidgets = {} currentId = 0 + + def getNewId(): global currentId currentId += 1 return currentId -class SvguiWidget: - + +class SvguiWidget(object): + def __init__(self, classname, id, **kwargs): self.classname = classname self.id = id @@ -50,9 +56,9 @@ def setinput(self, attrname, value): self.inputs[attrname] = value - + def getinput(self, attrname, default=None): - if not self.inputs.has_key(attrname): + if attrname not in self.inputs: self.inputs[attrname] = default return self.inputs[attrname] @@ -61,14 +67,14 @@ self.outputs[attrname] = value self.changed = True self.RefreshInterface() - + def updateoutputs(self, **kwargs): for attrname, value in kwargs.iteritems(): if self.outputs.get(attrname) != value: self.outputs[attrname] = value self.changed = True self.RefreshInterface() - + def RefreshInterface(self): interface = website.getHMI() if isinstance(interface, SVGUI_HMI) and self.changed and not self.inhibit: @@ -77,52 +83,58 @@ if d is not None: self.inhibit = True d.addCallback(self.InterfaceRefreshed) - + def InterfaceRefreshed(self, result): self.inhibit = False if self.changed: self.RefreshInterface() + def get_object_init_state(obj): # Convert objects to a dictionary of their representation attrs = obj.attrs.copy() attrs.update(obj.inputs) - d = { '__class__': obj.classname, - 'id': obj.id, - 'kwargs': json.dumps(attrs), - } + d = { + '__class__': obj.classname, + 'id': obj.id, + 'kwargs': json.dumps(attrs), + } return d + def get_object_current_state(obj): # Convert objects to a dictionary of their representation - d = { '__class__': obj.classname, - 'id': obj.id, - 'kwargs': json.dumps(obj.outputs), - } + d = { + '__class__': obj.classname, + 'id': obj.id, + 'kwargs': json.dumps(obj.outputs), + } return d + class SVGUI_HMI(website.PLCHMI): jsClass = u"LiveSVGPage.LiveSVGWidget" - - docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ - tags.xml(loaders.xmlfile(os.path.join(WorkingDir, svgfile))), - ]) - + + docFactory = loaders.stan(tags.div(render=tags.directive('liveElement'))[ + tags.xml(loaders.xmlfile(os.path.join(NS.WorkingDir, svgfile))), + ]) + def HMIinitialisation(self): gadgets = [] for gadget in svguiWidgets.values(): gadgets.append(unicode(json.dumps(gadget, default=get_object_init_state, indent=2), 'ascii')) d = self.callRemote('init', gadgets) d.addCallback(self.HMIinitialised) - - def sendData(self,data): + + def sendData(self, data): if self.initialised: - return self.callRemote('receiveData',unicode(json.dumps(data, default=get_object_current_state, indent=2), 'ascii')) + return self.callRemote('receiveData', unicode(json.dumps(data, default=get_object_current_state, indent=2), 'ascii')) return None - + def setattr(self, id, attrname, value): svguiWidgets[id].setinput(attrname, value) + def createSVGUIControl(*args, **kwargs): id = getNewId() gad = SvguiWidget(args[0], id, **kwargs) @@ -133,19 +145,21 @@ interface.callRemote('init', gadget) return id + def setAttr(id, attrname, value): gad = svguiWidgets.get(id, None) if gad is not None: gad.setoutput(attrname, value) + def updateAttr(id, **kwargs): gad = svguiWidgets.get(id, None) if gad is not None: gad.updateoutput(**kwargs) + def getAttr(id, attrname, default=None): gad = svguiWidgets.get(id, None) if gad is not None: return gad.getinput(attrname, default) return default - diff -r c1298e7ffe3a -r 8391c11477f4 svgui/svguilib.py --- a/svgui/svguilib.py Fri Mar 24 12:07:47 2017 +0000 +++ b/svgui/svguilib.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,8 +22,12 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# pylint: disable=old-style-class,undefined-variable + + class button: - + def __init__(self, parent, id, args): self.parent = parent self.id = id @@ -40,19 +44,19 @@ self.up = not self.state else: self.up = True - + # Add event on each element of the button if self.active: self.back_elt.addEventListener("mouseup", self, False) self.back_elt.addEventListener("mousedown", self, False) self.back_elt.addEventListener("mouseover", self, False) self.back_elt.addEventListener("mouseout", self, False) - + self.sele_elt.addEventListener("mouseup", self, False) self.sele_elt.addEventListener("mousedown", self, False) self.sele_elt.addEventListener("mouseover", self, False) self.sele_elt.addEventListener("mouseout", self, False) - + blockSVGElementDrag(self.back_elt) blockSVGElementDrag(self.sele_elt) @@ -66,7 +70,7 @@ else: self.sele_elt.removeAttribute("display") self.back_elt.setAttribute("display", "none") - + def updateValues(self, values): if values.state != self.state: self.state = values.state @@ -80,9 +84,9 @@ if evt.type == "mousedown": evt.stopPropagation() setCurrentObject(self) - + self.dragging = True - + if self.toggle: self.up = self.state else: @@ -90,18 +94,18 @@ self.state = True updateAttr(self.id, 'state', self.state) self.updateElements() - + if isCurrentObject(self) and self.dragging: # Quand le bouton est survole if evt.type == "mouseover" and self.toggle: self.up = self.state self.updateElements() - + # Quand le curseur quitte la zone du bouton - elif evt.type == "mouseout" and self.toggle: + elif evt.type == "mouseout" and self.toggle: self.up = not self.state self.updateElements() - + # Quand le bouton de la souris est relache elif evt.type == "mouseup": evt.stopPropagation() @@ -115,8 +119,9 @@ self.updateElements() self.dragging = False + class textControl: - + def __init__(self, parent, id, args): self.parent = parent self.id = id @@ -126,17 +131,15 @@ else: self.text = "" self.updateElements() - + def updateValues(self, values): if values.text != self.value: self.text = values.text updateAttr(self.id, 'text', self.text) self.updateElements() - + def updateElements(self): self.back_elt.firstChild.firstChild.textContent = self.text - + def handleEvent(self, evt): pass - - diff -r c1298e7ffe3a -r 8391c11477f4 targets/Generic/__init__.py --- a/targets/Generic/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Generic/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,7 +22,10 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from ..toolchain_makefile import toolchain_makefile + class Generic_target(toolchain_makefile): pass diff -r c1298e7ffe3a -r 8391c11477f4 targets/Linux/__init__.py --- a/targets/Linux/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Linux/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,12 +22,17 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from ..toolchain_gcc import toolchain_gcc + class Linux_target(toolchain_gcc): dlopen_prefix = "./" extension = ".so" + def getBuilderCFLAGS(self): return toolchain_gcc.getBuilderCFLAGS(self) + ["-fPIC"] + def getBuilderLDFLAGS(self): return toolchain_gcc.getBuilderLDFLAGS(self) + ["-shared", "-lrt"] diff -r c1298e7ffe3a -r 8391c11477f4 targets/Linux/plc_Linux_main.c --- a/targets/Linux/plc_Linux_main.c Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Linux/plc_Linux_main.c Tue Jan 30 16:06:58 2018 +0100 @@ -232,6 +232,7 @@ pthread_mutex_lock(&python_mutex); } +#ifndef HAVE_RETAIN void InitRetain(void) { } @@ -260,3 +261,4 @@ void Remind(unsigned int offset, unsigned int count, void *p) { } +#endif // !HAVE_RETAIN diff -r c1298e7ffe3a -r 8391c11477f4 targets/Win32/__init__.py --- a/targets/Win32/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Win32/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,10 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from ..toolchain_gcc import toolchain_gcc + class Win32_target(toolchain_gcc): dlopen_prefix = "" extension = ".dll" + def getBuilderLDFLAGS(self): return toolchain_gcc.getBuilderLDFLAGS(self) + ["-shared", "-lwinmm"] diff -r c1298e7ffe3a -r 8391c11477f4 targets/Win32/plc_Win32_main.c --- a/targets/Win32/plc_Win32_main.c Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Win32/plc_Win32_main.c Tue Jan 30 16:06:58 2018 +0100 @@ -241,6 +241,7 @@ WaitForSingleObject(python_sem, INFINITE); } +#ifndef HAVE_RETAIN void InitRetain(void) { } @@ -275,6 +276,7 @@ void Remind(unsigned int offset, unsigned int count, void *p) { } +#endif // !HAVE_RETAIN static void __attribute__((constructor)) beremiz_dll_init(void) diff -r c1298e7ffe3a -r 8391c11477f4 targets/Xenomai/__init__.py --- a/targets/Xenomai/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Xenomai/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,24 +22,28 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import from ..toolchain_gcc import toolchain_gcc + class Xenomai_target(toolchain_gcc): dlopen_prefix = "./" extension = ".so" + def getXenoConfig(self, flagsname): """ Get xeno-config from target parameters """ - xeno_config=self.CTRInstance.GetTarget().getcontent().getXenoConfig() + xeno_config = self.CTRInstance.GetTarget().getcontent().getXenoConfig() if xeno_config: from util.ProcessLogger import ProcessLogger - status, result, err_result = ProcessLogger(self.CTRInstance.logger, - xeno_config + " --skin=native --"+flagsname, - no_stdout=True).spin() + status, result, _err_result = ProcessLogger(self.CTRInstance.logger, + xeno_config + " --skin=native --"+flagsname, + no_stdout=True).spin() if status: - self.CTRInstance.logger.write_error(_("Unable to get Xenomai's %s \n")%flagsname) + self.CTRInstance.logger.write_error(_("Unable to get Xenomai's %s \n") % flagsname) return [result.strip()] return [] - + def getBuilderLDFLAGS(self): xeno_ldflags = self.getXenoConfig("ldflags") return toolchain_gcc.getBuilderLDFLAGS(self) + xeno_ldflags + ["-shared"] @@ -47,4 +51,3 @@ def getBuilderCFLAGS(self): xeno_cflags = self.getXenoConfig("cflags") return toolchain_gcc.getBuilderCFLAGS(self) + xeno_cflags + ["-fPIC"] - diff -r c1298e7ffe3a -r 8391c11477f4 targets/Xenomai/plc_Xenomai_main.c --- a/targets/Xenomai/plc_Xenomai_main.c Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/Xenomai/plc_Xenomai_main.c Tue Jan 30 16:06:58 2018 +0100 @@ -363,3 +363,33 @@ } /* as plc does not wait for lock. */ } +#ifndef HAVE_RETAIN +int CheckRetainBuffer(void) +{ + return 1; +} + +void ValidateRetainBuffer(void) +{ +} + +void InValidateRetainBuffer(void) +{ +} + +void Retain(unsigned int offset, unsigned int count, void *p) +{ +} + +void Remind(unsigned int offset, unsigned int count, void *p) +{ +} + +void CleanupRetain(void) +{ +} + +void InitRetain(void) +{ +} +#endif // !HAVE_RETAIN diff -r c1298e7ffe3a -r 8391c11477f4 targets/Xenomai/plc_Xenomai_noretain.c --- a/targets/Xenomai/plc_Xenomai_noretain.c Fri Mar 24 12:07:47 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,20 +0,0 @@ -int CheckRetainBuffer(void) -{ - return 1; -} - -void ValidateRetainBuffer(void) -{ -} - -void InValidateRetainBuffer(void) -{ -} - -void Retain(unsigned int offset, unsigned int count, void *p) -{ -} - -void Remind(unsigned int offset, unsigned int count, void *p) -{ -} diff -r c1298e7ffe3a -r 8391c11477f4 targets/__init__.py --- a/targets/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,87 +1,97 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -# Package initialisation -#import targets - -""" -Beremiz Targets - -- Target are python packages, containing at least one "XSD" file -- Target class may inherit from a toolchain_(toolchainname) -- The target folder's name must match to name define in the XSD for TargetType -""" - -from os import listdir, path - -_base_path = path.split(__file__)[0] -def _GetLocalTargetClassFactory(name): - return lambda:getattr(__import__(name,globals(),locals()), name+"_target") - -targets = dict([(name, {"xsd":path.join(_base_path, name, "XSD"), - "class":_GetLocalTargetClassFactory(name), - "code": { fname: path.join(_base_path, name, fname) - for fname in listdir(path.join(_base_path, name)) - if fname.startswith("plc_%s_main"%name) and - fname.endswith(".c")}}) - for name in listdir(_base_path) - if path.isdir(path.join(_base_path, name)) - and not name.startswith("__")]) - -toolchains = {"gcc": path.join(_base_path, "XSD_toolchain_gcc"), - "makefile": path.join(_base_path, "XSD_toolchain_makefile")} - -def GetBuilder(targetname): - return targets[targetname]["class"]() - -def GetTargetChoices(): - DictXSD_toolchain = {} - targetchoices = "" - - # Get all xsd toolchains - for toolchainname,xsdfilename in toolchains.iteritems() : - if path.isfile(xsdfilename): - DictXSD_toolchain["toolchain_"+toolchainname] = \ - open(xsdfilename).read() - - # Get all xsd targets - for targetname,nfo in targets.iteritems(): - xsd_string = open(nfo["xsd"]).read() - targetchoices += xsd_string%DictXSD_toolchain - - return targetchoices - -def GetTargetCode(targetname): - codedesc = targets[targetname]["code"] - code = "\n".join([open(fpath).read() for fname, fpath in sorted(codedesc.items())]) - return code - -def GetHeader(): - filename = path.join(path.split(__file__)[0],"beremiz.h") - return open(filename).read() - -def GetCode(name): - filename = path.join(path.split(__file__)[0],name) - return open(filename).read() - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Package initialisation + + +""" +Beremiz Targets + +- Target are python packages, containing at least one "XSD" file +- Target class may inherit from a toolchain_(toolchainname) +- The target folder's name must match to name define in the XSD for TargetType +""" + + +from __future__ import absolute_import +from os import listdir, path +import util.paths as paths + +_base_path = paths.AbsDir(__file__) + + +def _GetLocalTargetClassFactory(name): + return lambda: getattr(__import__(name, globals(), locals()), name+"_target") + + +targets = dict([(name, {"xsd": path.join(_base_path, name, "XSD"), + "class": _GetLocalTargetClassFactory(name), + "code": {fname: path.join(_base_path, name, fname) + for fname in listdir(path.join(_base_path, name)) + if (fname.startswith("plc_%s_main" % name) and + fname.endswith(".c"))}}) + for name in listdir(_base_path) + if (path.isdir(path.join(_base_path, name)) and + not name.startswith("__"))]) + +toolchains = {"gcc": path.join(_base_path, "XSD_toolchain_gcc"), + "makefile": path.join(_base_path, "XSD_toolchain_makefile")} + + +def GetBuilder(targetname): + return targets[targetname]["class"]() + + +def GetTargetChoices(): + DictXSD_toolchain = {} + targetchoices = "" + + # Get all xsd toolchains + for toolchainname, xsdfilename in toolchains.iteritems(): + if path.isfile(xsdfilename): + DictXSD_toolchain["toolchain_"+toolchainname] = open(xsdfilename).read() + + # Get all xsd targets + for _targetname, nfo in targets.iteritems(): + xsd_string = open(nfo["xsd"]).read() + targetchoices += xsd_string % DictXSD_toolchain + + return targetchoices + + +def GetTargetCode(targetname): + codedesc = targets[targetname]["code"] + code = "\n".join([open(fpath).read() for _fname, fpath in sorted(codedesc.items())]) + return code + + +def GetHeader(): + filename = paths.AbsNeighbourFile(__file__, "beremiz.h") + return open(filename).read() + + +def GetCode(name): + filename = paths.AbsNeighbourFile(__file__, name) + return open(filename).read() diff -r c1298e7ffe3a -r 8391c11477f4 targets/plc_debug.c --- a/targets/plc_debug.c Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/plc_debug.c Tue Jan 30 16:06:58 2018 +0100 @@ -10,6 +10,16 @@ * * * */ + +#ifdef TARGET_DEBUG_DISABLE + +void __init_debug (void){} +void __cleanup_debug (void){} +void __retrieve_debug(void){} +void __publish_debug (void){} + +#else + #include "iec_types_all.h" #include "POUS.h" /*for memcpy*/ @@ -165,7 +175,9 @@ /* compute next cursor positon. No need to check overflow, as BUFFER_SIZE is computed large enough */ - if(dsc->type == STRING_ENUM){ + if((dsc->type == STRING_ENUM) || + (dsc->type == STRING_P_ENUM) || + (dsc->type == STRING_O_ENUM)){ /* optimization for strings */ size = ((STRING*)visible_value_p)->len + 1; } @@ -326,3 +338,5 @@ return wait_error; } +#endif + diff -r c1298e7ffe3a -r 8391c11477f4 targets/plc_main_tail.c --- a/targets/plc_main_tail.c Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/plc_main_tail.c Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ /** * LOGGING **/ +#ifndef TARGET_LOGGING_DISABLE #ifndef LOG_BUFFER_SIZE #define LOG_BUFFER_SIZE (1<<14) /*16Ko*/ @@ -134,6 +135,10 @@ return 0; } +#endif + +#ifndef TARGET_EXT_SYNC_DISABLE + #define CALIBRATED -2 #define NOT_CALIBRATED -1 static int calibration_count = NOT_CALIBRATED; @@ -219,3 +224,5 @@ } } } + +#endif diff -r c1298e7ffe3a -r 8391c11477f4 targets/toolchain_gcc.py --- a/targets/toolchain_gcc.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/toolchain_gcc.py Tue Jan 30 16:06:58 2018 +0100 @@ -1,202 +1,241 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import os, re, operator -from util.ProcessLogger import ProcessLogger -import hashlib - -includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') - -class toolchain_gcc(): - """ - This abstract class contains GCC specific code. - It cannot be used as this and should be inherited in a target specific - class such as target_linux or target_win32 - """ - def __init__(self, CTRInstance): - self.CTRInstance = CTRInstance - self.buildpath = None - self.SetBuildPath(self.CTRInstance._getBuildPath()) - - def getBuilderCFLAGS(self): - """ - Returns list of builder specific CFLAGS - """ - return [self.CTRInstance.GetTarget().getcontent().getCFLAGS()] - - def getBuilderLDFLAGS(self): - """ - Returns list of builder specific LDFLAGS - """ - return self.CTRInstance.LDFLAGS + \ - [self.CTRInstance.GetTarget().getcontent().getLDFLAGS()] - - def GetBinaryCode(self): - try: - return open(self.exe_path, "rb").read() - except Exception, e: - return None - - def _GetMD5FileName(self): - return os.path.join(self.buildpath, "lastbuildPLC.md5") - - def ResetBinaryCodeMD5(self): - self.md5key = None - try: - os.remove(self._GetMD5FileName()) - except Exception, e: - pass - - def GetBinaryCodeMD5(self): - if self.md5key is not None: - return self.md5key - else: - try: - return open(self._GetMD5FileName(), "r").read() - except Exception, e: - return None - - def SetBuildPath(self, buildpath): - if self.buildpath != buildpath: - self.buildpath = buildpath - self.exe = self.CTRInstance.GetProjectName() + self.extension - self.exe_path = os.path.join(self.buildpath, self.exe) - self.md5key = None - self.srcmd5 = {} - - def check_and_update_hash_and_deps(self, bn): - # Get latest computed hash and deps - oldhash, deps = self.srcmd5.get(bn,(None,[])) - # read source - src = open(os.path.join(self.buildpath, bn)).read() - # compute new hash - newhash = hashlib.md5(src).hexdigest() - # compare - match = (oldhash == newhash) - if not match: - # file have changed - # update direct dependencies - deps = [] - for l in src.splitlines(): - res = includes_re.match(l) - if res is not None: - depfn = res.groups()[0] - if os.path.exists(os.path.join(self.buildpath, depfn)): - #print bn + " depends on "+depfn - deps.append(depfn) - # store that hashand deps - self.srcmd5[bn] = (newhash, deps) - # recurse through deps - # TODO detect cicular deps. - return reduce(operator.and_, map(self.check_and_update_hash_and_deps, deps), match) - - def build(self): - # Retrieve toolchain user parameters - toolchain_params = self.CTRInstance.GetTarget().getcontent() - self.compiler = toolchain_params.getCompiler() - self.linker = toolchain_params.getLinker() - - Builder_CFLAGS = ' '.join(self.getBuilderCFLAGS()) - - ######### GENERATE OBJECT FILES ######################################## - obns = [] - objs = [] - relink = self.GetBinaryCode() is None - for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: - if CFilesAndCFLAGS: - if Location : - self.CTRInstance.logger.write(".".join(map(str,Location))+" :\n") - else: - self.CTRInstance.logger.write(_("PLC :\n")) - - for CFile, CFLAGS in CFilesAndCFLAGS: - if CFile.endswith(".c"): - bn = os.path.basename(CFile) - obn = os.path.splitext(bn)[0]+".o" - objectfilename = os.path.splitext(CFile)[0]+".o" - - match = self.check_and_update_hash_and_deps(bn) - - if match: - self.CTRInstance.logger.write(" [pass] "+bn+" -> "+obn+"\n") - else: - relink = True - - self.CTRInstance.logger.write(" [CC] "+bn+" -> "+obn+"\n") - - status, result, err_result = ProcessLogger( - self.CTRInstance.logger, - "\"%s\" -c \"%s\" -o \"%s\" %s %s"% - (self.compiler, CFile, objectfilename, Builder_CFLAGS, CFLAGS) - ).spin() - - if status : - self.srcmd5.pop(bn) - self.CTRInstance.logger.write_error(_("C compilation of %s failed.\n")%bn) - return False - obns.append(obn) - objs.append(objectfilename) - elif CFile.endswith(".o"): - obns.append(os.path.basename(CFile)) - objs.append(CFile) - - ######### GENERATE library FILE ######################################## - # Link all the object files into one binary file - self.CTRInstance.logger.write(_("Linking :\n")) - if relink: - objstring = [] - - # Generate list .o files - listobjstring = '"' + '" "'.join(objs) + '"' - - ALLldflags = ' '.join(self.getBuilderLDFLAGS()) - - self.CTRInstance.logger.write(" [CC] " + ' '.join(obns)+" -> " + self.exe + "\n") - - status, result, err_result = ProcessLogger( - self.CTRInstance.logger, - "\"%s\" %s -o \"%s\" %s"% - (self.linker, - listobjstring, - self.exe_path, - ALLldflags) - ).spin() - - if status : - return False - - else: - self.CTRInstance.logger.write(" [pass] " + ' '.join(obns)+" -> " + self.exe + "\n") - - # Calculate md5 key and get data for the new created PLC - data=self.GetBinaryCode() - self.md5key = hashlib.md5(data).hexdigest() - - # Store new PLC filename based on md5 key - f = open(self._GetMD5FileName(), "w") - f.write(self.md5key) - f.close() - - return True - +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Paul Beltyukov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import os +import re +import operator +import hashlib + +from util.ProcessLogger import ProcessLogger + + +includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') + + +class toolchain_gcc(object): + """ + This abstract class contains GCC specific code. + It cannot be used as this and should be inherited in a target specific + class such as target_linux or target_win32 + """ + def __init__(self, CTRInstance): + self.CTRInstance = CTRInstance + self.buildpath = None + self.SetBuildPath(self.CTRInstance._getBuildPath()) + + def getBuilderCFLAGS(self): + """ + Returns list of builder specific CFLAGS + """ + return [self.CTRInstance.GetTarget().getcontent().getCFLAGS()] + + def getBuilderLDFLAGS(self): + """ + Returns list of builder specific LDFLAGS + """ + return self.CTRInstance.LDFLAGS + \ + [self.CTRInstance.GetTarget().getcontent().getLDFLAGS()] + + def getCompiler(self): + """ + Returns compiler + """ + return self.CTRInstance.GetTarget().getcontent().getCompiler() + + def getLinker(self): + """ + Returns linker + """ + return self.CTRInstance.GetTarget().getcontent().getLinker() + + def GetBinaryCode(self): + try: + return open(self.exe_path, "rb").read() + except Exception: + return None + + def _GetMD5FileName(self): + return os.path.join(self.buildpath, "lastbuildPLC.md5") + + def ResetBinaryCodeMD5(self): + self.md5key = None + try: + os.remove(self._GetMD5FileName()) + except Exception: + pass + + def GetBinaryCodeMD5(self): + if self.md5key is not None: + return self.md5key + else: + try: + return open(self._GetMD5FileName(), "r").read() + except Exception: + return None + + def SetBuildPath(self, buildpath): + if self.buildpath != buildpath: + self.buildpath = buildpath + self.exe = self.CTRInstance.GetProjectName() + self.extension + self.exe_path = os.path.join(self.buildpath, self.exe) + self.md5key = None + self.srcmd5 = {} + + def append_cfile_deps(self, src, deps): + for l in src.splitlines(): + res = includes_re.match(l) + if res is not None: + depfn = res.groups()[0] + if os.path.exists(os.path.join(self.buildpath, depfn)): + deps.append(depfn) + + def concat_deps(self, bn): + # read source + src = open(os.path.join(self.buildpath, bn), "r").read() + # update direct dependencies + deps = [] + self.append_cfile_deps(src, deps) + # recurse through deps + # TODO detect cicular deps. + return reduce(operator.concat, map(self.concat_deps, deps), src) + + def check_and_update_hash_and_deps(self, bn): + # Get latest computed hash and deps + oldhash, deps = self.srcmd5.get(bn, (None, [])) + # read source + src = open(os.path.join(self.buildpath, bn)).read() + # compute new hash + newhash = hashlib.md5(src).hexdigest() + # compare + match = (oldhash == newhash) + if not match: + # file have changed + # update direct dependencies + deps = [] + self.append_cfile_deps(src, deps) + # store that hashand deps + self.srcmd5[bn] = (newhash, deps) + # recurse through deps + # TODO detect cicular deps. + return reduce(operator.and_, map(self.check_and_update_hash_and_deps, deps), match) + + def calc_source_md5(self): + wholesrcdata = "" + for _Location, CFilesAndCFLAGS, _DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: + # Get CFiles list to give it to makefile + for CFile, _CFLAGS in CFilesAndCFLAGS: + CFileName = os.path.basename(CFile) + wholesrcdata += self.concat_deps(CFileName) + return hashlib.md5(wholesrcdata).hexdigest() + + def calc_md5(self): + return hashlib.md5(self.GetBinaryCode()).hexdigest() + + def build(self): + # Retrieve compiler and linker + self.compiler = self.getCompiler() + self.linker = self.getLinker() + + Builder_CFLAGS = ' '.join(self.getBuilderCFLAGS()) + + # ----------------- GENERATE OBJECT FILES ------------------------ + obns = [] + objs = [] + relink = self.GetBinaryCode() is None + for Location, CFilesAndCFLAGS, _DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: + if CFilesAndCFLAGS: + if Location: + self.CTRInstance.logger.write(".".join(map(str, Location))+" :\n") + else: + self.CTRInstance.logger.write(_("PLC :\n")) + + for CFile, CFLAGS in CFilesAndCFLAGS: + if CFile.endswith(".c"): + bn = os.path.basename(CFile) + obn = os.path.splitext(bn)[0]+".o" + objectfilename = os.path.splitext(CFile)[0]+".o" + + match = self.check_and_update_hash_and_deps(bn) + + if match: + self.CTRInstance.logger.write(" [pass] "+bn+" -> "+obn+"\n") + else: + relink = True + + self.CTRInstance.logger.write(" [CC] "+bn+" -> "+obn+"\n") + + status, _result, _err_result = ProcessLogger( + self.CTRInstance.logger, + "\"%s\" -c \"%s\" -o \"%s\" %s %s" % + (self.compiler, CFile, objectfilename, Builder_CFLAGS, CFLAGS) + ).spin() + + if status: + self.srcmd5.pop(bn) + self.CTRInstance.logger.write_error(_("C compilation of %s failed.\n") % bn) + return False + obns.append(obn) + objs.append(objectfilename) + elif CFile.endswith(".o"): + obns.append(os.path.basename(CFile)) + objs.append(CFile) + + # ---------------- GENERATE OUTPUT FILE -------------------------- + # Link all the object files into one binary file + self.CTRInstance.logger.write(_("Linking :\n")) + if relink: + # Generate list .o files + listobjstring = '"' + '" "'.join(objs) + '"' + + ALLldflags = ' '.join(self.getBuilderLDFLAGS()) + + self.CTRInstance.logger.write(" [CC] " + ' '.join(obns)+" -> " + self.exe + "\n") + + status, _result, _err_result = ProcessLogger( + self.CTRInstance.logger, + "\"%s\" %s -o \"%s\" %s" % + (self.linker, + listobjstring, + self.exe_path, + ALLldflags) + ).spin() + + if status: + return False + + else: + self.CTRInstance.logger.write(" [pass] " + ' '.join(obns)+" -> " + self.exe + "\n") + + # Calculate md5 key and get data for the new created PLC + self.md5key = self.calc_md5() + + # Store new PLC filename based on md5 key + f = open(self._GetMD5FileName(), "w") + f.write(self.md5key) + f.close() + + return True diff -r c1298e7ffe3a -r 8391c11477f4 targets/toolchain_makefile.py --- a/targets/toolchain_makefile.py Fri Mar 24 12:07:47 2017 +0000 +++ b/targets/toolchain_makefile.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,18 +22,23 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re, operator -from util.ProcessLogger import ProcessLogger + +from __future__ import absolute_import +import os +import re +import operator import hashlib -import time +from util.ProcessLogger import ProcessLogger -includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') -class toolchain_makefile(): +includes_re = re.compile('\s*#include\s*["<]([^">]*)[">].*') + + +class toolchain_makefile(object): def __init__(self, CTRInstance): self.CTRInstance = CTRInstance - self.md5key = None + self.md5key = None self.buildpath = None self.SetBuildPath(self.CTRInstance._getBuildPath()) @@ -52,7 +57,7 @@ self.md5key = None try: os.remove(self._GetMD5FileName()) - except Exception, e: + except Exception: pass def GetBinaryCodeMD5(self): @@ -61,12 +66,12 @@ else: try: return open(self._GetMD5FileName(), "r").read() - except IOError, e: + except IOError: return None def concat_deps(self, bn): # read source - src = open(os.path.join(self.buildpath, bn),"r").read() + src = open(os.path.join(self.buildpath, bn), "r").read() # update direct dependencies deps = [] for l in src.splitlines(): @@ -74,17 +79,17 @@ if res is not None: depfn = res.groups()[0] if os.path.exists(os.path.join(self.buildpath, depfn)): - #print bn + " depends on "+depfn + # print bn + " depends on "+depfn deps.append(depfn) # recurse through deps # TODO detect cicular deps. return reduce(operator.concat, map(self.concat_deps, deps), src) def build(self): - srcfiles= [] + srcfiles = [] cflags = [] - wholesrcdata = "" - for Location, CFilesAndCFLAGS, DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: + wholesrcdata = "" + for _Location, CFilesAndCFLAGS, _DoCalls in self.CTRInstance.LocationCFilesAndCFLAGS: # Get CFiles list to give it to makefile for CFile, CFLAGS in CFilesAndCFLAGS: CFileName = os.path.basename(CFile) @@ -92,7 +97,7 @@ srcfiles.append(CFileName) if CFLAGS not in cflags: cflags.append(CFLAGS) - + oldmd5 = self.md5key self.md5key = hashlib.md5(wholesrcdata).hexdigest() @@ -101,28 +106,26 @@ f.write(self.md5key) f.close() - if oldmd5 != self.md5key : + if oldmd5 != self.md5key: target = self.CTRInstance.GetTarget().getcontent() beremizcommand = {"src": ' '.join(srcfiles), "cflags": ' '.join(cflags), "md5": self.md5key, - "buildpath": self.buildpath - } - - # clean sequence of multiple whitespaces - cmd = re.sub(r"[ ]+", " ", target.getCommand()) + "buildpath": self.buildpath} - command = [ token % beremizcommand for token in cmd.split(' ')] + # clean sequence of multiple whitespaces + cmd = re.sub(r"[ ]+", " ", target.getCommand().strip()) + + command = [token % beremizcommand for token in cmd.split(' ')] # Call Makefile to build PLC code and link it with target specific code - status, result, err_result = ProcessLogger(self.CTRInstance.logger, - command).spin() - if status : + status, _result, _err_result = ProcessLogger(self.CTRInstance.logger, + command).spin() + if status: self.md5key = None self.CTRInstance.logger.write_error(_("C compilation failed.\n")) return False return True - else : + else: self.CTRInstance.logger.write(_("Source didn't change, no build.\n")) return True - diff -r c1298e7ffe3a -r 8391c11477f4 targets/typemapping.py --- a/targets/typemapping.py Fri Mar 24 12:07:47 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,116 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# This file is part of Beremiz, a Integrated Development Environment for -# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. -# -# Copyright (C) 2011: Edouard TISSERANT and Laurent BESSARD -# -# See COPYING file for copyrights details. -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -import ctypes -ctypes.pythonapi.PyString_AsString.argtypes = (ctypes.c_void_p,) -ctypes.pythonapi.PyString_AsString.restype = ctypes.POINTER(ctypes.c_char) - - -from ctypes import * -from datetime import timedelta as td - -class IEC_STRING(Structure): - """ - Must be changed according to changes in iec_types.h - """ - _fields_ = [("len", c_uint8), - ("body", c_char * 126)] - -class IEC_TIME(Structure): - """ - Must be changed according to changes in iec_types.h - """ - _fields_ = [("s", c_long), #tv_sec - ("ns", c_long)] #tv_nsec - -def _t(t, u=lambda x:x.value, p=lambda t,x:t(x)): return (t, u, p) -def _ttime(): return (IEC_TIME, - lambda x:td(0, x.s, x.ns/1000), - lambda t,x:t(x.days * 24 * 3600 + x.seconds, x.microseconds*1000)) - -SameEndianessTypeTranslator = { - "BOOL" : _t(c_uint8, lambda x:x.value!=0), - "STEP" : _t(c_uint8), - "TRANSITION" : _t(c_uint8), - "ACTION" : _t(c_uint8), - "SINT" : _t(c_int8), - "USINT" : _t(c_uint8), - "BYTE" : _t(c_uint8), - "STRING" : (IEC_STRING, - lambda x:x.body[:x.len], - lambda t,x:t(len(x),x)), - "INT" : _t(c_int16), - "UINT" : _t(c_uint16), - "WORD" : _t(c_uint16), - "DINT" : _t(c_int32), - "UDINT" : _t(c_uint32), - "DWORD" : _t(c_uint32), - "LINT" : _t(c_int64), - "ULINT" : _t(c_uint64), - "LWORD" : _t(c_uint64), - "REAL" : _t(c_float), - "LREAL" : _t(c_double), - "TIME" : _ttime(), - "TOD" : _ttime(), - "DATE" : _ttime(), - "DT" : _ttime(), - } - -SwapedEndianessTypeTranslator = { - #TODO - } - -TypeTranslator=SameEndianessTypeTranslator - -# Construct debugger natively supported types -DebugTypesSize = dict([(key,sizeof(t)) for key,(t,p,u) in SameEndianessTypeTranslator.iteritems() if t is not None]) - -def UnpackDebugBuffer(buff, indexes): - res = [] - buffoffset = 0 - buffsize = len(buff) - buffptr = cast(ctypes.pythonapi.PyString_AsString(id(buff)),c_void_p).value - for iectype in indexes: - c_type,unpack_func, pack_func = \ - TypeTranslator.get(iectype, - (None,None,None)) - if c_type is not None and buffoffset < buffsize: - cursor = c_void_p( buffptr + buffoffset) - value = unpack_func( cast(cursor, - POINTER(c_type)).contents) - buffoffset += sizeof(c_type) if iectype != "STRING" else len(value)+1 - res.append(value) - else: - break - if buffoffset and buffoffset == buffsize: - return res - return None - - - -LogLevels = ["CRITICAL","WARNING","INFO","DEBUG"] -LogLevelsCount = len(LogLevels) -LogLevelsDict = dict(zip(LogLevels,range(LogLevelsCount))) -LogLevelsDefault = LogLevelsDict["DEBUG"] - diff -r c1298e7ffe3a -r 8391c11477f4 tests/first_steps/beremiz.xml --- a/tests/first_steps/beremiz.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/first_steps/beremiz.xml Tue Jan 30 16:06:58 2018 +0100 @@ -1,6 +1,4 @@ - - - + diff -r c1298e7ffe3a -r 8391c11477f4 tests/svgui/svgui@svgui/gui.svg --- a/tests/svgui/svgui@svgui/gui.svg Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/svgui/svgui@svgui/gui.svg Tue Jan 30 16:06:58 2018 +0100 @@ -615,7 +615,7 @@ ry="5.029737" /> /dev/null) && pep8="pep8" + test -n $pep8 && (which pycodestyle > /dev/null) && pep8="pycodestyle" + if [ -z $pep8 ]; then + echo "pep8/pycodestyle is not found" + set_exit_error + fi +} + +pep8_checks_default() +{ + echo "Check basic code-style problems for PEP-8" + + test -n $pep8 && pep8_detect + test -z $pep8 && return + + user_ignore= + # user_ignore=$user_ignore,E265 # E265 block comment should start with '# ' + user_ignore=$user_ignore,E501 # E501 line too long (80 > 79 characters) + + # ignored by default, + default_ignore= + default_ignore=$default_ignore,E121 # E121 continuation line under-indented for hanging indent + default_ignore=$default_ignore,E123 # E123 closing bracket does not match indentation of opening bracket’s line + default_ignore=$default_ignore,E126 # E126 continuation line over-indented for hanging indent + default_ignore=$default_ignore,E133 # E133 closing bracket is missing indentation + default_ignore=$default_ignore,E226 # E226 missing whitespace around arithmetic operator + default_ignore=$default_ignore,E241 # E241 multiple spaces after ':' + default_ignore=$default_ignore,E242 # E242 tab after ‘,’ + default_ignore=$default_ignore,E704 # E704 multiple statements on one line (def) + default_ignore=$default_ignore,W503 # W503 line break occurred before a binary operator + ignore=$user_ignore,$default_ignore + + # $pep8 --ignore $ignore --exclude build ./ + $pep8 --max-line-length 300 --exclude build ./ + if [ $? -ne 0 ]; then + set_exit_error + fi +} + + +pep8_checks_selected() +{ + echo "Check basic code-style problems for PEP-8 (selective)" + + test -n $pep8 && pep8_detect + test -z $pep8 && return + + # select checks: + user_select= + user_select=$user_select,W291 # W291 trailing whitespace + user_select=$user_select,E401 # E401 multiple imports on one line + user_select=$user_select,E265 # E265 block comment should start with '# ' + user_select=$user_select,E228 # E228 missing whitespace around modulo operator + user_select=$user_select,W293 # W293 blank line contains whitespace + user_select=$user_select,E302 # E302 expected 2 blank lines, found 1 + user_select=$user_select,E261 # E261 at least two spaces before inline comment + user_select=$user_select,E271 # E271 multiple spaces after keyword + user_select=$user_select,E231 # E231 missing whitespace after ',' + user_select=$user_select,E303 # E303 too many blank lines (2) + user_select=$user_select,E225 # E225 missing whitespace around operator + user_select=$user_select,E711 # E711 comparison to None should be 'if cond is not None:' + user_select=$user_select,E251 # E251 unexpected spaces around keyword / parameter equals + user_select=$user_select,E227 # E227 missing whitespace around bitwise or shift operator + user_select=$user_select,E202 # E202 whitespace before ')' + user_select=$user_select,E201 # E201 whitespace after '{' + user_select=$user_select,W391 # W391 blank line at end of file + user_select=$user_select,E305 # E305 expected 2 blank lines after class or function definition, found X + user_select=$user_select,E306 # E306 expected 1 blank line before a nested definition, found X + user_select=$user_select,E703 # E703 statement ends with a semicolon + user_select=$user_select,E701 # E701 multiple statements on one line (colon) + user_select=$user_select,E221 # E221 multiple spaces before operator + user_select=$user_select,E741 # E741 ambiguous variable name 'l' + user_select=$user_select,E111 # E111 indentation is not a multiple of four + user_select=$user_select,E222 # E222 multiple spaces after operator + user_select=$user_select,E712 # E712 comparison to True should be 'if cond is True:' or 'if cond:' + user_select=$user_select,E262 # E262 inline comment should start with '# ' + user_select=$user_select,E203 # E203 whitespace before ',' + user_select=$user_select,E731 # E731 do not assign a lambda expression, use a def + user_select=$user_select,W601 # W601 .has_key() is deprecated, use 'in' + user_select=$user_select,E502 # E502 the backslash is redundant between brackets + user_select=$user_select,W602 # W602 deprecated form of raising exception + user_select=$user_select,E129 # E129 visually indented line with same indent as next logical line + user_select=$user_select,E127 # E127 continuation line over-indented for visual indent + user_select=$user_select,E128 # E128 continuation line under-indented for visual indent + user_select=$user_select,E125 # E125 continuation line with same indent as next logical line + user_select=$user_select,E114 # E114 indentation is not a multiple of four (comment) + user_select=$user_select,E211 # E211 whitespace before '[' + user_select=$user_select,W191 # W191 indentation contains tabs + user_select=$user_select,E101 # E101 indentation contains mixed spaces and tabs + user_select=$user_select,E124 # E124 closing bracket does not match visual indentation + user_select=$user_select,E272 # E272 multiple spaces before keyword + user_select=$user_select,E713 # E713 test for membership should be 'not in' + user_select=$user_select,E122 # E122 continuation line missing indentation or outdented + user_select=$user_select,E131 # E131 continuation line unaligned for hanging indent + user_select=$user_select,E721 # E721 do not compare types, use 'isinstance()' + user_select=$user_select,E115 # E115 expected an indented block (comment) + user_select=$user_select,E722 # E722 do not use bare except' + user_select=$user_select,E266 # E266 too many leading '#' for block comment + user_select=$user_select,E402 # E402 module level import not at top of file + user_select=$user_select,W503 # W503 line break before binary operator + + $pep8 --select $user_select --exclude=build . + if [ $? -ne 0 ]; then + set_exit_error + fi +} + +flake8_checks() +{ + echo "Check for problems using flake8 ..." + + which flake8 > /dev/null + if [ $? -ne 0 ]; then + echo "flake8 is not found" + set_exit_error + return + fi + + flake8 --max-line-length=300 --exclude=build --builtins="_" ./ + if [ $? -ne 0 ]; then + set_exit_error + fi +} + +pylint_checks() +{ + echo "Check for problems using pylint ..." + + which pylint > /dev/null + if [ $? -ne 0 ]; then + echo "pylint is not found" + set_exit_error + return + fi + + export PYTHONPATH="$PWD/../CanFestival-3/objdictgen":$PYTHONPATH + + disable= + # These warnings most likely will not be fixed + + disable=$disable,C0103 # invalid-name + disable=$disable,C0326 # bad whitespace + disable=$disable,W0110 # (deprecated-lambda) map/filter on lambda could be replaced by comprehension + disable=$disable,W1401 # (anomalous-backslash-in-string) Anomalous backslash in string: '\.'. String constant might be missing an r prefix. + disable=$disable,W0613 # (unused-argument) Unused argument 'X' + disable=$disable,W0622 # (redefined-builtin) Redefining built-in + disable=$disable,W0621 # (redefined-outer-name) Redefining name 'Y' from outer scope (line X) + disable=$disable,W0122 # (exec-used) Use of exec + disable=$disable,W0123 # (eval-used) Use of eval + disable=$disable,I0011 # (locally-disabled) Locally disabling ungrouped-imports (C0412) + disable=$disable,R0204 # (redefined-variable-type) Redefinition of current type from X to Y + disable=$disable,R0201 # (no-self-use) Method could be a function + disable=$disable,W0221 # (arguments-differ) Arguments number differs from overridden 'X' method + disable=$disable,C0201 # (consider-iterating-dictionary) Consider iterating the dictionary directly instead of calling .keys() + + # It'd be nice to fix warnings below some day + disable=$disable,C0111 # missing-docstring + disable=$disable,W0703 # broad-except + disable=$disable,C0301 # Line too long + disable=$disable,C0302 # Too many lines in module + disable=$disable,W0511 # fixme + disable=$disable,R0901 # (too-many-ancestors) Too many ancestors (9/7) + disable=$disable,R0902 # (too-many-instance-attributes) Too many instance attributes (10/7) + disable=$disable,R0903 # (too-few-public-methods) Too few public methods (0/2) + disable=$disable,R0904 # (too-many-public-methods) Too many public methods (41/20) + disable=$disable,R0911 # (too-many-return-statements) Too many return statements (7/6) + disable=$disable,R0912 # (too-many-branches) Too many branches (61/12) + disable=$disable,R0913 # (too-many-arguments) Too many arguments (6/5) + disable=$disable,R0914 # (too-many-locals) Too many local variables (18/15) + disable=$disable,R0915 # (too-many-statements) Too many statements (57/50) + disable=$disable,R0916 # (too-many-boolean-expressions) Too many boolean expressions in if statement (6/5) + disable=$disable,R0101 # (too-many-nested-blocks) Too many nested blocks (7/5) + disable=$disable,R0801 # (duplicate-code) Similar lines in N files + + + enable= + enable=$enable,E1601 # print statement used + enable=$enable,C0325 # (superfluous-parens) Unnecessary parens after keyword + enable=$enable,W0404 # reimported module + enable=$enable,C0411 # (wrong-import-order) standard import "import x" comes before "import y" + enable=$enable,W0108 # (unnecessary-lambda) Lambda may not be necessary + enable=$enable,C0412 # (ungrouped-imports) Imports from package X are not grouped + enable=$enable,C0321 # (multiple-statements) More than one statement on a single line + enable=$enable,W0231 # (super-init-not-called) __init__ method from base class is not called + enable=$enable,W0105 # (pointless-string-statement) String statement has no effect + enable=$enable,W0311 # (bad-indentation) Bad indentation. Found 16 spaces, expected 12 + enable=$enable,W0101 # (unreachable) Unreachable code + enable=$enable,E0102 # (function-redefined) method already defined + enable=$enable,W0602 # (global-variable-not-assigned) Using global for 'X' but no assignment is done + enable=$enable,W0612 # (unused-variable) Unused variable 'X' + enable=$enable,W0611 # (unused-import) Unused import X + enable=$enable,C1001 # (old-style-class) Old-style class defined. Problem with PyJS + enable=$enable,W0102 # (dangerous-default-value) Dangerous default value {} as argument + enable=$enable,W0403 # (relative-import) Relative import 'Y', should be 'X.Y' + enable=$enable,C0112 # (empty-docstring) + enable=$enable,W0631 # (undefined-loop-variable) Using possibly undefined loop variable 'X' + enable=$enable,W0104 # (pointless-statement) Statement seems to have no effect + enable=$enable,W0107 # (unnecessary-pass) Unnecessary pass statement + enable=$enable,W0406 # (import-self) Module import itself + enable=$enable,C0413 # (wrong-import-position) Import "import X" should be placed at the top of the module + enable=$enable,E1305 # (too-many-format-args) Too many arguments for format string + enable=$enable,E0704 # (misplaced-bare-raise) The raise statement is not inside an except clause + enable=$enable,C0123 # (unidiomatic-typecheck) Using type() instead of isinstance() for a typecheck. + enable=$enable,E0601 # (used-before-assignment) Using variable 'X' before assignment + enable=$enable,E1120 # (no-value-for-parameter) No value for argument 'X' in function call + enable=$enable,E0701 # (bad-except-order) Bad except clauses order (X is an ancestor class of Y) + enable=$enable,E0611 # (no-name-in-module) No name 'X' in module 'Y' + enable=$enable,E0213 # (no-self-argument) Method should have "self" as first argument + enable=$enable,E0401 # (import-error) Unable to import 'X' + enable=$enable,E1121 # (too-many-function-args) Too many positional arguments for function call + enable=$enable,E0602 # (undefined-variable) Undefined variable 'X' + enable=$enable,W0232 # (no-init) Class has no __init__ method + enable=$enable,W0233 # (non-parent-init-called) __init__ method from a non direct base class 'X' is called + enable=$enable,W0601 # (global-variable-undefined) Global variable 'X' undefined at the module level + enable=$enable,W0623 # (redefine-in-handler) Redefining name 'X' from outer scope (line Y) in exception handler + enable=$enable,W0106 # (expression-not-assigned) Expression "X" is assigned to nothing + enable=$enable,C0330 # (bad-continuation) Wrong hanging indentation before block + enable=$enable,E1136 # (unsubscriptable-object) Value 'X' is unsubscriptable + enable=$enable,W1618 # (no-absolute-import) import missing `from __future__ import absolute_import` + # enable= + + options= + options="$options --rcfile=.pylint" + # options="$options --py3k" # report errors for Python 3 porting + + if [ -n "$enable" ]; then + options="$options --disable=all" + options="$options --enable=$enable" + else + options="$options --disable=$disable" + fi + # echo $options + + find ./ -name '*.py' | grep -v '/build/' | xargs pylint $options + if [ $? -ne 0 ]; then + set_exit_error + fi +} + +main() +{ + compile_checks + pep8_checks_default + # pep8_checks_selected + # flake8_checks + pylint_checks + exit $exit_code +} + +main diff -r c1298e7ffe3a -r 8391c11477f4 tests/tools/conftest.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/conftest.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import os +import sys + +# import pytest +# import xvfbwrapper + + +def init_environment(): + """Append module root directory to sys.path""" + try: + import Beremiz as _Beremiz + except ImportError: + sys.path.append( + os.path.abspath( + os.path.join( + os.path.dirname(__file__), '..', '..') + ) + ) + + +init_environment() + +# +# Something seems to be broken in Beremiz application, +# because after tests in test_application.py during Xvfb shutdown +# pytest returns error message: +# pytest: Fatal IO error 11 (Die Ressource ist zur Zeit nicht verfügbar) on X server :2821. +# +# As a result of that pytest returns code 1 as some tests were failed, +# but they aren't. +# +# To avoid this Xvfb is launched and killed not by pytest. +# $ Xvfb :42 -screen 0 1280x1024x24 & +# $ export DISPLAY=:42 +# $ pytest --timeout=10 ./tests/tools +# $ pkill -9 Xvfb +# +# TODO: find root of this problem. + + +# vdisplay = None +# +# @pytest.fixture(scope="session", autouse=True) +# def start_xvfb_server(request): +# global vdisplay +# vdisplay = xvfbwrapper.Xvfb(width=1280, height=720) +# vdisplay.start() +# request.addfinalizer(stop_xvfb_server) +# +# def stop_xvfb_server(): +# if vdisplay is not None: +# vdisplay.stop() diff -r c1298e7ffe3a -r 8391c11477f4 tests/tools/run_python_tests.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/run_python_tests.sh Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,17 @@ +#!/bin/sh + +LC_ALL=ru_RU.utf-8 + +export DISPLAY=:42 +Xvfb $DISPLAY -screen 0 1280x1024x24 & +sleep 1 + +ret=0 +DELAY=400 +KILL_DELAY=$(($DELAY + 30)) +timeout -k $KILL_DELAY $DELAY pytest --timeout=10 ./tests/tools +ret=$? + +pkill -9 Xvfb + +exit $ret diff -r c1298e7ffe3a -r 8391c11477f4 tests/tools/test_CustomIntCtrl.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/test_CustomIntCtrl.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import unittest +import time + +import wx +import conftest +import controls.CustomIntCtrl + + +class TestCustomIntCtrl(unittest.TestCase): + def setUp(self): + self.app = wx.App() + self.frame = wx.Frame(None) + + def tearDown(self): + self.frame.Destroy() + wx.CallAfter(self.app.Exit) + self.app.MainLoop() + + def testMaxLimit(self): + """Test working upper bound""" + self.AddControls() + self.int_ctrl.SetValue(self.max_val + 100) + self.ProcessEvents() + + self.txt_ctrl.SetFocus() + self.ProcessEvents() + self.assertEqual(self.int_ctrl.GetValue(), self.max_val) + + def testMinLimit(self): + """Test working lower bound""" + self.AddControls() + self.int_ctrl.SetValue(self.min_val - 100) + self.ProcessEvents() + + self.txt_ctrl.SetFocus() + self.ProcessEvents() + + self.assertEqual(self.int_ctrl.GetValue(), self.min_val) + + def testCorrectValue(self): + """Test case if no limiting is necessary""" + self.AddControls() + val = (self.max_val + self.min_val) / 2 + self.int_ctrl.SetValue(val) + self.ProcessEvents() + + self.txt_ctrl.SetFocus() + self.ProcessEvents() + + self.assertEqual(self.int_ctrl.GetValue(), val) + + def testEventBinding(self): + """Test event sending after edit and bound checks are done""" + self.AddControls() + self.event_happend = False + + def EventHandler(event): + self.event_happend = True + event.Skip() + + self.int_ctrl.Bind(controls.CustomIntCtrl.EVT_CUSTOM_INT, EventHandler) + + val = (self.max_val + self.min_val) / 2 + + self.int_ctrl.SetValue(val) + self.ProcessEvents() + self.txt_ctrl.SetFocus() + + self.ProcessEvents() + self.txt_ctrl.SetFocus() + self.ProcessEvents() + + self.assertEqual(self.int_ctrl.GetValue(), val) + self.assertTrue(self.event_happend) + + def testLongNumbers(self): + """Test support of long integer""" + self.AddControls() + val = 40000000000 + self.int_ctrl.SetMax(val) + self.int_ctrl.SetValue(val) + self.ProcessEvents() + + self.txt_ctrl.SetFocus() + self.ProcessEvents() + + self.assertEqual(val, val) + + def ProcessEvents(self): + for dummy in range(0, 10): + wx.Yield() + time.sleep(0.01) + + def AddControls(self): + vs = wx.BoxSizer(wx.VERTICAL) + self.int_ctrl = controls.CustomIntCtrl(self.frame) + self.txt_ctrl = wx.TextCtrl(self.frame) + vs.Add(self.int_ctrl, 0, wx.ALIGN_CENTRE | wx.ALL, 5) + vs.Add(self.txt_ctrl, 0, wx.ALIGN_CENTRE | wx.ALL, 5) + self.frame.SetSizer(vs) + vs.Fit(self.frame) + self.frame.Show() + self.frame.Raise() + + self.min_val = 50 + self.max_val = 100 + self.int_ctrl.SetBounds(self.min_val, self.max_val) + self.ProcessEvents() + + +if __name__ == '__main__': + conftest.init_environment() + unittest.main() diff -r c1298e7ffe3a -r 8391c11477f4 tests/tools/test_application.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/tools/test_application.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +from __future__ import print_function +import os +import sys +import unittest +import time + +import pytest +import wx +import ddt + +import conftest +import Beremiz +import PLCOpenEditor + + +class UserApplicationTest(unittest.TestCase): + def InstallExceptionHandler(self): + def handle_exception(e_type, e_value, e_traceback): + # traceback.print_exception(e_type, e_value, e_traceback) + self.exc_info = [e_type, e_value, e_traceback] + self.exc_info = None + self.old_excepthook = sys.excepthook + sys.excepthook = handle_exception + + def StartApp(self): + self.app = None + + def FinishApp(self): + wx.CallAfter(self.app.frame.Close) + self.app.MainLoop() + self.app = None + + def setUp(self): + self.app = None + + def tearDown(self): + if self.app is not None and self.app.frame is not None: + self.FinishApp() + + def RunUIActions(self, actions): + for act in actions: + wx.CallAfter(*act) + self.ProcessEvents() + + def CheckForErrors(self): + if self.exc_info is not None: + # reraise catched previously exception + raise self.exc_info[0], self.exc_info[1], self.exc_info[2] + + def ProcessEvents(self): + for dummy in range(0, 30): + self.CheckForErrors() + wx.Yield() + time.sleep(0.01) + + +@ddt.ddt +class BeremizApplicationTest(UserApplicationTest): + """Test Beremiz as whole application""" + + def StartApp(self): + self.app = Beremiz.BeremizIDELauncher() + # disable default exception handler in Beremiz + self.app.InstallExceptionHandler = lambda: None + self.InstallExceptionHandler() + self.app.PreStart() + self.app.frame.Show() + self.ProcessEvents() + self.app.frame.ShowFullScreen(True) + self.ProcessEvents() + + def FinishApp(self): + wx.CallAfter(self.app.frame.Close) + self.app.MainLoop() + time.sleep(1) + self.app = None + + def GetSkippedProjectTreeItems(self): + """ + Returns the list of skipped items in the project tree. + + Beremiz test don't need to skip any elemnts in the project tree. + """ + return [] + + def OpenAllProjectElements(self): + """Open editor for every object in the project tree""" + self.app.frame.ProjectTree.ExpandAll() + self.ProcessEvents() + item = self.app.frame.ProjectTree.GetRootItem() + skip = self.GetSkippedProjectTreeItems() + tree_id = self.app.frame.ProjectTree.GetId() + while item is not None: + self.app.frame.ProjectTree.SelectItem(item, True) + self.ProcessEvents() + if item not in skip: + event = wx.lib.agw.customtreectrl.TreeEvent( + wx.lib.agw.customtreectrl.wxEVT_TREE_ITEM_ACTIVATED, + tree_id, item) + self.app.frame.OnProjectTreeItemActivated(event) + self.ProcessEvents() + item = self.app.frame.ProjectTree.GetNextVisible(item) + + def CheckTestProject(self, project): + sys.argv = ["", project] + self.StartApp() + self.OpenAllProjectElements() + user_actions = self.GetUserActions() + self.RunUIActions(user_actions) + self.FinishApp() + + def GetProjectPath(self, project): + return os.path.abspath(os.path.join(os.path.dirname(__file__), "..", project)) + + def GetUserActions(self): + """ + Returns list of user actions that will be executed + on every test project by testCheckProject test. + """ + user_actions = [ + [self.app.frame.SwitchFullScrMode, None], + [self.app.frame.SwitchFullScrMode, None], + [self.app.frame.CTR._Clean], + [self.app.frame.CTR._Build], + [self.app.frame.CTR._Connect], + [self.app.frame.CTR._Transfer], + [self.app.frame.CTR._Run], + [self.app.frame.CTR._Stop], + [self.app.frame.CTR._Disconnect], + [self.app.frame.CTR._Clean], + ] + return user_actions + + def testStartUp(self): + """Checks whether the app starts and finishes correctly""" + sys.argv = [""] + self.StartApp() + self.FinishApp() + + @ddt.data( + "first_steps", + "logging", + "svgui", + "traffic_lights", + "wxGlade", + "python", + "wiimote", + "wxHMI", + ) + @pytest.mark.timeout(30) + def testCheckProject(self, name): + """ + Checks that test PLC project can be open, + compiled and run on SoftPLC. + """ + project = self.GetProjectPath(name) + print("Testing example " + name) + self.CheckTestProject(project) + + +class PLCOpenEditorApplicationTest(BeremizApplicationTest): + """Test PLCOpenEditor as whole application""" + + def StartApp(self): + self.app = PLCOpenEditor.PLCOpenEditorApp() + # disable default exception handler in application + self.app.InstallExceptionHandler = lambda: None + self.InstallExceptionHandler() + self.app.Show() + self.ProcessEvents() + self.app.frame.ShowFullScreen(True) + self.ProcessEvents() + + def FinishApp(self): + wx.CallAfter(self.app.frame.Close) + self.app.MainLoop() + time.sleep(1) + self.app = None + + def GetSkippedProjectTreeItems(self): + """ + Returns the list of skipped items in the project tree. + + Root item opens dialog window for project settings. + To avoid code that handles closing dialog windows just skip this item. + """ + return [self.app.frame.ProjectTree.GetRootItem()] + + def GetUserActions(self): + return [] + + def GetProjectPath(self, project): + """Open PLC program in every Beremiz test project""" + project_dir = BeremizApplicationTest.GetProjectPath(self, project) + return os.path.join(project_dir, "plc.xml") + + +if __name__ == '__main__': + conftest.init_environment() + unittest.main() diff -r c1298e7ffe3a -r 8391c11477f4 tests/wamp/.crossbar/config.json --- a/tests/wamp/.crossbar/config.json Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wamp/.crossbar/config.json Tue Jan 30 16:06:58 2018 +0100 @@ -1,43 +1,54 @@ - { - "controller": { - }, - "workers": [ - { - "type": "router", - "options": { - "pythonpath": [".."] - }, - "realms": [ - { - "name": "Automation", - "roles": [ - { - "name": "anonymous", - "permissions": [ + "version": 2, + "controller": {}, + "workers": [ + { + "type": "router", + "options": { + "pythonpath": [ + ".." + ] + }, + "realms": [ + { + "name": "Automation", + "roles": [ { - "uri": "*", - "publish": true, - "subscribe": true, - "call": true, - "register": true + "name": "anonymous", + "permissions": [ + { + "uri": "", + "match": "prefix", + "allow": { + "call": true, + "register": true, + "publish": true, + "subscribe": true + }, + "disclose": { + "caller": false, + "publisher": false + }, + "cache": true + } + ] } - ] - } - ] - } - ], - "transports": [ - { - "type": "websocket", - "endpoint": { - "type": "tcp", - "port": 8888 - }, - "url": "ws://127.0.0.1:8888/", - "serializers" : ["msgpack"] - } - ] - } - ] + ] + } + ], + "transports": [ + { + "type": "websocket", + "endpoint": { + "type": "tcp", + "port": 8888 + }, + "url": "ws://127.0.0.1:8888/", + "serializers": [ + "msgpack" + ] + } + ] + } + ] } diff -r c1298e7ffe3a -r 8391c11477f4 tests/wamp/README --- a/tests/wamp/README Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wamp/README Tue Jan 30 16:06:58 2018 +0100 @@ -1,12 +1,25 @@ -This project contains wamp client config to be loaded at runtime startup. +Crossbar test router configuration is available in .crossbar directory. + +Starting command: +crossbar start + +This project contains wamp client config to be loaded at runtime startup. project_files/wampconf.json wampconf.json is in "Project Files", so it is copied to runtime's working directory, and then loaded after program transfer + runtime restart. Otherwise, wamp config file path can be forced : -./Beremiz_service.py -c /path/to/my/wampconf.json /working/dir +./Beremiz_service.py -c /path/to/my/wampconf.json /working/dir -Crossbar test router configuration is available in .crossbar directory. Start with : -crossbar -d start +Otherwise, path for CRA secret can be forced : +./Beremiz_service.py -s /path/to/my/secret /working/dir +Tested on version: + Crossbar.io : 17.12.1 (Crossbar.io COMMUNITY) + Autobahn : 17.10.1 (with JSON, MessagePack, CBOR, UBJSON) + Twisted : 17.9.0-EPollReactor + LMDB : 0.93/lmdb-0.9.18 + Python : 2.7.12/CPython + + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wamp/beremiz.xml --- a/tests/wamp/beremiz.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wamp/beremiz.xml Tue Jan 30 16:06:58 2018 +0100 @@ -1,4 +1,4 @@ - + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wiimote/plc.xml --- a/tests/wiimote/plc.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wiimote/plc.xml Tue Jan 30 16:06:58 2018 +0100 @@ -1,14 +1,7 @@ - - - - + + + + @@ -229,10 +222,18 @@ c + + + + + + - + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wxGlade/HMIFrame@wxglade_hmi/hmi.wxg --- a/tests/wxGlade/HMIFrame@wxglade_hmi/hmi.wxg Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wxGlade/HMIFrame@wxglade_hmi/hmi.wxg Tue Jan 30 16:06:58 2018 +0100 @@ -1,7 +1,7 @@ - + - + HMIFrame @@ -26,7 +26,7 @@ 1 - + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wxGlade/plc.xml --- a/tests/wxGlade/plc.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wxGlade/plc.xml Tue Jan 30 16:06:58 2018 +0100 @@ -1,14 +1,7 @@ - - - - + + + + @@ -28,7 +21,7 @@ - + @@ -38,7 +31,7 @@ - + @@ -47,15 +40,15 @@ - - + + - - + + @@ -63,8 +56,8 @@ - - + + @@ -83,54 +76,52 @@ - - + + BOOL#TRUE - - + + 'int(HMIFrame.checkbox_1.GetValue())' - - + + ')' - - + + 'HMIFrame.spin_ctrl_1.SetValue(' - - + + - + - - - - + + - + - - + + @@ -139,29 +130,29 @@ - - - - - - - - - - - - + + + + + + + + + + + + - + + - - - - + + + @@ -169,8 +160,8 @@ - - + + @@ -178,12 +169,12 @@ - - - - - - + + + + + + @@ -202,17 +193,17 @@ - - + + - - - - + + + + @@ -220,10 +211,10 @@ - - - - + + + + @@ -231,8 +222,8 @@ - - + + @@ -240,8 +231,8 @@ - - + + @@ -260,13 +251,13 @@ - - + + - - + + @@ -274,15 +265,15 @@ counter - - + + - - + + @@ -290,8 +281,8 @@ - - + + @@ -310,15 +301,15 @@ - - + + - - + + @@ -326,10 +317,10 @@ - - - - + + + + @@ -348,17 +339,15 @@ - - + + - - - - + + @@ -372,46 +361,53 @@ - - + + - + - - + + - + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + INT#1 + + + + + + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wxHMI/HMI@wxglade_hmi/hmi.wxg --- a/tests/wxHMI/HMI@wxglade_hmi/hmi.wxg Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wxHMI/HMI@wxglade_hmi/hmi.wxg Tue Jan 30 16:06:58 2018 +0100 @@ -1,9 +1,9 @@ - + - + - + frame_1 0 @@ -13,7 +13,7 @@ 4 0 - wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL + wxALIGN_CENTER 0 @@ -53,7 +53,7 @@ 0 - 4 + 6 2 0 @@ -64,6 +64,8 @@ + + @@ -72,26 +74,20 @@ 0 - + - SetPLCGlobalVar + SetPLCTestBtnGlobalVar - - "DrawTest" - 0 - - + + - SetPLCGlobalVar + SetPLCTestBtn2GlobalVar - - "DrawEscher" - diff -r c1298e7ffe3a -r 8391c11477f4 tests/wxHMI/HMI@wxglade_hmi/pyfile.xml --- a/tests/wxHMI/HMI@wxglade_hmi/pyfile.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wxHMI/HMI@wxglade_hmi/pyfile.xml Tue Jan 30 16:06:58 2018 +0100 @@ -1,134 +1,140 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wxHMI/plc.xml --- a/tests/wxHMI/plc.xml Fri Mar 24 12:07:47 2017 +0000 +++ b/tests/wxHMI/plc.xml Tue Jan 30 16:06:58 2018 +0100 @@ -1,592 +1,1641 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Power_ON - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Power_OFF - - - - - - - - - - - - - power - - - - - - - DrawTest - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ZaxisPos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - - - - - - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - XaxisPos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - tmp - - - - - - - 1 - - - - - - - tmp - - - - - - - - - - - - - YaxisPos - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - - - - - - - power - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - BOOL#TRUE - - - - - - - 'wxglade_hmi.UpdPos()' - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - LocalVar0 - - - - - - - - - - - - - LocalVar1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Power_OFF + + + + + + + Power_ON + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + power + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XaxisPos + + + + + + + power + + + + + + + XAxisMinus + + + + + + + XAxisPlus + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DrawTestDo.Q1 + + + + + + + DrawTestDo.Q1 + + + + + + + power + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + YaxisPos + + + + + + + YAxisMinus + + + + + + + YAxisPlus + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DrawTestDo.Q1 + + + + + + + DrawLogoDo.Q1 + + + + + + + power + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TaxisPos + + + + + + + power + + + + + + + TAxisPlus + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ZaxisPos + + + + + + + ZAxisMinus + + + + + + + ZAxisPlus + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + TAxisMinus + + + + + + + DrawLogoDo.Q1 + + + + + + + DrawTest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Power_OFF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DrawTestBtn + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DrawLogo + + + + + + + + + + + + + DrawLogo + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Power_OFF + + + + + + + DrawTest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + BOOL#TRUE + + + + + + + 'wxglade_hmi.UpdPos()' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + InitValue + + + + + + + + + + + NotInitializedVariable + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + counter + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Out + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Period + + + + + + + 2 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Out + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Power + + + + + + + T#100ms + + + + + + + Up + + + + + + + Down + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + INT#32767 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r c1298e7ffe3a -r 8391c11477f4 tests/wxHMI/project_files/DrawLogo.png Binary file tests/wxHMI/project_files/DrawLogo.png has changed diff -r c1298e7ffe3a -r 8391c11477f4 util/BitmapLibrary.py --- a/util/BitmapLibrary.py Fri Mar 24 12:07:47 2017 +0000 +++ b/util/BitmapLibrary.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,49 +22,54 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +from __future__ import absolute_import import os import wx -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Library Structures -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- BitmapLibrary = {} BitmapFolders = [] -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Library Helpers -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + def AddBitmapFolder(path): if os.path.exists(path) and os.path.isdir(path) and path not in BitmapFolders: BitmapFolders.append(path) + def SearchBitmap(bmp_name): for folder in BitmapFolders: bmp_path = os.path.join(folder, bmp_name + ".png") if os.path.isfile(bmp_path): return wx.Bitmap(bmp_path) return None - + + def GetBitmap(bmp_name1, bmp_name2=None, size=None): bmp = BitmapLibrary.get((bmp_name1, bmp_name2, size)) if bmp is not None: return bmp - + if bmp_name2 is None: bmp = SearchBitmap(bmp_name1) else: # Bitmap with two icon bmp1 = SearchBitmap(bmp_name1) bmp2 = SearchBitmap(bmp_name2) - + if bmp1 is not None and bmp2 is not None: # Calculate bitmap size width = bmp1.GetWidth() + bmp2.GetWidth() - 1 height = max(bmp1.GetHeight(), bmp2.GetHeight()) - + # Create bitmap with both icons bmp = wx.EmptyBitmap(width, height) dc = wx.MemoryDC() @@ -73,13 +78,13 @@ dc.DrawBitmap(bmp1, 0, 0) dc.DrawBitmap(bmp2, bmp1.GetWidth() - 1, 0) dc.Destroy() - + elif bmp1 is not None: bmp = bmp1 elif bmp2 is not None: bmp = bmp2 - + if bmp is not None: BitmapLibrary[(bmp_name1, bmp_name2, size)] = bmp - + return bmp diff -r c1298e7ffe3a -r 8391c11477f4 util/ExceptionHandler.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/ExceptionHandler.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,156 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2016-2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import os +import sys +import time +import tempfile +import platform +import traceback +import threading +import wx + +Max_Traceback_List_Size = 20 + + +def Display_Exception_Dialog(e_type, e_value, e_tb, bug_report_path): + trcbck_lst = [] + for i, line in enumerate(traceback.extract_tb(e_tb)): + trcbck = " " + str(i+1) + ". " + if line[0].find(os.getcwd()) == -1: + trcbck += "file : " + str(line[0]) + ", " + else: + trcbck += "file : " + str(line[0][len(os.getcwd()):]) + ", " + trcbck += "line : " + str(line[1]) + ", " + "function : " + str(line[2]) + trcbck_lst.append(trcbck) + + # Allow clicking.... + cap = wx.Window_GetCapture() + if cap: + cap.ReleaseMouse() + + dlg = wx.SingleChoiceDialog( + None, + _(""" +An unhandled exception (bug) occured. Bug report saved at : +(%s) + +Please be kind enough to send this file to: +beremiz-devel@lists.sourceforge.net + +You should now restart program. + +Traceback: +""") % bug_report_path + + repr(e_type) + " : " + repr(e_value), + _("Error"), + trcbck_lst) + try: + res = (dlg.ShowModal() == wx.ID_OK) + finally: + dlg.Destroy() + + return res + + +def get_last_traceback(tb): + while tb.tb_next: + tb = tb.tb_next + return tb + + +def format_namespace(d, indent=' '): + return '\n'.join(['%s%s: %s' % (indent, k, repr(v)[:10000]) for k, v in d.iteritems()]) + + +ignored_exceptions = [] # a problem with a line in a module is only reported once per session + + +def AddExceptHook(app_version='[No version]'): + + def save_bug_report(e_type, e_value, e_traceback, bug_report_path, date): + info = { + 'app-title': wx.GetApp().GetAppName(), + 'app-version': app_version, + 'wx-version': wx.VERSION_STRING, + 'wx-platform': wx.Platform, + 'python-version': platform.python_version(), + 'platform': platform.platform(), + 'e-type': e_type, + 'e-value': e_value, + 'date': date, + 'cwd': os.getcwd(), + } + if e_traceback: + info['traceback'] = ''.join(traceback.format_tb(e_traceback)) + '%s: %s' % (e_type, e_value) + last_tb = get_last_traceback(e_traceback) + exception_locals = last_tb.tb_frame.f_locals # the locals at the level of the stack trace where the exception actually occurred + info['locals'] = format_namespace(exception_locals) + if 'self' in exception_locals: + try: + info['self'] = format_namespace(exception_locals['self'].__dict__) + except Exception: + pass + path = os.path.dirname(bug_report_path) + if not os.path.exists(path): + os.mkdir(path) + output = open(bug_report_path, 'w') + lst = info.keys() + lst.sort() + for a in lst: + output.write(a + ":\n" + str(info[a]) + "\n\n") + output.close() + + def handle_exception(e_type, e_value, e_traceback): + traceback.print_exception(e_type, e_value, e_traceback) # this is very helpful when there's an exception in the rest of this func + last_tb = get_last_traceback(e_traceback) + ex = (last_tb.tb_frame.f_code.co_filename, last_tb.tb_frame.f_lineno) + if ex not in ignored_exceptions: + ignored_exceptions.append(ex) + date = time.ctime() + path = tempfile.gettempdir()+os.sep+wx.GetApp().GetAppName() + bug_report_path = path + os.sep + "bug_report_" + time.strftime("%Y_%m_%d__%H-%M-%S") + ".txt" + save_bug_report(e_type, e_value, e_traceback, bug_report_path, date) + Display_Exception_Dialog(e_type, e_value, e_traceback, bug_report_path) + # sys.excepthook = lambda *args: wx.CallAfter(handle_exception, *args) + sys.excepthook = handle_exception + + init_old = threading.Thread.__init__ + + def init(self, *args, **kwargs): + init_old(self, *args, **kwargs) + run_old = self.run + + def run_with_except_hook(*args, **kw): + try: + run_old(*args, **kw) + except (KeyboardInterrupt, SystemExit): + raise + except Exception: + sys.excepthook(*sys.exc_info()) + self.run = run_with_except_hook + threading.Thread.__init__ = init diff -r c1298e7ffe3a -r 8391c11477f4 util/MiniTextControler.py --- a/util/MiniTextControler.py Fri Mar 24 12:07:47 2017 +0000 +++ b/util/MiniTextControler.py Tue Jan 30 16:06:58 2018 +0100 @@ -26,51 +26,54 @@ Minimal tab controller for a simple text editor """ + +from __future__ import absolute_import import os -class MiniTextControler: - + +class MiniTextControler(object): + def __init__(self, filepath, controller): self.FilePath = filepath self.BaseController = controller - + def __del__(self): self.BaseController = None - + def CTNFullName(self): return "" - + def SetEditedElementText(self, tagname, text): file = open(self.FilePath, "w") file.write(text) file.close() - - def GetEditedElementText(self, tagname, debug = False): + + def GetEditedElementText(self, tagname, debug=False): if os.path.isfile(self.FilePath): file = open(self.FilePath, "r") text = file.read() file.close() return text return "" - - def GetEditedElementInterfaceVars(self, tagname, tree=False, debug = False): + + def GetEditedElementInterfaceVars(self, tagname, tree=False, debug=False): return [] - - def GetEditedElementType(self, tagname, debug = False): + + def GetEditedElementType(self, tagname, debug=False): return "program" - - def GetBlockType(self, type, inputs = None, debug = False): + + def GetBlockType(self, type, inputs=None, debug=False): return self.BaseController.GetBlockType(type, inputs, debug) - - def GetBlockTypes(self, tagname = "", debug = False): + + def GetBlockTypes(self, tagname="", debug=False): return self.BaseController.GetBlockTypes(tagname, debug) - - def GetDataTypes(self, tagname = "", basetypes = True, only_locatables = False, debug = False): + + def GetDataTypes(self, tagname="", basetypes=True, only_locatables=False, debug=False): return self.BaseController.GetDataTypes(tagname, basetypes, only_locatables, debug) - - def GetEnumeratedDataValues(self, debug = False): + + def GetEnumeratedDataValues(self, debug=False): return self.BaseController.GetEnumeratedDataValues(debug) - + def StartBuffering(self): pass @@ -79,4 +82,3 @@ def BufferProject(self): pass - diff -r c1298e7ffe3a -r 8391c11477f4 util/ProcessLogger.py --- a/util/ProcessLogger.py Fri Mar 24 12:07:47 2017 +0000 +++ b/util/ProcessLogger.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,11 +22,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import time + +from __future__ import absolute_import +import os +import sys +import subprocess +import ctypes +from threading import Timer, Lock, Thread, Semaphore import wx -import subprocess, ctypes -from threading import Timer, Lock, Thread, Semaphore -import os, sys if os.name == 'posix': from signal import SIGTERM, SIGKILL @@ -48,36 +51,39 @@ def run(self): outchunk = None self.retval = None - while self.retval is None and not self.killed : + while self.retval is None and not self.killed: if self.endcallback: self.retval = self.Proc.poll() else: self.retval = self.Proc.returncode - + outchunk = self.fd.readline() - if self.callback : self.callback(outchunk) - while outchunk != '' and not self.killed : + if self.callback: + self.callback(outchunk) + while outchunk != '' and not self.killed: outchunk = self.fd.readline() - if self.callback : self.callback(outchunk) + if self.callback: + self.callback(outchunk) if self.endcallback: try: err = self.Proc.wait() - except: + except Exception: err = self.retval self.finished = True self.endcallback(self.Proc.pid, err) -class ProcessLogger: - def __init__(self, logger, Command, finish_callback = None, - no_stdout = False, no_stderr = False, no_gui = True, - timeout = None, outlimit = None, errlimit = None, - endlog = None, keyword = None, kill_it = False, cwd = None, - encoding = None): + +class ProcessLogger(object): + def __init__(self, logger, Command, finish_callback=None, + no_stdout=False, no_stderr=False, no_gui=True, + timeout=None, outlimit=None, errlimit=None, + endlog=None, keyword=None, kill_it=False, cwd=None, + encoding=None, output_encoding=None): self.logger = logger if not isinstance(Command, list): self.Command_str = Command self.Command = [] - for i,word in enumerate(Command.replace("'",'"').split('"')): + for i, word in enumerate(Command.replace("'", '"').split('"')): if i % 2 == 0: word = word.strip() if len(word) > 0: @@ -89,6 +95,7 @@ self.Command_str = subprocess.list2cmdline(self.Command) fsencoding = sys.getfilesystemencoding() + self.output_encoding = output_encoding if encoding is None: encoding = fsencoding @@ -108,17 +115,18 @@ self.errdata = [] self.keyword = keyword self.kill_it = kill_it - self.startsem = Semaphore(0) + self.startsem = Semaphore(0) self.finishsem = Semaphore(0) self.endlock = Lock() - popenargs= { - "cwd":os.getcwd() if cwd is None else cwd, - "stdin":subprocess.PIPE, - "stdout":subprocess.PIPE, - "stderr":subprocess.PIPE} - - if no_gui == True and wx.Platform == '__WXMSW__': + popenargs = { + "cwd": os.getcwd() if cwd is None else cwd, + "stdin": subprocess.PIPE, + "stdout": subprocess.PIPE, + "stderr": subprocess.PIPE + } + + if no_gui and wx.Platform == '__WXMSW__': self.startupinfo = subprocess.STARTUPINFO() self.startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW popenargs["startupinfo"] = self.startupinfo @@ -126,37 +134,40 @@ popenargs["shell"] = False if timeout: - self.timeout = Timer(timeout,self.endlog) + self.timeout = Timer(timeout, self.endlog) self.timeout.start() else: self.timeout = None - - self.Proc = subprocess.Popen( self.Command, **popenargs ) + + self.Proc = subprocess.Popen(self.Command, **popenargs) self.outt = outputThread( - self.Proc, - self.Proc.stdout, - self.output, - self.finish) + self.Proc, + self.Proc.stdout, + self.output, + self.finish) self.outt.start() self.errt = outputThread( - self.Proc, - self.Proc.stderr, - self.errors) + self.Proc, + self.Proc.stderr, + self.errors) self.errt.start() self.startsem.release() - - def output(self,v): + def output(self, v): + if v and self.output_encoding: + v = v.decode(self.output_encoding) self.outdata.append(v) self.outlen += 1 if not self.no_stdout: self.logger.write(v) - if (self.keyword and v.find(self.keyword)!=-1) or (self.outlimit and self.outlen > self.outlimit): + if (self.keyword and v.find(self.keyword) != -1) or (self.outlimit and self.outlen > self.outlimit): self.endlog() - def errors(self,v): + def errors(self, v): + if v and self.output_encoding: + v = v.decode(self.output_encoding) self.errdata.append(v) self.errlen += 1 if not self.no_stderr: @@ -164,28 +175,29 @@ if self.errlimit and self.errlen > self.errlimit: self.endlog() - def log_the_end(self,ecode,pid): + def log_the_end(self, ecode, pid): self.logger.write(self.Command_str + "\n") - self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1 = str(ecode), a2 = str(pid))) - - def finish(self, pid,ecode): - # avoid running function before start is finished + self.logger.write_warning(_("exited with status {a1} (pid {a2})\n").format(a1=str(ecode), a2=str(pid))) + + def finish(self, pid, ecode): + # avoid running function before start is finished self.startsem.acquire() + self.startsem.release() if self.timeout: self.timeout.cancel() self.exitcode = ecode if self.exitcode != 0: - self.log_the_end(ecode,pid) + self.log_the_end(ecode, pid) if self.finish_callback is not None: - self.finish_callback(self,ecode,pid) + self.finish_callback(self, ecode, pid) self.errt.join() self.finishsem.release() - def kill(self,gently=True): + def kill(self, gently=True): # avoid running kill before start is finished self.startsem.acquire() self.startsem.release() - + self.outt.killed = True self.errt.killed = True if wx.Platform == '__WXMSW__': @@ -195,12 +207,12 @@ ctypes.windll.kernel32.CloseHandle(handle) else: if gently: - sig=SIGTERM + sig = SIGTERM else: - sig=SIGKILL + sig = SIGKILL try: os.kill(self.Proc.pid, sig) - except: + except Exception: pass self.outt.join() self.errt.join() @@ -208,11 +220,9 @@ def endlog(self): if self.endlock.acquire(False): if not self.outt.finished and self.kill_it: - self.kill() + self.kill() self.finishsem.release() - def spin(self): self.finishsem.acquire() return [self.exitcode, "".join(self.outdata), "".join(self.errdata)] - diff -r c1298e7ffe3a -r 8391c11477f4 util/TranslationCatalogs.py --- a/util/TranslationCatalogs.py Fri Mar 24 12:07:47 2017 +0000 +++ b/util/TranslationCatalogs.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,15 +22,18 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os +from __future__ import absolute_import +import os +import __builtin__ import wx -# Get the default language -langid = wx.LANGUAGE_DEFAULT -# Define locale for wx -locale = wx.Locale(langid) +locale = None + + +__builtin__.__dict__['_'] = wx.GetTranslation + def GetDomain(path): for name in os.listdir(path): @@ -44,9 +47,19 @@ return basename return None + def AddCatalog(locale_dir): if os.path.exists(locale_dir) and os.path.isdir(locale_dir): domain = GetDomain(locale_dir) if domain is not None: + global locale + if locale is None: + # Define locale for wx + locale = wx.Locale(wx.LANGUAGE_DEFAULT) + locale.AddCatalogLookupPathPrefix(locale_dir) locale.AddCatalog(domain) + + +def NoTranslate(x): + return x diff -r c1298e7ffe3a -r 8391c11477f4 util/Zeroconf.py --- a/util/Zeroconf.py Fri Mar 24 12:07:47 2017 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1559 +0,0 @@ -""" Multicast DNS Service Discovery for Python, v0.12 - Copyright (C) 2003, Paul Scott-Murphy - - This module provides a framework for the use of DNS Service Discovery - using IP multicast. It has been tested against the JRendezvous - implementation from StrangeBerry, - and against the mDNSResponder from Mac OS X 10.3.8. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -""" - -"""0.12 update - allow selection of binding interface - typo fix - Thanks A. M. Kuchlingi - removed all use of word 'Rendezvous' - this is an API change""" - -"""0.11 update - correction to comments for addListener method - support for new record types seen from OS X - - IPv6 address - - hostinfo - ignore unknown DNS record types - fixes to name decoding - works alongside other processes using port 5353 (e.g. on Mac OS X) - tested against Mac OS X 10.3.2's mDNSResponder - corrections to removal of list entries for service browser""" - -"""0.10 update - Jonathon Paisley contributed these corrections: - always multicast replies, even when query is unicast - correct a pointer encoding problem - can now write records in any order - traceback shown on failure - better TXT record parsing - server is now separate from name - can cancel a service browser - - modified some unit tests to accommodate these changes""" - -"""0.09 update - remove all records on service unregistration - fix DOS security problem with readName""" - -"""0.08 update - changed licensing to LGPL""" - -"""0.07 update - faster shutdown on engine - pointer encoding of outgoing names - ServiceBrowser now works - new unit tests""" - -"""0.06 update - small improvements with unit tests - added defined exception types - new style objects - fixed hostname/interface problem - fixed socket timeout problem - fixed addServiceListener() typo bug - using select() for socket reads - tested on Debian unstable with Python 2.2.2""" - -"""0.05 update - ensure case insensitivty on domain names - support for unicast DNS queries""" - -"""0.04 update - added some unit tests - added __ne__ adjuncts where required - ensure names end in '.local.' - timeout on receiving socket for clean shutdown""" - -__author__ = "Paul Scott-Murphy" -__email__ = "paul at scott dash murphy dot com" -__version__ = "0.12" - -import string -import time -import struct -import socket -import threading -import select -import traceback - -__all__ = ["Zeroconf", "ServiceInfo", "ServiceBrowser"] - -# hook for threads - -globals()['_GLOBAL_DONE'] = 0 - -# Some timing constants - -_UNREGISTER_TIME = 125 -_CHECK_TIME = 175 -_REGISTER_TIME = 225 -_LISTENER_TIME = 200 -_BROWSER_TIME = 500 - -# Some DNS constants - -_MDNS_ADDR = '224.0.0.251' -_MDNS_PORT = 5353; -_DNS_PORT = 53; -_DNS_TTL = 60 * 60; # one hour default TTL - -_MAX_MSG_TYPICAL = 1460 # unused -_MAX_MSG_ABSOLUTE = 8972 - -_FLAGS_QR_MASK = 0x8000 # query response mask -_FLAGS_QR_QUERY = 0x0000 # query -_FLAGS_QR_RESPONSE = 0x8000 # response - -_FLAGS_AA = 0x0400 # Authorative answer -_FLAGS_TC = 0x0200 # Truncated -_FLAGS_RD = 0x0100 # Recursion desired -_FLAGS_RA = 0x8000 # Recursion available - -_FLAGS_Z = 0x0040 # Zero -_FLAGS_AD = 0x0020 # Authentic data -_FLAGS_CD = 0x0010 # Checking disabled - -_CLASS_IN = 1 -_CLASS_CS = 2 -_CLASS_CH = 3 -_CLASS_HS = 4 -_CLASS_NONE = 254 -_CLASS_ANY = 255 -_CLASS_MASK = 0x7FFF -_CLASS_UNIQUE = 0x8000 - -_TYPE_A = 1 -_TYPE_NS = 2 -_TYPE_MD = 3 -_TYPE_MF = 4 -_TYPE_CNAME = 5 -_TYPE_SOA = 6 -_TYPE_MB = 7 -_TYPE_MG = 8 -_TYPE_MR = 9 -_TYPE_NULL = 10 -_TYPE_WKS = 11 -_TYPE_PTR = 12 -_TYPE_HINFO = 13 -_TYPE_MINFO = 14 -_TYPE_MX = 15 -_TYPE_TXT = 16 -_TYPE_AAAA = 28 -_TYPE_SRV = 33 -_TYPE_ANY = 255 - -# Mapping constants to names - -_CLASSES = { _CLASS_IN : "in", - _CLASS_CS : "cs", - _CLASS_CH : "ch", - _CLASS_HS : "hs", - _CLASS_NONE : "none", - _CLASS_ANY : "any" } - -_TYPES = { _TYPE_A : "a", - _TYPE_NS : "ns", - _TYPE_MD : "md", - _TYPE_MF : "mf", - _TYPE_CNAME : "cname", - _TYPE_SOA : "soa", - _TYPE_MB : "mb", - _TYPE_MG : "mg", - _TYPE_MR : "mr", - _TYPE_NULL : "null", - _TYPE_WKS : "wks", - _TYPE_PTR : "ptr", - _TYPE_HINFO : "hinfo", - _TYPE_MINFO : "minfo", - _TYPE_MX : "mx", - _TYPE_TXT : "txt", - _TYPE_AAAA : "quada", - _TYPE_SRV : "srv", - _TYPE_ANY : "any" } - -# utility functions - -def currentTimeMillis(): - """Current system time in milliseconds""" - return time.time() * 1000 - -# Exceptions - -class NonLocalNameException(Exception): - pass - -class NonUniqueNameException(Exception): - pass - -class NamePartTooLongException(Exception): - pass - -class AbstractMethodException(Exception): - pass - -class BadTypeInNameException(Exception): - pass - -# implementation classes - -class DNSEntry(object): - """A DNS entry""" - - def __init__(self, name, type, clazz): - self.key = string.lower(name) - self.name = name - self.type = type - self.clazz = clazz & _CLASS_MASK - self.unique = (clazz & _CLASS_UNIQUE) != 0 - - def __eq__(self, other): - """Equality test on name, type, and class""" - if isinstance(other, DNSEntry): - return self.name == other.name and self.type == other.type and self.clazz == other.clazz - return 0 - - def __ne__(self, other): - """Non-equality test""" - return not self.__eq__(other) - - def getClazz(self, clazz): - """Class accessor""" - try: - return _CLASSES[clazz] - except: - return "?(%s)" % (clazz) - - def getType(self, type): - """Type accessor""" - try: - return _TYPES[type] - except: - return "?(%s)" % (type) - - def toString(self, hdr, other): - """String representation with additional information""" - result = "%s[%s,%s" % (hdr, self.getType(self.type), self.getClazz(self.clazz)) - if self.unique: - result += "-unique," - else: - result += "," - result += self.name - if other is not None: - result += ",%s]" % (other) - else: - result += "]" - return result - -class DNSQuestion(DNSEntry): - """A DNS question entry""" - - def __init__(self, name, type, clazz): - if not name.endswith(".local."): - raise NonLocalNameException - DNSEntry.__init__(self, name, type, clazz) - - def answeredBy(self, rec): - """Returns true if the question is answered by the record""" - return self.clazz == rec.clazz and (self.type == rec.type or self.type == _TYPE_ANY) and self.name == rec.name - - def __repr__(self): - """String representation""" - return DNSEntry.toString(self, "question", None) - - -class DNSRecord(DNSEntry): - """A DNS record - like a DNS entry, but has a TTL""" - - def __init__(self, name, type, clazz, ttl): - DNSEntry.__init__(self, name, type, clazz) - self.ttl = ttl - self.created = currentTimeMillis() - - def __eq__(self, other): - """Tests equality as per DNSRecord""" - if isinstance(other, DNSRecord): - return DNSEntry.__eq__(self, other) - return 0 - - def suppressedBy(self, msg): - """Returns true if any answer in a message can suffice for the - information held in this record.""" - for record in msg.answers: - if self.suppressedByAnswer(record): - return 1 - return 0 - - def suppressedByAnswer(self, other): - """Returns true if another record has same name, type and class, - and if its TTL is at least half of this record's.""" - if self == other and other.ttl > (self.ttl / 2): - return 1 - return 0 - - def getExpirationTime(self, percent): - """Returns the time at which this record will have expired - by a certain percentage.""" - return self.created + (percent * self.ttl * 10) - - def getRemainingTTL(self, now): - """Returns the remaining TTL in seconds.""" - return max(0, (self.getExpirationTime(100) - now) / 1000) - - def isExpired(self, now): - """Returns true if this record has expired.""" - return self.getExpirationTime(100) <= now - - def isStale(self, now): - """Returns true if this record is at least half way expired.""" - return self.getExpirationTime(50) <= now - - def resetTTL(self, other): - """Sets this record's TTL and created time to that of - another record.""" - self.created = other.created - self.ttl = other.ttl - - def write(self, out): - """Abstract method""" - raise AbstractMethodException - - def toString(self, other): - """String representation with addtional information""" - arg = "%s/%s,%s" % (self.ttl, self.getRemainingTTL(currentTimeMillis()), other) - return DNSEntry.toString(self, "record", arg) - -class DNSAddress(DNSRecord): - """A DNS address record""" - - def __init__(self, name, type, clazz, ttl, address): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.address = address - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeString(self.address, len(self.address)) - - def __eq__(self, other): - """Tests equality on address""" - if isinstance(other, DNSAddress): - return self.address == other.address - return 0 - - def __repr__(self): - """String representation""" - try: - return socket.inet_ntoa(self.address) - except: - return self.address - -class DNSHinfo(DNSRecord): - """A DNS host information record""" - - def __init__(self, name, type, clazz, ttl, cpu, os): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.cpu = cpu - self.os = os - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeString(self.cpu, len(self.cpu)) - out.writeString(self.os, len(self.os)) - - def __eq__(self, other): - """Tests equality on cpu and os""" - if isinstance(other, DNSHinfo): - return self.cpu == other.cpu and self.os == other.os - return 0 - - def __repr__(self): - """String representation""" - return self.cpu + " " + self.os - -class DNSPointer(DNSRecord): - """A DNS pointer record""" - - def __init__(self, name, type, clazz, ttl, alias): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.alias = alias - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeName(self.alias) - - def __eq__(self, other): - """Tests equality on alias""" - if isinstance(other, DNSPointer): - return self.alias == other.alias - return 0 - - def __repr__(self): - """String representation""" - return self.toString(self.alias) - -class DNSText(DNSRecord): - """A DNS text record""" - - def __init__(self, name, type, clazz, ttl, text): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.text = text - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeString(self.text, len(self.text)) - - def __eq__(self, other): - """Tests equality on text""" - if isinstance(other, DNSText): - return self.text == other.text - return 0 - - def __repr__(self): - """String representation""" - if len(self.text) > 10: - return self.toString(self.text[:7] + "...") - else: - return self.toString(self.text) - -class DNSService(DNSRecord): - """A DNS service record""" - - def __init__(self, name, type, clazz, ttl, priority, weight, port, server): - DNSRecord.__init__(self, name, type, clazz, ttl) - self.priority = priority - self.weight = weight - self.port = port - self.server = server - - def write(self, out): - """Used in constructing an outgoing packet""" - out.writeShort(self.priority) - out.writeShort(self.weight) - out.writeShort(self.port) - out.writeName(self.server) - - def __eq__(self, other): - """Tests equality on priority, weight, port and server""" - if isinstance(other, DNSService): - return self.priority == other.priority and self.weight == other.weight and self.port == other.port and self.server == other.server - return 0 - - def __repr__(self): - """String representation""" - return self.toString("%s:%s" % (self.server, self.port)) - -class DNSIncoming(object): - """Object representation of an incoming DNS packet""" - - def __init__(self, data): - """Constructor from string holding bytes of packet""" - self.offset = 0 - self.data = data - self.questions = [] - self.answers = [] - self.numQuestions = 0 - self.numAnswers = 0 - self.numAuthorities = 0 - self.numAdditionals = 0 - - self.readHeader() - self.readQuestions() - self.readOthers() - - def readHeader(self): - """Reads header portion of packet""" - format = '!HHHHHH' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - - self.id = info[0] - self.flags = info[1] - self.numQuestions = info[2] - self.numAnswers = info[3] - self.numAuthorities = info[4] - self.numAdditionals = info[5] - - def readQuestions(self): - """Reads questions section of packet""" - format = '!HH' - length = struct.calcsize(format) - for i in range(0, self.numQuestions): - name = self.readName() - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - - question = DNSQuestion(name, info[0], info[1]) - self.questions.append(question) - - def readInt(self): - """Reads an integer from the packet""" - format = '!I' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - return info[0] - - def readCharacterString(self): - """Reads a character string from the packet""" - length = ord(self.data[self.offset]) - self.offset += 1 - return self.readString(length) - - def readString(self, len): - """Reads a string of a given length from the packet""" - format = '!' + str(len) + 's' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - return info[0] - - def readUnsignedShort(self): - """Reads an unsigned short from the packet""" - format = '!H' - length = struct.calcsize(format) - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - return info[0] - - def readOthers(self): - """Reads the answers, authorities and additionals section of the packet""" - format = '!HHiH' - length = struct.calcsize(format) - n = self.numAnswers + self.numAuthorities + self.numAdditionals - for i in range(0, n): - domain = self.readName() - info = struct.unpack(format, self.data[self.offset:self.offset+length]) - self.offset += length - - rec = None - if info[0] == _TYPE_A: - rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(4)) - elif info[0] == _TYPE_CNAME or info[0] == _TYPE_PTR: - rec = DNSPointer(domain, info[0], info[1], info[2], self.readName()) - elif info[0] == _TYPE_TXT: - rec = DNSText(domain, info[0], info[1], info[2], self.readString(info[3])) - elif info[0] == _TYPE_SRV: - rec = DNSService(domain, info[0], info[1], info[2], self.readUnsignedShort(), self.readUnsignedShort(), self.readUnsignedShort(), self.readName()) - elif info[0] == _TYPE_HINFO: - rec = DNSHinfo(domain, info[0], info[1], info[2], self.readCharacterString(), self.readCharacterString()) - elif info[0] == _TYPE_AAAA: - rec = DNSAddress(domain, info[0], info[1], info[2], self.readString(16)) - else: - # Try to ignore types we don't know about - # this may mean the rest of the name is - # unable to be parsed, and may show errors - # so this is left for debugging. New types - # encountered need to be parsed properly. - # - #print "UNKNOWN TYPE = " + str(info[0]) - #raise BadTypeInNameException - pass - - if rec is not None: - self.answers.append(rec) - - def isQuery(self): - """Returns true if this is a query""" - return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_QUERY - - def isResponse(self): - """Returns true if this is a response""" - return (self.flags & _FLAGS_QR_MASK) == _FLAGS_QR_RESPONSE - - def readUTF(self, offset, len): - """Reads a UTF-8 string of a given length from the packet""" - result = self.data[offset:offset+len].decode('utf-8') - return result - - def readName(self): - """Reads a domain name from the packet""" - result = '' - off = self.offset - next = -1 - first = off - - while 1: - len = ord(self.data[off]) - off += 1 - if len == 0: - break - t = len & 0xC0 - if t == 0x00: - result = ''.join((result, self.readUTF(off, len) + '.')) - off += len - elif t == 0xC0: - if next < 0: - next = off + 1 - off = ((len & 0x3F) << 8) | ord(self.data[off]) - if off >= first: - raise _("Bad domain name (circular) at ") + str(off) - first = off - else: - raise _("Bad domain name at ") + str(off) - - if next >= 0: - self.offset = next - else: - self.offset = off - - return result - - -class DNSOutgoing(object): - """Object representation of an outgoing packet""" - - def __init__(self, flags, multicast = 1): - self.finished = 0 - self.id = 0 - self.multicast = multicast - self.flags = flags - self.names = {} - self.data = [] - self.size = 12 - - self.questions = [] - self.answers = [] - self.authorities = [] - self.additionals = [] - - def addQuestion(self, record): - """Adds a question""" - self.questions.append(record) - - def addAnswer(self, inp, record): - """Adds an answer""" - if not record.suppressedBy(inp): - self.addAnswerAtTime(record, 0) - - def addAnswerAtTime(self, record, now): - """Adds an answer if if does not expire by a certain time""" - if record is not None: - if now == 0 or not record.isExpired(now): - self.answers.append((record, now)) - - def addAuthorativeAnswer(self, record): - """Adds an authoritative answer""" - self.authorities.append(record) - - def addAdditionalAnswer(self, record): - """Adds an additional answer""" - self.additionals.append(record) - - def writeByte(self, value): - """Writes a single byte to the packet""" - format = '!c' - self.data.append(struct.pack(format, chr(value))) - self.size += 1 - - def insertShort(self, index, value): - """Inserts an unsigned short in a certain position in the packet""" - format = '!H' - self.data.insert(index, struct.pack(format, value)) - self.size += 2 - - def writeShort(self, value): - """Writes an unsigned short to the packet""" - format = '!H' - self.data.append(struct.pack(format, value)) - self.size += 2 - - def writeInt(self, value): - """Writes an unsigned integer to the packet""" - format = '!I' - self.data.append(struct.pack(format, int(value))) - self.size += 4 - - def writeString(self, value, length): - """Writes a string to the packet""" - format = '!' + str(length) + 's' - self.data.append(struct.pack(format, value)) - self.size += length - - def writeUTF(self, s): - """Writes a UTF-8 string of a given length to the packet""" - utfstr = s.encode('utf-8') - length = len(utfstr) - if length > 64: - raise NamePartTooLongException - self.writeByte(length) - self.writeString(utfstr, length) - - def writeName(self, name): - """Writes a domain name to the packet""" - - try: - # Find existing instance of this name in packet - # - index = self.names[name] - except KeyError: - # No record of this name already, so write it - # out as normal, recording the location of the name - # for future pointers to it. - # - self.names[name] = self.size - parts = name.split('.') - if parts[-1] == '': - parts = parts[:-1] - for part in parts: - self.writeUTF(part) - self.writeByte(0) - return - - # An index was found, so write a pointer to it - # - self.writeByte((index >> 8) | 0xC0) - self.writeByte(index) - - def writeQuestion(self, question): - """Writes a question to the packet""" - self.writeName(question.name) - self.writeShort(question.type) - self.writeShort(question.clazz) - - def writeRecord(self, record, now): - """Writes a record (answer, authoritative answer, additional) to - the packet""" - self.writeName(record.name) - self.writeShort(record.type) - if record.unique and self.multicast: - self.writeShort(record.clazz | _CLASS_UNIQUE) - else: - self.writeShort(record.clazz) - if now == 0: - self.writeInt(record.ttl) - else: - self.writeInt(record.getRemainingTTL(now)) - index = len(self.data) - # Adjust size for the short we will write before this record - # - self.size += 2 - record.write(self) - self.size -= 2 - - length = len(''.join(self.data[index:])) - self.insertShort(index, length) # Here is the short we adjusted for - - def packet(self): - """Returns a string containing the packet's bytes - - No further parts should be added to the packet once this - is done.""" - if not self.finished: - self.finished = 1 - for question in self.questions: - self.writeQuestion(question) - for answer, time in self.answers: - self.writeRecord(answer, time) - for authority in self.authorities: - self.writeRecord(authority, 0) - for additional in self.additionals: - self.writeRecord(additional, 0) - - self.insertShort(0, len(self.additionals)) - self.insertShort(0, len(self.authorities)) - self.insertShort(0, len(self.answers)) - self.insertShort(0, len(self.questions)) - self.insertShort(0, self.flags) - if self.multicast: - self.insertShort(0, 0) - else: - self.insertShort(0, self.id) - return ''.join(self.data) - - -class DNSCache(object): - """A cache of DNS entries""" - - def __init__(self): - self.cache = {} - - def add(self, entry): - """Adds an entry""" - try: - list = self.cache[entry.key] - except: - list = self.cache[entry.key] = [] - list.append(entry) - - def remove(self, entry): - """Removes an entry""" - try: - list = self.cache[entry.key] - list.remove(entry) - except: - pass - - def get(self, entry): - """Gets an entry by key. Will return None if there is no - matching entry.""" - try: - list = self.cache[entry.key] - return list[list.index(entry)] - except: - return None - - def getByDetails(self, name, type, clazz): - """Gets an entry by details. Will return None if there is - no matching entry.""" - entry = DNSEntry(name, type, clazz) - return self.get(entry) - - def entriesWithName(self, name): - """Returns a list of entries whose key matches the name.""" - try: - return self.cache[name] - except: - return [] - - def entries(self): - """Returns a list of all entries""" - def add(x, y): return x+y - try: - return reduce(add, self.cache.values()) - except: - return [] - - -class Engine(threading.Thread): - """An engine wraps read access to sockets, allowing objects that - need to receive data from sockets to be called back when the - sockets are ready. - - A reader needs a handle_read() method, which is called when the socket - it is interested in is ready for reading. - - Writers are not implemented here, because we only send short - packets. - """ - - def __init__(self, zeroconf): - threading.Thread.__init__(self) - self.zeroconf = zeroconf - self.readers = {} # maps socket to reader - self.timeout = 5 - self.condition = threading.Condition() - self.start() - - def run(self): - while not globals()['_GLOBAL_DONE']: - rs = self.getReaders() - if len(rs) == 0: - # No sockets to manage, but we wait for the timeout - # or addition of a socket - # - self.condition.acquire() - self.condition.wait(self.timeout) - self.condition.release() - else: - try: - rr, wr, er = select.select(rs, [], [], self.timeout) - for socket in rr: - try: - self.readers[socket].handle_read() - except: - # Ignore errors that occur on shutdown - pass - except: - pass - - def getReaders(self): - result = [] - self.condition.acquire() - result = self.readers.keys() - self.condition.release() - return result - - def addReader(self, reader, socket): - self.condition.acquire() - self.readers[socket] = reader - self.condition.notify() - self.condition.release() - - def delReader(self, socket): - self.condition.acquire() - del(self.readers[socket]) - self.condition.notify() - self.condition.release() - - def notify(self): - self.condition.acquire() - self.condition.notify() - self.condition.release() - -class Listener(object): - """A Listener is used by this module to listen on the multicast - group to which DNS messages are sent, allowing the implementation - to cache information as it arrives. - - It requires registration with an Engine object in order to have - the read() method called when a socket is availble for reading.""" - - def __init__(self, zeroconf): - self.zeroconf = zeroconf - self.zeroconf.engine.addReader(self, self.zeroconf.socket) - - def handle_read(self): - data, (addr, port) = self.zeroconf.socket.recvfrom(_MAX_MSG_ABSOLUTE) - self.data = data - msg = DNSIncoming(data) - if msg.isQuery(): - # Always multicast responses - # - if port == _MDNS_PORT: - self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) - # If it's not a multicast query, reply via unicast - # and multicast - # - elif port == _DNS_PORT: - self.zeroconf.handleQuery(msg, addr, port) - self.zeroconf.handleQuery(msg, _MDNS_ADDR, _MDNS_PORT) - else: - self.zeroconf.handleResponse(msg) - - -class Reaper(threading.Thread): - """A Reaper is used by this module to remove cache entries that - have expired.""" - - def __init__(self, zeroconf): - threading.Thread.__init__(self) - self.zeroconf = zeroconf - self.start() - - def run(self): - while 1: - self.zeroconf.wait(10 * 1000) - if globals()['_GLOBAL_DONE']: - return - now = currentTimeMillis() - for record in self.zeroconf.cache.entries(): - if record.isExpired(now): - self.zeroconf.updateRecord(now, record) - self.zeroconf.cache.remove(record) - - -class ServiceBrowser(threading.Thread): - """Used to browse for a service of a specific type. - - The listener object will have its addService() and - removeService() methods called when this browser - discovers changes in the services availability.""" - - def __init__(self, zeroconf, type, listener): - """Creates a browser for a specific type""" - threading.Thread.__init__(self) - self.zeroconf = zeroconf - self.type = type - self.listener = listener - self.services = {} - self.nextTime = currentTimeMillis() - self.delay = _BROWSER_TIME - self.list = [] - - self.done = 0 - - self.zeroconf.addListener(self, DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) - self.start() - - def updateRecord(self, zeroconf, now, record): - """Callback invoked by Zeroconf when new information arrives. - - Updates information required by browser in the Zeroconf cache.""" - if record.type == _TYPE_PTR and record.name == self.type: - expired = record.isExpired(now) - try: - oldrecord = self.services[record.alias.lower()] - if not expired: - oldrecord.resetTTL(record) - else: - del(self.services[record.alias.lower()]) - callback = lambda x: self.listener.removeService(x, self.type, record.alias) - self.list.append(callback) - return - except: - if not expired: - self.services[record.alias.lower()] = record - callback = lambda x: self.listener.addService(x, self.type, record.alias) - self.list.append(callback) - - expires = record.getExpirationTime(75) - if expires < self.nextTime: - self.nextTime = expires - - def cancel(self): - self.done = 1 - self.zeroconf.notifyAll() - - def run(self): - while 1: - event = None - now = currentTimeMillis() - if len(self.list) == 0 and self.nextTime > now: - self.zeroconf.wait(self.nextTime - now) - if globals()['_GLOBAL_DONE'] or self.done: - return - now = currentTimeMillis() - - if self.nextTime <= now: - out = DNSOutgoing(_FLAGS_QR_QUERY) - out.addQuestion(DNSQuestion(self.type, _TYPE_PTR, _CLASS_IN)) - for record in self.services.values(): - if not record.isExpired(now): - out.addAnswerAtTime(record, now) - self.zeroconf.send(out) - self.nextTime = now + self.delay - self.delay = min(20 * 1000, self.delay * 2) - - if len(self.list) > 0: - event = self.list.pop(0) - - if event is not None: - event(self.zeroconf) - - -class ServiceInfo(object): - """Service information""" - - def __init__(self, type, name, address=None, port=None, weight=0, priority=0, properties=None, server=None): - """Create a service description. - - type: fully qualified service type name - name: fully qualified service name - address: IP address as unsigned short, network byte order - port: port that the service runs on - weight: weight of the service - priority: priority of the service - properties: dictionary of properties (or a string holding the bytes for the text field) - server: fully qualified name for service host (defaults to name)""" - - if not name.endswith(type): - raise BadTypeInNameException - self.type = type - self.name = name - self.address = address - self.port = port - self.weight = weight - self.priority = priority - if server: - self.server = server - else: - self.server = name - self.setProperties(properties) - - def setProperties(self, properties): - """Sets properties and text of this info from a dictionary""" - if isinstance(properties, dict): - self.properties = properties - list = [] - result = '' - for key in properties: - value = properties[key] - if value is None: - suffix = ''.encode('utf-8') - elif isinstance(value, str): - suffix = value.encode('utf-8') - elif isinstance(value, int): - if value: - suffix = 'true' - else: - suffix = 'false' - else: - suffix = ''.encode('utf-8') - list.append('='.join((key, suffix))) - for item in list: - result = ''.join((result, struct.pack('!c', chr(len(item))), item)) - self.text = result - else: - self.text = properties - - def setText(self, text): - """Sets properties and text given a text field""" - self.text = text - try: - result = {} - end = len(text) - index = 0 - strs = [] - while index < end: - length = ord(text[index]) - index += 1 - strs.append(text[index:index+length]) - index += length - - for s in strs: - eindex = s.find('=') - if eindex == -1: - # No equals sign at all - key = s - value = 0 - else: - key = s[:eindex] - value = s[eindex+1:] - if value == 'true': - value = 1 - elif value == 'false' or not value: - value = 0 - - # Only update non-existent properties - if key and result.get(key) == None: - result[key] = value - - self.properties = result - except: - traceback.print_exc() - self.properties = None - - def getType(self): - """Type accessor""" - return self.type - - def getName(self): - """Name accessor""" - if self.type is not None and self.name.endswith("." + self.type): - return self.name[:len(self.name) - len(self.type) - 1] - return self.name - - def getAddress(self): - """Address accessor""" - return self.address - - def getPort(self): - """Port accessor""" - return self.port - - def getPriority(self): - """Pirority accessor""" - return self.priority - - def getWeight(self): - """Weight accessor""" - return self.weight - - def getProperties(self): - """Properties accessor""" - return self.properties - - def getText(self): - """Text accessor""" - return self.text - - def getServer(self): - """Server accessor""" - return self.server - - def updateRecord(self, zeroconf, now, record): - """Updates service information from a DNS record""" - if record is not None and not record.isExpired(now): - if record.type == _TYPE_A: - if record.name == self.server: - self.address = record.address - elif record.type == _TYPE_SRV: - if record.name == self.name: - self.server = record.server - self.port = record.port - self.weight = record.weight - self.priority = record.priority - self.updateRecord(zeroconf, now, zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN)) - elif record.type == _TYPE_TXT: - if record.name == self.name: - self.setText(record.text) - - def request(self, zeroconf, timeout): - """Returns true if the service could be discovered on the - network, and updates this object with details discovered. - """ - now = currentTimeMillis() - delay = _LISTENER_TIME - next = now + delay - last = now + timeout - result = 0 - try: - zeroconf.addListener(self, DNSQuestion(self.name, _TYPE_ANY, _CLASS_IN)) - while self.server is None or self.address is None or self.text is None: - if last <= now: - return 0 - if next <= now: - out = DNSOutgoing(_FLAGS_QR_QUERY) - out.addQuestion(DNSQuestion(self.name, _TYPE_SRV, _CLASS_IN)) - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_SRV, _CLASS_IN), now) - out.addQuestion(DNSQuestion(self.name, _TYPE_TXT, _CLASS_IN)) - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.name, _TYPE_TXT, _CLASS_IN), now) - if self.server is not None: - out.addQuestion(DNSQuestion(self.server, _TYPE_A, _CLASS_IN)) - out.addAnswerAtTime(zeroconf.cache.getByDetails(self.server, _TYPE_A, _CLASS_IN), now) - zeroconf.send(out) - next = now + delay - delay = delay * 2 - - zeroconf.wait(min(next, last) - now) - now = currentTimeMillis() - result = 1 - finally: - zeroconf.removeListener(self) - - return result - - def __eq__(self, other): - """Tests equality of service name""" - if isinstance(other, ServiceInfo): - return other.name == self.name - return 0 - - def __ne__(self, other): - """Non-equality test""" - return not self.__eq__(other) - - def __repr__(self): - """String representation""" - result = "service[%s,%s:%s," % (self.name, socket.inet_ntoa(self.getAddress()), self.port) - if self.text is None: - result += "None" - else: - if len(self.text) < 20: - result += self.text - else: - result += self.text[:17] + "..." - result += "]" - return result - - -class Zeroconf(object): - """Implementation of Zeroconf Multicast DNS Service Discovery - - Supports registration, unregistration, queries and browsing. - """ - def __init__(self, bindaddress=None): - """Creates an instance of the Zeroconf class, establishing - multicast communications, listening and reaping threads.""" - globals()['_GLOBAL_DONE'] = 0 - self.intf = bindaddress - self.group = ('', _MDNS_PORT) - self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - try: - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1) - except: - # SO_REUSEADDR should be equivalent to SO_REUSEPORT for - # multicast UDP sockets (p 731, "TCP/IP Illustrated, - # Volume 2"), but some BSD-derived systems require - # SO_REUSEPORT to be specified explicity. Also, not all - # versions of Python have SO_REUSEPORT available. So - # if you're on a BSD-based system, and haven't upgraded - # to Python 2.3 yet, you may find this library doesn't - # work as expected. - # - pass - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_TTL, 255) - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) - try: - self.socket.bind(self.group) - except: - # Some versions of linux raise an exception even though - # the SO_REUSE* options have been set, so ignore it - # - pass - - if self.intf is not None: - self.socket.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(self.intf) + socket.inet_aton('0.0.0.0')) - self.socket.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) - - self.listeners = [] - self.browsers = [] - self.services = {} - - self.cache = DNSCache() - - self.condition = threading.Condition() - - self.engine = Engine(self) - self.listener = Listener(self) - self.reaper = Reaper(self) - - def isLoopback(self): - if self.intf is not None: - return self.intf.startswith("127.0.0.1") - return False - - def isLinklocal(self): - if self.intf is not None: - return self.intf.startswith("169.254.") - return False - - def wait(self, timeout): - """Calling thread waits for a given number of milliseconds or - until notified.""" - self.condition.acquire() - self.condition.wait(timeout/1000) - self.condition.release() - - def notifyAll(self): - """Notifies all waiting threads""" - self.condition.acquire() - self.condition.notifyAll() - self.condition.release() - - def getServiceInfo(self, type, name, timeout=3000): - """Returns network's service information for a particular - name and type, or None if no service matches by the timeout, - which defaults to 3 seconds.""" - info = ServiceInfo(type, name) - if info.request(self, timeout): - return info - return None - - def addServiceListener(self, type, listener): - """Adds a listener for a particular service type. This object - will then have its updateRecord method called when information - arrives for that type.""" - self.removeServiceListener(listener) - self.browsers.append(ServiceBrowser(self, type, listener)) - - def removeServiceListener(self, listener): - """Removes a listener from the set that is currently listening.""" - for browser in self.browsers: - if browser.listener == listener: - browser.cancel() - del(browser) - - def registerService(self, info, ttl=_DNS_TTL): - """Registers service information to the network with a default TTL - of 60 seconds. Zeroconf will then respond to requests for - information for that service. The name of the service may be - changed if needed to make it unique on the network.""" - self.checkService(info) - self.services[info.name.lower()] = info - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, ttl, info.name), 0) - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, ttl, info.priority, info.weight, info.port, info.server), 0) - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, ttl, info.text), 0) - if info.address: - out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, ttl, info.address), 0) - self.send(out) - i += 1 - nextTime += _REGISTER_TIME - - def unregisterService(self, info): - """Unregister a service.""" - try: - del(self.services[info.name.lower()]) - except: - pass - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.name), 0) - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) - if info.address: - out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) - self.send(out) - i += 1 - nextTime += _UNREGISTER_TIME - - def unregisterAllServices(self): - """Unregister all registered services.""" - if len(self.services) > 0: - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - for info in self.services.values(): - out.addAnswerAtTime(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, 0, info.name), 0) - out.addAnswerAtTime(DNSService(info.name, _TYPE_SRV, _CLASS_IN, 0, info.priority, info.weight, info.port, info.server), 0) - out.addAnswerAtTime(DNSText(info.name, _TYPE_TXT, _CLASS_IN, 0, info.text), 0) - if info.address: - out.addAnswerAtTime(DNSAddress(info.server, _TYPE_A, _CLASS_IN, 0, info.address), 0) - self.send(out) - i += 1 - nextTime += _UNREGISTER_TIME - - def checkService(self, info): - """Checks the network for a unique service name, modifying the - ServiceInfo passed in if it is not unique.""" - now = currentTimeMillis() - nextTime = now - i = 0 - while i < 3: - for record in self.cache.entriesWithName(info.type): - if record.type == _TYPE_PTR and not record.isExpired(now) and record.alias == info.name: - if (info.name.find('.') < 0): - info.name = info.name + ".[" + info.address + ":" + info.port + "]." + info.type - self.checkService(info) - return - raise NonUniqueNameException - if now < nextTime: - self.wait(nextTime - now) - now = currentTimeMillis() - continue - out = DNSOutgoing(_FLAGS_QR_QUERY | _FLAGS_AA) - self.debug = out - out.addQuestion(DNSQuestion(info.type, _TYPE_PTR, _CLASS_IN)) - out.addAuthorativeAnswer(DNSPointer(info.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, info.name)) - self.send(out) - i += 1 - nextTime += _CHECK_TIME - - def addListener(self, listener, question): - """Adds a listener for a given question. The listener will have - its updateRecord method called when information is available to - answer the question.""" - now = currentTimeMillis() - self.listeners.append(listener) - if question is not None: - for record in self.cache.entriesWithName(question.name): - if question.answeredBy(record) and not record.isExpired(now): - listener.updateRecord(self, now, record) - self.notifyAll() - - def removeListener(self, listener): - """Removes a listener.""" - try: - self.listeners.remove(listener) - self.notifyAll() - except: - pass - - def updateRecord(self, now, rec): - """Used to notify listeners of new information that has updated - a record.""" - for listener in self.listeners: - listener.updateRecord(self, now, rec) - self.notifyAll() - - def handleResponse(self, msg): - """Deal with incoming response packets. All answers - are held in the cache, and listeners are notified.""" - now = currentTimeMillis() - for record in msg.answers: - expired = record.isExpired(now) - if record in self.cache.entries(): - if expired: - self.cache.remove(record) - else: - entry = self.cache.get(record) - if entry is not None: - entry.resetTTL(record) - record = entry - else: - self.cache.add(record) - - self.updateRecord(now, record) - - def handleQuery(self, msg, addr, port): - """Deal with incoming query packets. Provides a response if - possible.""" - out = None - - # Support unicast client responses - # - if port != _MDNS_PORT: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA, 0) - for question in msg.questions: - out.addQuestion(question) - - for question in msg.questions: - if question.type == _TYPE_PTR: - for service in self.services.values(): - if question.name == service.type: - if out is None: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - out.addAnswer(msg, DNSPointer(service.type, _TYPE_PTR, _CLASS_IN, _DNS_TTL, service.name)) - else: - try: - if out is None: - out = DNSOutgoing(_FLAGS_QR_RESPONSE | _FLAGS_AA) - - # Answer A record queries for any service addresses we know - if question.type == _TYPE_A or question.type == _TYPE_ANY: - for service in self.services.values(): - if service.server == question.name.lower(): - out.addAnswer(msg, DNSAddress(question.name, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) - - service = self.services.get(question.name.lower(), None) - if not service: continue - - if question.type == _TYPE_SRV or question.type == _TYPE_ANY: - out.addAnswer(msg, DNSService(question.name, _TYPE_SRV, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.priority, service.weight, service.port, service.server)) - if question.type == _TYPE_TXT or question.type == _TYPE_ANY: - out.addAnswer(msg, DNSText(question.name, _TYPE_TXT, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.text)) - if question.type == _TYPE_SRV: - out.addAdditionalAnswer(DNSAddress(service.server, _TYPE_A, _CLASS_IN | _CLASS_UNIQUE, _DNS_TTL, service.address)) - except: - traceback.print_exc() - - if out is not None and out.answers: - out.id = msg.id - self.send(out, addr, port) - - def send(self, out, addr = _MDNS_ADDR, port = _MDNS_PORT): - """Sends an outgoing packet.""" - # This is a quick test to see if we can parse the packets we generate - #temp = DNSIncoming(out.packet()) - try: - bytes_sent = self.socket.sendto(out.packet(), 0, (addr, port)) - except: - # Ignore this, it may be a temporary loss of network connection - pass - - def close(self): - """Ends the background threads, and prevent this instance from - servicing further queries.""" - if globals()['_GLOBAL_DONE'] == 0: - globals()['_GLOBAL_DONE'] = 1 - self.notifyAll() - self.engine.notify() - self.unregisterAllServices() - self.socket.setsockopt(socket.SOL_IP, socket.IP_DROP_MEMBERSHIP, socket.inet_aton(_MDNS_ADDR) + socket.inet_aton('0.0.0.0')) - self.socket.close() - -# Test a few module features, including service registration, service -# query (for Zoe), and service unregistration. - -if __name__ == '__main__': - print "Multicast DNS Service Discovery for Python, version", __version__ - r = Zeroconf() - print "1. Testing registration of a service..." - desc = {'version':'0.10','a':'test value', 'b':'another value'} - info = ServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.", socket.inet_aton("127.0.0.1"), 1234, 0, 0, desc) - print " Registering service..." - r.registerService(info) - print " Registration done." - print "2. Testing query of service information..." - print " Getting ZOE service:", str(r.getServiceInfo("_http._tcp.local.", "ZOE._http._tcp.local.")) - print " Query done." - print "3. Testing query of own service..." - print " Getting self:", str(r.getServiceInfo("_http._tcp.local.", "My Service Name._http._tcp.local.")) - print " Query done." - print "4. Testing unregister of service information..." - r.unregisterService(info) - print " Unregister done." - r.close() diff -r c1298e7ffe3a -r 8391c11477f4 util/misc.py --- a/util/misc.py Fri Mar 24 12:07:47 2017 +0000 +++ b/util/misc.py Tue Jan 30 16:06:58 2018 +0100 @@ -26,39 +26,40 @@ Misc definitions """ -import os,sys -# helper func to check path write permission +from __future__ import absolute_import +import os + +from util.BitmapLibrary import AddBitmapFolder +from util.TranslationCatalogs import AddCatalog + + def CheckPathPerm(path): + """ Helper func to check path write permission """ if path is None or not os.path.isdir(path): return False for root, dirs, files in os.walk(path): - for name in files: - if os.access(root, os.W_OK) is not True or os.access(os.path.join(root, name), os.W_OK) is not True: - return False + files = [f for f in files if not f[0] == '.'] + dirs[:] = [d for d in dirs if not d[0] == '.'] + for name in files: + if os.access(root, os.W_OK) is not True or os.access(os.path.join(root, name), os.W_OK) is not True: + return False return True + def GetClassImporter(classpath): - if type(classpath)==str: + if isinstance(classpath, str): def fac(): - mod=__import__(classpath.rsplit('.',1)[0]) + mod = __import__(classpath.rsplit('.', 1)[0]) return reduce(getattr, classpath.split('.')[1:], mod) return fac else: return classpath + def InstallLocalRessources(CWD): - from BitmapLibrary import AddBitmapFolder - from TranslationCatalogs import AddCatalog - import wx - # Beremiz bitmaps AddBitmapFolder(os.path.join(CWD, "images")) # Internationalization AddCatalog(os.path.join(CWD, "locale")) - import gettext - import __builtin__ - - __builtin__.__dict__['_'] = wx.GetTranslation - diff -r c1298e7ffe3a -r 8391c11477f4 util/paths.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/util/paths.py Tue Jan 30 16:06:58 2018 +0100 @@ -0,0 +1,50 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +# This file is part of Beremiz, a Integrated Development Environment for +# programming IEC 61131-3 automates supporting plcopen standard and CanFestival. +# +# Copyright (C) 2017: Andrey Skvortsov +# +# See COPYING file for copyrights details. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +from __future__ import absolute_import +import os +import sys + + +def AbsFile(file): + if isinstance(file, str): + file = unicode(file, sys.getfilesystemencoding()) + return file + + +def AbsDir(file): + file = AbsFile(file) + return os.path.dirname(os.path.realpath(file)) + + +def AbsNeighbourFile(file, *args): + return os.path.join(AbsDir(file), *args) + + +def AbsParentDir(file, level=1): + path = AbsDir(file) + for dummy in range(0, level): + path = os.path.dirname(path) + return path diff -r c1298e7ffe3a -r 8391c11477f4 version.py --- a/version.py Fri Mar 24 12:07:47 2017 +0000 +++ b/version.py Tue Jan 30 16:06:58 2018 +0100 @@ -23,90 +23,159 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import subprocess, os +from __future__ import absolute_import +import subprocess +import os + +import util.paths as paths + + +def GetCommunityHelpMsg(): + return _( + "The best place to ask questions about Beremiz/PLCOpenEditor\n" + "is project's mailing list: beremiz-devel@lists.sourceforge.net\n" + "\n" + "This is the main community support channel.\n" + "For posting it is required to be subscribed to the mailing list.\n" + "\n" + "You can subscribe to the list here:\n" + "https://lists.sourceforge.net/lists/listinfo/beremiz-devel" + ) + def GetAppRevision(): rev = None - app_dir=os.path.dirname(os.path.realpath(__file__)) + app_dir = paths.AbsDir(__file__) try: pipe = subprocess.Popen( ["hg", "id", "-i"], - stdout = subprocess.PIPE, - cwd = app_dir + stdout=subprocess.PIPE, + cwd=app_dir ) rev = pipe.communicate()[0] if pipe.returncode != 0: rev = None - except: + except Exception: pass - + # if this is not mercurial repository # try to read revision from file if rev is None: try: - f = open(os.path.join(app_dir,"revision")) + f = open(os.path.join(app_dir, "revision")) rev = f.readline() - except: + except Exception: pass return rev + def GetAboutDialogInfo(): import wx info = wx.AboutDialogInfo() info.Name = "Beremiz" info.Version = app_version - - info.Copyright = "(C) 2016 Andrey Skvortsov\n" + + info.Copyright = "" + info.Copyright += "(C) 2016-2017 Andrey Skvortsov\n" info.Copyright += "(C) 2008-2015 Eduard Tisserant\n" info.Copyright += "(C) 2008-2015 Laurent Bessard" info.WebSite = ("http://beremiz.org", "beremiz.org") - + info.Description = _("Open Source framework for automation, " - "implemented IEC 61131 IDE with constantly growing set of extensions " - "and flexible PLC runtime.") - - info.Developers = ("Andrey Skvortsov ", - "Sergey Surkov ", - "Edouard Tisserant ", - "Laurent Bessard ") + "implemented IEC 61131 IDE with constantly growing set of extensions " + "and flexible PLC runtime.") + info.Developers = ( + "Andrey Skvortsov ", + "Sergey Surkov ", + "Edouard Tisserant ", + "Laurent Bessard ") - info.License = ('\n This program is free software; you can redistribute it and/or\n' - ' modify it under the terms of the GNU General Public License\n' - ' as published by the Free Software Foundation; either version 2\n' - ' of the License, or (at your option) any later version.\n' - '\n' - ' This program is distributed in the hope that it will be useful,\n' - ' but WITHOUT ANY WARRANTY; without even the implied warranty of\n' - ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n' - ' GNU General Public License below for more details.\n' - '\n' - '\n' - '\n' - '') + info.License = ( + '\n This program is free software; you can redistribute it and/or\n' + ' modify it under the terms of the GNU General Public License\n' + ' as published by the Free Software Foundation; either version 2\n' + ' of the License, or (at your option) any later version.\n' + '\n' + ' This program is distributed in the hope that it will be useful,\n' + ' but WITHOUT ANY WARRANTY; without even the implied warranty of\n' + ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n' + ' GNU General Public License below for more details.\n' + '\n' + '\n' + '\n' + '' + ) # read license file - path=os.path.join(os.path.dirname(os.path.abspath(__file__))) - license_path = os.path.join(path, u"COPYING") - license='' + path = paths.AbsDir(__file__) + license_path = os.path.join(path, "COPYING") if os.path.exists(license_path): with open(license_path) as f: info.License += f.read() info.Icon = wx.Icon(os.path.join(path, "images", "about_brz_logo.png"), wx.BITMAP_TYPE_PNG) - info.Translators = ("Russian\t- Andrey Skvortsov ", - "Korean\t- Reinhard Lee ", - "German\t- Mark Muzenhardt ", - "French\t- Laurent Bessard ") + info.Translators = ( + "Bengali (Bangladesh)", + " Adhir Dutta , 2017", + "", + + "Chinese", + " Frank Guan , 2017", + "", + + "French", + " Fabien Marteau , 2017", + " Laurent Bessard , 2008", + "", + + "German", + " Andrey Skvortsov , 2017", + " Mark Muzenhardt , 2012", + "", + + "Hungarian", + " Gábor Véninger , 2017", + "", + + "Italian", + " Luca Magnabosco , 2017", + " Manuele Conti , 2017", + "", + + "Korean", + " Reinhard Lee , 2012", + "", + + "Portuguese (Portugal)", + " Pedro Coimbra , 2017", + "", + + "Portuguese (Brazil)", + " Thiago Alves , 2017", + "", + + "Russian", + " Andrey Skvortsov , 2017", + "", + + "Slovenian", + " Janez Pregelj , 2017", + "", + + "Spanish", + " Marcial González de Armas , 2017", + " Carlos Guilarte , 2017", + "", + + ) return info -app_version = "1.2" + +app_version = "1.2" rev = GetAppRevision() if rev is not None: app_version = app_version + "-" + rev.rstrip() - - - diff -r c1298e7ffe3a -r 8391c11477f4 wxglade_hmi/__init__.py --- a/wxglade_hmi/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/wxglade_hmi/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,4 +22,5 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -from wxglade_hmi import * +from __future__ import absolute_import +from wxglade_hmi.wxglade_hmi import * diff -r c1298e7ffe3a -r 8391c11477f4 wxglade_hmi/wxglade_hmi.py --- a/wxglade_hmi/wxglade_hmi.py Fri Mar 24 12:07:47 2017 +0000 +++ b/wxglade_hmi/wxglade_hmi.py Tue Jan 30 16:06:58 2018 +0100 @@ -5,6 +5,7 @@ # programming IEC 61131-3 automates supporting plcopen standard and CanFestival. # # Copyright (C) 2007: Edouard TISSERANT and Laurent BESSARD +# Copyright (C) 2017: Andrey Skvortsov # # See COPYING file for copyrights details. # @@ -22,26 +23,35 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import wx -import os, sys, shutil + +from __future__ import absolute_import +import os +import sys +import shutil from xml.dom import minidom +import wx + +import util.paths as paths from py_ext import PythonFileCTNMixin + class WxGladeHMI(PythonFileCTNMixin): ConfNodeMethods = [ - {"bitmap" : "editWXGLADE", - "name" : _("WXGLADE GUI"), - "tooltip" : _("Edit a WxWidgets GUI with WXGlade"), - "method" : "_editWXGLADE"}, + { + "bitmap": "editWXGLADE", + "name": _("WXGLADE GUI"), + "tooltip": _("Edit a WxWidgets GUI with WXGlade"), + "method": "_editWXGLADE" + }, ] def GetIconName(self): return "wxGlade" def ConfNodePath(self): - return os.path.join(os.path.dirname(__file__)) + return paths.AbsDir(__file__) def _getWXGLADEpath(self, project_path=None): if project_path is None: @@ -49,14 +59,28 @@ # define name for wxGlade gui file return os.path.join(project_path, "hmi.wxg") + def GetWxGladePath(self): + path = None + try: + from wxglade import __file__ as fileName + path = os.path.dirname(fileName) + return path + except ImportError: + pass + + defLibDir = "/usr/share/wxglade" + if os.path.isdir(defLibDir): + path = defLibDir + + return path + def launch_wxglade(self, options, wait=False): - from wxglade import __file__ as fileName - path = os.path.dirname(fileName) + path = self.GetWxGladePath() glade = os.path.join(path, 'wxglade.py') if wx.Platform == '__WXMSW__': - glade = "\"%s\""%glade - mode = {False:os.P_NOWAIT, True:os.P_WAIT}[wait] - os.spawnv(mode, sys.executable, ["\"%s\""%sys.executable] + [glade] + options) + glade = "\"%s\"" % glade + mode = {False: os.P_NOWAIT, True: os.P_WAIT}[wait] + os.spawnv(mode, sys.executable, ["\"%s\"" % sys.executable] + [glade] + options) def OnCTNSave(self, from_project_path=None): if from_project_path is not None: @@ -65,46 +89,45 @@ return PythonFileCTNMixin.OnCTNSave(self, from_project_path) def CTNGenerate_C(self, buildpath, locations): - + hmi_frames = [] - - wxgfile_path=self._getWXGLADEpath() + + wxgfile_path = self._getWXGLADEpath() if os.path.exists(wxgfile_path): wxgfile = open(wxgfile_path, 'r') wxgtree = minidom.parse(wxgfile) wxgfile.close() - + for node in wxgtree.childNodes[1].childNodes: if node.nodeType == wxgtree.ELEMENT_NODE: hmi_frames.append({ - "name" : node.getAttribute("name"), - "class" : node.getAttribute("class"), - "handlers" : [ - hnode.firstChild.data for hnode in + "name": node.getAttribute("name"), + "class": node.getAttribute("class"), + "handlers": [ + hnode.firstChild.data for hnode in node.getElementsByTagName("handler")]}) - - hmipyfile_path=os.path.join(self._getBuildPath(), "hmi.py") + + hmipyfile_path = os.path.join(self._getBuildPath(), "hmi.py") if wx.Platform == '__WXMSW__': - wxgfile_path = "\"%s\""%wxgfile_path - wxghmipyfile_path = "\"%s\""%hmipyfile_path + wxgfile_path = "\"%s\"" % wxgfile_path + wxghmipyfile_path = "\"%s\"" % hmipyfile_path else: wxghmipyfile_path = hmipyfile_path self.launch_wxglade(['-o', wxghmipyfile_path, '-g', 'python', wxgfile_path], wait=True) - + hmipyfile = open(hmipyfile_path, 'r') define_hmi = hmipyfile.read().decode('utf-8') hmipyfile.close() - + else: define_hmi = "" - - declare_hmi = "\n".join(["%(name)s = None\n" % x + - "\n".join(["%(class)s.%(h)s = %(h)s"% - dict(x,h=h) for h in x['handlers']]) - for x in hmi_frames]) - global_hmi = ("global %s\n"%",".join( - [x["name"] for x in hmi_frames]) - if len(hmi_frames) > 0 else "") + + declare_hmi = "\n".join(["%(name)s = None\n" % x + + "\n".join(["%(class)s.%(h)s = %(h)s" % + dict(x, h=h) for h in x['handlers']]) + for x in hmi_frames]) + global_hmi = ("global %s\n" % ",".join( + [x["name"] for x in hmi_frames]) if len(hmi_frames) > 0 else "") init_hmi = "\n".join(["""\ def OnCloseFrame(evt): wx.MessageBox(_("Please stop PLC to close")) @@ -114,17 +137,17 @@ %(name)s.Show() """ % x for x in hmi_frames]) cleanup_hmi = "\n".join( - ["if %(name)s is not None: %(name)s.Destroy()" % x - for x in hmi_frames]) - + ["if %(name)s is not None: %(name)s.Destroy()" % x + for x in hmi_frames]) + self.PreSectionsTexts = { - "globals":define_hmi, - "start":global_hmi, - "stop":global_hmi + cleanup_hmi + "globals": define_hmi, + "start": global_hmi, + "stop": global_hmi + cleanup_hmi } self.PostSectionsTexts = { - "globals":declare_hmi, - "start":init_hmi, + "globals": declare_hmi, + "start": init_hmi, } return PythonFileCTNMixin.CTNGenerate_C(self, buildpath, locations) @@ -136,13 +159,13 @@ dialog = wx.MessageDialog(self.GetCTRoot().AppFrame, _("You don't have write permissions.\nOpen wxGlade anyway ?"), _("Open wxGlade"), - wx.YES_NO|wx.ICON_QUESTION) + wx.YES_NO | wx.ICON_QUESTION) open_wxglade = dialog.ShowModal() == wx.ID_YES dialog.Destroy() if open_wxglade: if not os.path.exists(wxg_filename): hmi_name = self.BaseParams.getName() - open(wxg_filename,"w").write(""" + open(wxg_filename, "w").write(""" @@ -155,6 +178,5 @@ """ % {"name": hmi_name, "class": "Class_%s" % hmi_name}) if wx.Platform == '__WXMSW__': - wxg_filename = "\"%s\""%wxg_filename + wxg_filename = "\"%s\"" % wxg_filename self.launch_wxglade([wxg_filename]) - diff -r c1298e7ffe3a -r 8391c11477f4 xmlclass/__init__.py --- a/xmlclass/__init__.py Fri Mar 24 12:07:47 2017 +0000 +++ b/xmlclass/__init__.py Tue Jan 30 16:06:58 2018 +0100 @@ -23,6 +23,13 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # Package initialisation - -from xmlclass import ClassFactory, GenerateParser, DefaultElementClass, GetAttributeValue, time_model, CreateNode, NodeSetAttr, NodeRenameAttr -from xsdschema import XSDClassFactory, GenerateParserFromXSD, GenerateParserFromXSDstring +from __future__ import absolute_import +from .xmlclass import (ClassFactory, + GenerateParser, + DefaultElementClass, + GetAttributeValue, + time_model, + CreateNode, + NodeSetAttr, + NodeRenameAttr) +from .xsdschema import XSDClassFactory, GenerateParserFromXSD, GenerateParserFromXSDstring diff -r c1298e7ffe3a -r 8391c11477f4 xmlclass/xmlclass.py --- a/xmlclass/xmlclass.py Fri Mar 24 12:07:47 2017 +0000 +++ b/xmlclass/xmlclass.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,16 +22,21 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, sys + +from __future__ import absolute_import +from __future__ import print_function +import os import re import datetime from types import * from xml.dom import minidom -from xml.sax.saxutils import escape, unescape, quoteattr -from lxml import etree +from xml.sax.saxutils import unescape from new import classobj from collections import OrderedDict +from lxml import etree + + def CreateNode(name): node = minidom.Node() node.nodeName = name @@ -39,9 +44,11 @@ node.childNodes = [] return node + def NodeRenameAttr(node, old_name, new_name): node._attrs[new_name] = node._attrs.pop(old_name) + def NodeSetAttr(node, name, value): attr = minidom.Attr(name) text = minidom.Text() @@ -49,10 +56,10 @@ attr.childNodes[0] = text node._attrs[name] = attr -""" -Regular expression models for checking all kind of string values defined in XML -standard -""" + +# Regular expression models for checking all kind of +# string values defined in XML standard + Name_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*)$') Names_model = re.compile('([a-zA-Z_\:][\w\.\-\:]*(?: [a-zA-Z_\:][\w\.\-\:]*)*)$') NMToken_model = re.compile('([\w\.\-\:]*)$') @@ -72,16 +79,17 @@ date_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') datetime_model = re.compile('([0-9]{4})-([0-9]{2})-([0-9]{2})[ T]([0-9]{2}):([0-9]{2}):([0-9]{2}(?:\.[0-9]*)?)((?:[\-\+][0-9]{2}:[0-9]{2})|Z)?$') + class xml_timezone(datetime.tzinfo): def SetOffset(self, offset): if offset == "Z": - self.__offset = timedelta(minutes = 0) + self.__offset = datetime.timedelta(minutes=0) self.__name = "UTC" else: - sign = {"-" : -1, "+" : 1}[offset[0]] + sign = {"-": -1, "+": 1}[offset[0]] hours, minutes = [int(val) for val in offset[1:].split(":")] - self.__offset = timedelta(minutes=sign * (hours * 60 + minutes)) + self.__offset = datetime.timedelta(minutes=sign * (hours * 60 + minutes)) self.__name = "" def utcoffset(self, dt): @@ -91,12 +99,15 @@ return self.__name def dst(self, dt): - return ZERO - -[SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, - ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT, + return datetime.timedelta(0) + + +[ + SYNTAXELEMENT, SYNTAXATTRIBUTE, SIMPLETYPE, COMPLEXTYPE, COMPILEDCOMPLEXTYPE, + ATTRIBUTESGROUP, ELEMENTSGROUP, ATTRIBUTE, ELEMENT, CHOICE, ANY, TAG, CONSTRAINT, ] = range(13) + def NotSupportedYet(type): """ Function that generates a function that point out to user that datatype @@ -105,14 +116,14 @@ @return: function generated """ def GetUnknownValue(attr): - raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % \ - type) + raise ValueError("\"%s\" type isn't supported by \"xmlclass\" yet!" % type) return GetUnknownValue -""" -This function calculates the number of whitespace for indentation -""" + def getIndent(indent, balise): + """ + This function calculates the number of whitespace for indentation + """ first = indent * 2 second = first + len(balise) + 1 return u'\t'.expandtabs(first), u'\t'.expandtabs(second) @@ -140,7 +151,7 @@ def GetNormalizedString(attr, extract=True): """ - Function that normalizes a string according to XML 1.0. Replace + Function that normalizes a string according to XML 1.0. Replace tabulations, line feed and carriage return by white space @param attr: tree node containing data to extract or data to normalize @param extract: attr is a tree node or not @@ -155,14 +166,14 @@ def GetToken(attr, extract=True): """ - Function that tokenizes a string according to XML 1.0. Remove any leading - and trailing white space and replace internal sequence of two or more + Function that tokenizes a string according to XML 1.0. Remove any leading + and trailing white space and replace internal sequence of two or more spaces by only one white space @param attr: tree node containing data to extract or data to tokenize @param extract: attr is a tree node or not @return: data tokenized as string """ - return " ".join([part for part in + return " ".join([part for part in GetNormalizedString(attr, extract).split(" ") if part]) @@ -182,11 +193,11 @@ raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) try: return int(value, 16) - except: + except Exception: raise ValueError("\"%s\" isn't a valid hexadecimal integer!" % value) -def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, +def GenerateIntegerExtraction(minInclusive=None, maxInclusive=None, minExclusive=None, maxExclusive=None): """ Function that generates an extraction function for integer defining min and @@ -212,31 +223,33 @@ try: # TODO: permit to write value like 1E2 value = int(value) - except: + except Exception: raise ValueError("\"%s\" isn't a valid integer!" % value) if minInclusive is not None and value < minInclusive: - raise ValueError("\"%d\" isn't greater or equal to %d!" % \ + raise ValueError("\"%d\" isn't greater or equal to %d!" % (value, minInclusive)) if maxInclusive is not None and value > maxInclusive: - raise ValueError("\"%d\" isn't lesser or equal to %d!" % \ + raise ValueError("\"%d\" isn't lesser or equal to %d!" % (value, maxInclusive)) if minExclusive is not None and value <= minExclusive: - raise ValueError("\"%d\" isn't greater than %d!" % \ + raise ValueError("\"%d\" isn't greater than %d!" % (value, minExclusive)) if maxExclusive is not None and value >= maxExclusive: - raise ValueError("\"%d\" isn't lesser than %d!" % \ + raise ValueError("\"%d\" isn't lesser than %d!" % (value, maxExclusive)) return value return GetInteger -def GenerateFloatExtraction(type, extra_values=[]): +def GenerateFloatExtraction(type, extra_values=None): """ Function that generates an extraction function for float @param type: name of the type of float @return: function generated """ - def GetFloat(attr, extract = True): + extra_values = [] if extra_values is None else extra_values + + def GetFloat(attr, extract=True): """ Function that extracts a float from a tree node or a string @param attr: tree node containing data to extract or data as a string @@ -251,7 +264,7 @@ return value try: return float(value) - except: + except Exception: raise ValueError("\"%s\" isn't a valid %s!" % (value, type)) return GetFloat @@ -401,7 +414,7 @@ raise ValueError("Member limit can't be defined to \"unbounded\"!") try: limit = int(value) - except: + except Exception: raise ValueError("\"%s\" isn't a valid value for this member limit!" % value) if limit < 0: raise ValueError("Member limit can't be negative!") @@ -435,8 +448,8 @@ if value in list: return value else: - raise ValueError("\"%s\" isn't a valid value for %s!" % \ - (value, type)) + raise ValueError( + "\"%s\" isn't a valid value for %s!" % (value, type)) return GetEnumerated @@ -497,8 +510,8 @@ if item in list: values.append(item) else: - raise ValueError("\"%s\" isn't a valid value for %s!" % \ - (value, type)) + raise ValueError( + "\"%s\" isn't a valid value for %s!" % (value, type)) return values return GetLists @@ -517,7 +530,7 @@ check that all extracted items match the model @param attr: tree node containing data to extract or data as a string @param extract: attr is a tree node or not - @return: data as a list of string if matching + @return: data as a list of string if matching """ if extract: value = GetAttributeValue(attr) @@ -529,24 +542,24 @@ if result is not None: values.append(item) else: - raise ValueError("\"%s\" isn't a valid value for %s!" % \ - (value, type)) + raise ValueError("\"%s\" isn't a valid value for %s!" % (value, type)) return values return GetModelNameList + def GenerateAnyInfos(infos): - + def GetTextElement(tree): if infos["namespace"][0] == "##any": return tree.xpath("p")[0] return tree.xpath("ns:p", namespaces={"ns": infos["namespace"][0]})[0] - + def ExtractAny(tree): return GetTextElement(tree).text - + def GenerateAny(tree, value): GetTextElement(tree).text = etree.CDATA(value) - + def InitialAny(): if infos["namespace"][0] == "##any": element_name = "p" @@ -555,15 +568,16 @@ p = etree.Element(element_name) p.text = etree.CDATA("") return p - + return { - "type": COMPLEXTYPE, + "type": COMPLEXTYPE, "extract": ExtractAny, "generate": GenerateAny, "initial": InitialAny, "check": lambda x: isinstance(x, (StringType, UnicodeType, etree.ElementBase)) } + def GenerateTagInfos(infos): def ExtractTag(tree): if len(tree._attrs) > 0: @@ -574,28 +588,30 @@ return True else: return None - + def GenerateTag(value, name=None, indent=0): if name is not None and not (infos["minOccurs"] == 0 and value is None): - ind1, ind2 = getIndent(indent, name) + ind1, _ind2 = getIndent(indent, name) return ind1 + "<%s/>\n" % name else: return "" - + return { - "type": TAG, + "type": TAG, "extract": ExtractTag, "generate": GenerateTag, "initial": lambda: None, - "check": lambda x: x == None or infos["minOccurs"] == 0 and value == True + "check": lambda x: x is None or infos["minOccurs"] == 0 and x } + def FindTypeInfos(factory, infos): if isinstance(infos, (UnicodeType, StringType)): namespace, name = DecomposeQualifiedName(infos) return factory.GetQualifiedNameInfos(name, namespace) return infos - + + def GetElementInitialValue(factory, infos): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) if infos["minOccurs"] == 1: @@ -612,23 +628,25 @@ DefaultElementClass.__setattr__(value, "tag", element_name) value._init_() return value - return [initial_value() for i in xrange(infos["minOccurs"])] + return [initial_value() for dummy in xrange(infos["minOccurs"])] else: return [] + def GetContentInfos(name, choices): for choice_infos in choices: - if choices_infos["type"] == "sequence": - for element_infos in choices_infos["elements"]: + if choice_infos["type"] == "sequence": + for element_infos in choice_infos["elements"]: if element_infos["type"] == CHOICE: if GetContentInfos(name, element_infos["choices"]): - return choices_infos + return choice_infos elif element_infos["name"] == name: - return choices_infos + return choice_infos elif choice_infos["name"] == name: - return choices_infos + return choice_infos return None + def ComputeContentChoices(factory, name, infos): choices = [] for choice in infos["choices"]: @@ -649,6 +667,7 @@ choices.append((choice["name"], choice)) return choices + def GenerateContentInfos(factory, name, choices): choices_dict = {} for choice_name, infos in choices: @@ -656,60 +675,60 @@ for element in infos["elements"]: if element["type"] == CHOICE: element["elmt_type"] = GenerateContentInfos(factory, name, ComputeContentChoices(factory, name, element)) - elif choices_dict.has_key(element["name"]): + elif element["name"] in choices_dict: raise ValueError("'%s' element defined two times in choice" % choice_name) else: choices_dict[element["name"]] = infos else: - if choices_dict.has_key(choice_name): + if choice_name in choices_dict: raise ValueError("'%s' element defined two times in choice" % choice_name) choices_dict[choice_name] = infos prefix = ("%s:" % factory.TargetNamespace if factory.TargetNamespace is not None else "") choices_xpath = "|".join(map(lambda x: prefix + x, choices_dict.keys())) - + def GetContentInitial(): content_name, infos = choices[0] if content_name == "sequence": content_value = [] - for i in xrange(infos["minOccurs"]): + for dummy in xrange(infos["minOccurs"]): for element_infos in infos["elements"]: content_value.extend(GetElementInitialValue(factory, element_infos)) else: content_value = GetElementInitialValue(factory, infos) return content_value - + return { "type": COMPLEXTYPE, "choices_xpath": etree.XPath(choices_xpath, namespaces=factory.NSMAP), "initial": GetContentInitial, } -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Structure extraction functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- def DecomposeQualifiedName(name): result = QName_model.match(name) if not result: - raise ValueError("\"%s\" isn't a valid QName value!" % name) + raise ValueError("\"%s\" isn't a valid QName value!" % name) parts = result.groups()[0].split(':') if len(parts) == 1: return None, parts[0] return parts - -def GenerateElement(element_name, attributes, elements_model, + + +def GenerateElement(element_name, attributes, elements_model, accept_text=False): def ExtractElement(factory, node): attrs = factory.ExtractNodeAttrs(element_name, node, attributes) children_structure = "" - children_infos = [] children = [] for child in node.childNodes: if child.nodeName not in ["#comment", "#text"]: namespace, childname = DecomposeQualifiedName(child.nodeName) - children_structure += "%s "%childname + children_structure += "%s " % childname result = elements_model.match(children_structure) if not result: raise ValueError("Invalid structure for \"%s\" children!. First element invalid." % node.nodeName) @@ -725,8 +744,8 @@ namespace, childname = DecomposeQualifiedName(child.nodeName) infos = factory.GetQualifiedNameInfos(childname, namespace) if infos["type"] != SYNTAXELEMENT: - raise ValueError("\"%s\" can't be a member child!" % name) - if infos["extract"].has_key(element_name): + raise ValueError("\"%s\" can't be a member child!" % childname) + if element_name in infos["extract"]: children.append(infos["extract"][element_name](factory, child)) else: children.append(infos["extract"]["default"](factory, child)) @@ -734,10 +753,10 @@ return ExtractElement -""" -Class that generate class from an XML Tree -""" -class ClassFactory: +class ClassFactory(object): + """ + Class that generate class from an XML Tree + """ def __init__(self, document, filepath=None, debug=False): self.Document = document @@ -746,20 +765,20 @@ else: self.BaseFolder = self.FileName = None self.Debug = debug - + # Dictionary for stocking Classes and Types definitions created from # the XML tree self.XMLClassDefinitions = {} - + self.DefinedNamespaces = {} self.NSMAP = {} self.Namespaces = {} self.SchemaNamespace = None self.TargetNamespace = None self.etreeNamespaceFormat = "%s" - + self.CurrentCompilations = [] - + # Dictionaries for stocking Classes and Types generated self.ComputeAfter = [] if self.FileName is not None: @@ -773,36 +792,36 @@ def GetQualifiedNameInfos(self, name, namespace=None, canbenone=False): if namespace is None: - if self.Namespaces[self.SchemaNamespace].has_key(name): + if name in self.Namespaces[self.SchemaNamespace]: return self.Namespaces[self.SchemaNamespace][name] for space, elements in self.Namespaces.iteritems(): - if space != self.SchemaNamespace and elements.has_key(name): + if space != self.SchemaNamespace and name in elements: return elements[name] parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: return element if not canbenone: raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) - elif self.Namespaces.has_key(namespace): - if self.Namespaces[namespace].has_key(name): + elif namespace in self.Namespaces: + if name in self.Namespaces[namespace]: return self.Namespaces[namespace][name] parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: @@ -815,36 +834,36 @@ def SplitQualifiedName(self, name, namespace=None, canbenone=False): if namespace is None: - if self.Namespaces[self.SchemaNamespace].has_key(name): + if name in self.Namespaces[self.SchemaNamespace]: return name, None for space, elements in self.Namespaces.items(): - if space != self.SchemaNamespace and elements.has_key(name): + if space != self.SchemaNamespace and name in elements: return name, None parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: - return part[1], part[0] + return parts[1], parts[0] if not canbenone: raise ValueError("Unknown element \"%s\" for any defined namespaces!" % name) - elif self.Namespaces.has_key(namespace): - if self.Namespaces[namespace].has_key(name): + elif namespace in self.Namespaces: + if name in self.Namespaces[namespace]: return name, None parts = name.split("_", 1) if len(parts) > 1: group = self.GetQualifiedNameInfos(parts[0], namespace) if group is not None and group["type"] == ELEMENTSGROUP: elements = [] - if group.has_key("elements"): + if "elements" in group: elements = group["elements"] - elif group.has_key("choices"): + elif "choices" in group: elements = group["choices"] for element in elements: if element["name"] == parts[1]: @@ -858,7 +877,7 @@ def ExtractNodeAttrs(self, element_name, node, valid_attrs): attrs = {} for qualified_name, attr in node._attrs.items(): - namespace, name = DecomposeQualifiedName(qualified_name) + namespace, name = DecomposeQualifiedName(qualified_name) if name in valid_attrs: infos = self.GetQualifiedNameInfos(name, namespace) if infos["type"] != SYNTAXATTRIBUTE: @@ -878,9 +897,9 @@ raise ValueError("Invalid attribute \"%s\" for member \"%s\"!" % (qualified_name, node.nodeName)) for attr in valid_attrs: if attr not in attrs and \ - self.Namespaces[self.SchemaNamespace].has_key(attr) and \ - self.Namespaces[self.SchemaNamespace][attr].has_key("default"): - if self.Namespaces[self.SchemaNamespace][attr]["default"].has_key(element_name): + attr in self.Namespaces[self.SchemaNamespace] and \ + "default" in self.Namespaces[self.SchemaNamespace][attr]: + if element_name in self.Namespaces[self.SchemaNamespace][attr]["default"]: default = self.Namespaces[self.SchemaNamespace][attr]["default"][element_name] else: default = self.Namespaces[self.SchemaNamespace][attr]["default"]["default"] @@ -892,7 +911,7 @@ result = [] for child_infos in elements: if child_infos is not None: - if child_infos[1].has_key("name") and schema: + if "name" in child_infos[1] and schema: self.CurrentCompilations.append(child_infos[1]["name"]) namespace, name = DecomposeQualifiedName(child_infos[0]) infos = self.GetQualifiedNameInfos(name, namespace) @@ -901,7 +920,7 @@ element = infos["reduce"](self, child_infos[1], child_infos[2]) if element is not None: result.append(element) - if child_infos[1].has_key("name") and schema: + if "name" in child_infos[1] and schema: self.CurrentCompilations.pop(-1) annotations = [] children = [] @@ -913,22 +932,22 @@ return annotations, children def AddComplexType(self, typename, infos): - if not self.XMLClassDefinitions.has_key(typename): + if typename not in self.XMLClassDefinitions: self.XMLClassDefinitions[typename] = infos else: raise ValueError("\"%s\" class already defined. Choose another name!" % typename) def ParseSchema(self): pass - + def AddEquivalentClass(self, name, base): if name != base: equivalences = self.EquivalentClassesParent.setdefault(self.etreeNamespaceFormat % base, {}) equivalences[self.etreeNamespaceFormat % name] = True - + def AddDistinctionBetweenParentsInLookupClass( - self, lookup_classes, parent, typeinfos): - parent = (self.etreeNamespaceFormat % parent + self, lookup_classes, parent, typeinfos): + parent = (self.etreeNamespaceFormat % parent if parent is not None else None) parent_class = lookup_classes.get(parent) if parent_class is not None: @@ -939,7 +958,7 @@ lookup_classes[parent] = [typeinfos, parent_class] else: lookup_classes[parent] = typeinfos - + def AddToLookupClass(self, name, parent, typeinfos): lookup_name = self.etreeNamespaceFormat % name if isinstance(typeinfos, (StringType, UnicodeType)): @@ -958,7 +977,7 @@ self.AddDistinctionBetweenParentsInLookupClass( lookup_classes, parent, typeinfos) self.ComputedClassesLookUp[lookup_name] = lookup_classes - + def ExtractTypeInfos(self, name, parent, typeinfos): if isinstance(typeinfos, (StringType, UnicodeType)): namespace, type_name = DecomposeQualifiedName(typeinfos) @@ -986,16 +1005,16 @@ return self.CreateClass(name, parent, typeinfos) elif typeinfos["type"] == SIMPLETYPE: return typeinfos - + def GetEquivalentParents(self, parent): return reduce(lambda x, y: x + y, - [[p] + self.GetEquivalentParents(p) - for p in self.EquivalentClassesParent.get(parent, {}).keys()], []) - - """ - Methods that generates the classes - """ + [[p] + self.GetEquivalentParents(p) + for p in self.EquivalentClassesParent.get(parent, {}).keys()], []) + def CreateClasses(self): + """ + Method that generates the classes + """ self.ParseSchema() for name, infos in self.Namespaces[self.TargetNamespace].items(): if infos["type"] == ELEMENT: @@ -1015,9 +1034,9 @@ self.Namespaces[self.TargetNamespace][result["name"]] = result elif infos["type"] == ELEMENTSGROUP: elements = [] - if infos.has_key("elements"): + if "elements" in infos: elements = infos["elements"] - elif infos.has_key("choices"): + elif "choices" in infos: elements = infos["choices"] for element in elements: if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and \ @@ -1028,7 +1047,7 @@ if result is not None and \ not isinstance(result, (UnicodeType, StringType)): self.Namespaces[self.TargetNamespace][result["name"]] = result - + for name, parents in self.ComputedClassesLookUp.iteritems(): if isinstance(parents, DictType): computed_classes = parents.items() @@ -1042,19 +1061,19 @@ parents = dict(computed_classes) self.ComputedClassesLookUp[name] = parents parents[equivalent_parent] = computed_class - + return self.ComputedClasses - def CreateClass(self, name, parent, classinfos, baseclass = False): + def CreateClass(self, name, parent, classinfos, baseclass=False): if parent is not None: classname = "%s_%s" % (parent, name) else: classname = name - + # Checks that classe haven't been generated yet if self.AlreadyComputed.get(classname, False): return self.ComputedClassesInfos.get(classname, None) - + # If base classes haven't been generated bases = [] base_infos = classinfos.get("base", None) @@ -1088,29 +1107,29 @@ bases.append(DefaultElementClass) bases = tuple(bases) classmembers = {"__doc__": classinfos.get("doc", ""), "IsBaseClass": baseclass} - + self.AlreadyComputed[classname] = True - + for attribute in classinfos["attributes"]: infos = self.ExtractTypeInfos(attribute["name"], name, attribute["attr_type"]) - if infos is not None: + if infos is not None: if infos["type"] != SIMPLETYPE: raise ValueError("\"%s\" type is not a simple type!" % attribute["attr_type"]) attrname = attribute["name"] if attribute["use"] == "optional": - classmembers["add%s"%attrname] = generateAddMethod(attrname, self, attribute) - classmembers["delete%s"%attrname] = generateDeleteMethod(attrname) - classmembers["set%s"%attrname] = generateSetMethod(attrname) - classmembers["get%s"%attrname] = generateGetMethod(attrname) + classmembers["add%s" % attrname] = generateAddMethod(attrname, self, attribute) + classmembers["delete%s" % attrname] = generateDeleteMethod(attrname) + classmembers["set%s" % attrname] = generateSetMethod(attrname) + classmembers["get%s" % attrname] = generateGetMethod(attrname) else: raise ValueError("\"%s\" type unrecognized!" % attribute["attr_type"]) attribute["attr_type"] = infos - + for element in classinfos["elements"]: if element["type"] == CHOICE: elmtname = element["name"] choices = ComputeContentChoices(self, name, element) - classmembers["get%schoices"%elmtname] = generateGetChoicesMethod(element["choices"]) + classmembers["get%schoices" % elmtname] = generateGetChoicesMethod(element["choices"]) if element["maxOccurs"] == "unbounded" or element["maxOccurs"] > 1: classmembers["append%sbytype" % elmtname] = generateAppendChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) classmembers["insert%sbytype" % elmtname] = generateInsertChoiceByTypeMethod(element["maxOccurs"], self, element["choices"]) @@ -1141,60 +1160,62 @@ classmembers["delete%s" % elmtname] = generateDeleteMethod(elmtname) classmembers["set%s" % elmtname] = generateSetMethod(elmtname) classmembers["get%s" % elmtname] = generateGetMethod(elmtname) - + classmembers["_init_"] = generateInitMethod(self, classinfos) classmembers["StructurePattern"] = GetStructurePattern(classinfos) classmembers["getElementAttributes"] = generateGetElementAttributes(self, classinfos) classmembers["getElementInfos"] = generateGetElementInfos(self, classinfos) classmembers["setElementValue"] = generateSetElementValue(self, classinfos) - + class_definition = classobj(str(name), bases, classmembers) setattr(class_definition, "__getattr__", generateGetattrMethod(self, class_definition, classinfos)) setattr(class_definition, "__setattr__", generateSetattrMethod(self, class_definition, classinfos)) - class_infos = {"type": COMPILEDCOMPLEXTYPE, - "name": classname, - "initial": generateClassCreateFunction(class_definition), + class_infos = { + "type": COMPILEDCOMPLEXTYPE, + "name": classname, + "initial": generateClassCreateFunction(class_definition), } - + if self.FileName is not None: self.ComputedClasses[self.FileName][classname] = class_definition else: self.ComputedClasses[classname] = class_definition self.ComputedClassesInfos[classname] = class_infos - + self.AddToLookupClass(name, parent, class_definition) self.AddToLookupClass(classname, None, class_definition) - + return class_infos - """ - Methods that print the classes generated - """ def PrintClasses(self): + """ + Method that print the classes generated + """ items = self.ComputedClasses.items() items.sort() if self.FileName is not None: for filename, classes in items: - print "File '%s':" % filename + print("File '%s':" % filename) class_items = classes.items() class_items.sort() for classname, xmlclass in class_items: - print "%s: %s" % (classname, str(xmlclass)) + print("%s: %s" % (classname, str(xmlclass))) else: for classname, xmlclass in items: - print "%s: %s" % (classname, str(xmlclass)) - + print("%s: %s" % (classname, str(xmlclass))) + def PrintClassNames(self): classnames = self.XMLClassDefinitions.keys() classnames.sort() for classname in classnames: - print classname - -""" -Method that generate the method for generating the xml tree structure model by -following the attributes list defined -""" + print(classname) + + def ComputeMultiplicity(name, infos): + """ + Method that generate the method for generating the xml tree structure model by + following the attributes list defined + """ if infos["minOccurs"] == 0: if infos["maxOccurs"] == "unbounded": return "(?:%s)*" % name @@ -1211,15 +1232,17 @@ return "(?:%s){1,%d}" % (name, infos["maxOccurs"]) else: if infos["maxOccurs"] == "unbounded": - return "(?:%s){%d,}" % (name, infos["minOccurs"], name) - else: - return "(?:%s){%d,%d}" % (name, infos["minOccurs"], - infos["maxOccurs"]) + return "(?:%s){%d,}" % (name, infos["minOccurs"]) + else: + return "(?:%s){%d,%d}" % (name, + infos["minOccurs"], + infos["maxOccurs"]) + def GetStructurePattern(classinfos): base_structure_pattern = ( classinfos["base"].StructurePattern.pattern[:-1] - if classinfos.has_key("base") else "") + if "base" in classinfos else "") elements = [] for element in classinfos["elements"]: if element["type"] == ANY: @@ -1244,33 +1267,34 @@ else: raise ValueError("XSD structure not yet supported!") -""" -Method that generate the method for creating a class instance -""" + def generateClassCreateFunction(class_definition): + """ + Method that generate the method for creating a class instance + """ def classCreatefunction(): return class_definition() return classCreatefunction + def generateGetattrMethod(factory, class_definition, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) - optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"]) elements = dict([(element["name"], element) for element in classinfos["elements"]]) - + def getattrMethod(self, name): - if attributes.has_key(name): + if name in attributes: attribute_infos = attributes[name] attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"]) value = self.get(name) if value is not None: return attribute_infos["attr_type"]["extract"](value, extract=False) - elif attribute_infos.has_key("fixed"): + elif "fixed" in attribute_infos: return attribute_infos["attr_type"]["extract"](attribute_infos["fixed"], extract=False) - elif attribute_infos.has_key("default"): + elif "default" in attribute_infos: return attribute_infos["attr_type"]["extract"](attribute_infos["default"], extract=False) return None - - elif elements.has_key(name): + + elif name in elements: element_infos = elements[name] element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"]) if element_infos["type"] == CHOICE: @@ -1279,7 +1303,7 @@ return content elif len(content) > 0: return content[0] - return None + return None elif element_infos["type"] == ANY: return element_infos["elmt_type"]["extract"](self) elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE: @@ -1290,29 +1314,30 @@ values = self.findall(element_name) if element_infos["elmt_type"]["type"] == SIMPLETYPE: return map(lambda value: - element_infos["elmt_type"]["extract"](value.text, extract=False), - values) + element_infos["elmt_type"]["extract"](value.text, extract=False), + values) return values else: value = self.find(element_name) if element_infos["elmt_type"]["type"] == SIMPLETYPE: return element_infos["elmt_type"]["extract"](value.text, extract=False) return value - - elif classinfos.has_key("base"): + + elif "base" in classinfos: return classinfos["base"].__getattr__(self, name) - + return DefaultElementClass.__getattribute__(self, name) - + return getattrMethod + def generateSetattrMethod(factory, class_definition, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) optional_attributes = dict([(attr["name"], True) for attr in classinfos["attributes"] if attr["use"] == "optional"]) elements = OrderedDict([(element["name"], element) for element in classinfos["elements"]]) - + def setattrMethod(self, name, value): - if attributes.has_key(name): + if name in attributes: attribute_infos = attributes[name] attribute_infos["attr_type"] = FindTypeInfos(factory, attribute_infos["attr_type"]) if optional_attributes.get(name, False): @@ -1320,65 +1345,66 @@ if value is None or value == default: self.attrib.pop(name, None) return - elif attribute_infos.has_key("fixed"): + elif "fixed" in attribute_infos: return return self.set(name, attribute_infos["attr_type"]["generate"](value)) - - elif elements.has_key(name): + + elif name in elements: element_infos = elements[name] element_infos["elmt_type"] = FindTypeInfos(factory, element_infos["elmt_type"]) if element_infos["type"] == ANY: element_infos["elmt_type"]["generate"](self, value) - + elif name == "content" and element_infos["elmt_type"]["type"] == SIMPLETYPE: self.text = element_infos["elmt_type"]["generate"](value) - + else: prefix = ("%s:" % factory.TargetNamespace if factory.TargetNamespace is not None else "") element_xpath = (prefix + name if name != "content" else elements["content"]["elmt_type"]["choices_xpath"].path) - + for element in self.xpath(element_xpath, namespaces=factory.NSMAP): self.remove(element) - + if value is not None: element_idx = elements.keys().index(name) if element_idx > 0: previous_elements_xpath = "|".join(map( lambda x: prefix + x - if x != "content" - else elements["content"]["elmt_type"]["choices_xpath"].path, + if x != "content" + else elements["content"]["elmt_type"]["choices_xpath"].path, elements.keys()[:element_idx])) - + insertion_point = len(self.xpath(previous_elements_xpath, namespaces=factory.NSMAP)) else: insertion_point = 0 - + if not isinstance(value, ListType): value = [value] - + for element in reversed(value): if element_infos["elmt_type"]["type"] == SIMPLETYPE: tmp_element = etree.Element(factory.etreeNamespaceFormat % name) tmp_element.text = element_infos["elmt_type"]["generate"](element) element = tmp_element self.insert(insertion_point, element) - - elif classinfos.has_key("base"): + + elif "base" in classinfos: return classinfos["base"].__setattr__(self, name, value) - + else: raise AttributeError("'%s' can't have an attribute '%s'." % (self.__class__.__name__, name)) - + return setattrMethod + def gettypeinfos(name, facets): - if facets.has_key("enumeration") and facets["enumeration"][0] is not None: + if "enumeration" in facets and facets["enumeration"][0] is not None: return facets["enumeration"][0] - elif facets.has_key("maxInclusive"): - limits = {"max" : None, "min" : None} + elif "maxInclusive" in facets: + limits = {"max": None, "min": None} if facets["maxInclusive"][0] is not None: limits["max"] = facets["maxInclusive"][0] elif facets["maxExclusive"][0] is not None: @@ -1391,24 +1417,28 @@ return limits return name + def generateGetElementAttributes(factory, classinfos): def getElementAttributes(self): attr_list = [] - if classinfos.has_key("base"): + if "base" in classinfos: attr_list.extend(classinfos["base"].getElementAttributes(self)) for attr in classinfos["attributes"]: if attr["use"] != "prohibited": - attr_params = {"name" : attr["name"], "use" : attr["use"], - "type" : gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]), - "value" : getattr(self, attr["name"], "")} + attr_params = { + "name": attr["name"], + "use": attr["use"], + "type": gettypeinfos(attr["attr_type"]["basename"], attr["attr_type"]["facets"]), + "value": getattr(self, attr["name"], "")} attr_list.append(attr_params) return attr_list return getElementAttributes + def generateGetElementInfos(factory, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) elements = dict([(element["name"], element) for element in classinfos["elements"]]) - + def getElementInfos(self, name, path=None, derived=False): attr_type = "element" value = None @@ -1416,17 +1446,17 @@ children = [] if path is not None: parts = path.split(".", 1) - if attributes.has_key(parts[0]): + if parts[0] in attributes: if len(parts) != 1: raise ValueError("Wrong path!") - attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], + attr_type = gettypeinfos(attributes[parts[0]]["attr_type"]["basename"], attributes[parts[0]]["attr_type"]["facets"]) value = getattr(self, parts[0], "") - elif elements.has_key(parts[0]): + elif parts[0] in elements: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: if len(parts) != 1: raise ValueError("Wrong path!") - attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], + attr_type = gettypeinfos(elements[parts[0]]["elmt_type"]["basename"], elements[parts[0]]["elmt_type"]["facets"]) value = getattr(self, parts[0], "") elif parts[0] == "content": @@ -1439,17 +1469,17 @@ return attr.getElementInfos(parts[0]) else: return attr.getElementInfos(parts[0], parts[1]) - elif elements.has_key("content"): + elif "content" in elements: if len(parts) > 0: return self.content.getElementInfos(name, path) - elif classinfos.has_key("base"): + elif "base" in classinfos: classinfos["base"].getElementInfos(name, path) else: raise ValueError("Wrong path!") else: if not derived: children.extend(self.getElementAttributes()) - if classinfos.has_key("base"): + if "base" in classinfos: children.extend(classinfos["base"].getElementInfos(self, name, derived=True)["children"]) for element_name, element in elements.items(): if element["minOccurs"] == 0: @@ -1463,8 +1493,10 @@ if self.content is not None: children.extend(self.content.getElementInfos(value)["children"]) elif element["elmt_type"]["type"] == SIMPLETYPE: - children.append({"name": element_name, "require": element["minOccurs"] != 0, - "type": gettypeinfos(element["elmt_type"]["basename"], + children.append({ + "name": element_name, + "require": element["minOccurs"] != 0, + "type": gettypeinfos(element["elmt_type"]["basename"], element["elmt_type"]["facets"]), "value": getattr(self, element_name, None)}) else: @@ -1475,28 +1507,29 @@ return {"name": name, "type": attr_type, "value": value, "use": use, "children": children} return getElementInfos + def generateSetElementValue(factory, classinfos): attributes = dict([(attr["name"], attr) for attr in classinfos["attributes"] if attr["use"] != "prohibited"]) elements = dict([(element["name"], element) for element in classinfos["elements"]]) - + def setElementValue(self, path, value): if path is not None: parts = path.split(".", 1) - if attributes.has_key(parts[0]): + if parts[0] in attributes: if len(parts) != 1: raise ValueError("Wrong path!") if attributes[parts[0]]["attr_type"]["basename"] == "boolean": setattr(self, parts[0], value) elif attributes[parts[0]]["use"] == "optional" and value == "": - if attributes[parts[0]].has_key("default"): - setattr(self, parts[0], - attributes[parts[0]]["attr_type"]["extract"]( - attributes[parts[0]]["default"], False)) + if "default" in attributes[parts[0]]: + setattr(self, parts[0], + attributes[parts[0]]["attr_type"]["extract"]( + attributes[parts[0]]["default"], False)) else: setattr(self, parts[0], None) else: setattr(self, parts[0], attributes[parts[0]]["attr_type"]["extract"](value, False)) - elif elements.has_key(parts[0]): + elif parts[0] in elements: if elements[parts[0]]["elmt_type"]["type"] == SIMPLETYPE: if len(parts) != 1: raise ValueError("Wrong path!") @@ -1511,17 +1544,17 @@ if instance is None and elements[parts[0]]["minOccurs"] == 0: instance = elements[parts[0]]["elmt_type"]["initial"]() setattr(self, parts[0], instance) - if instance != None: + if instance is not None: if len(parts) > 1: instance.setElementValue(parts[1], value) else: instance.setElementValue(None, value) - elif elements.has_key("content"): + elif "content" in elements: if len(parts) > 0: self.content.setElementValue(path, value) - elif classinfos.has_key("base"): + elif "base" in classinfos: classinfos["base"].setElementValue(self, path, value) - elif elements.has_key("content"): + elif "content" in elements: if value == "": if elements["content"]["minOccurs"] == 0: self.setcontent([]) @@ -1531,12 +1564,14 @@ self.setcontentbytype(value) return setElementValue -""" -Methods that generates the different methods for setting and getting the attributes -""" + def generateInitMethod(factory, classinfos): + """ + Methods that generates the different methods for setting and getting the attributes + """ + def initMethod(self): - if classinfos.has_key("base"): + if "base" in classinfos: classinfos["base"]._init_(self) for attribute in classinfos["attributes"]: attribute["attr_type"] = FindTypeInfos(factory, attribute["attr_type"]) @@ -1544,30 +1579,29 @@ self.set(attribute["name"], attribute["attr_type"]["generate"](attribute["attr_type"]["initial"]())) for element in classinfos["elements"]: if element["type"] != CHOICE: - element_name = ( - etree.QName(factory.NSMAP["xhtml"], "p") - if element["type"] == ANY - else factory.etreeNamespaceFormat % element["name"]) initial = GetElementInitialValue(factory, element) if initial is not None: map(self.append, initial) return initMethod + def generateSetMethod(attr): def setMethod(self, value): setattr(self, attr, value) return setMethod + def generateGetMethod(attr): def getMethod(self): return getattr(self, attr, None) return getMethod + def generateAddMethod(attr, factory, infos): def addMethod(self): if infos["type"] == ATTRIBUTE: infos["attr_type"] = FindTypeInfos(factory, infos["attr_type"]) - if not infos.has_key("default"): + if "default" not in infos: setattr(self, attr, infos["attr_type"]["initial"]()) elif infos["type"] == ELEMENT: infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) @@ -1579,11 +1613,13 @@ raise ValueError("Invalid class attribute!") return addMethod + def generateDeleteMethod(attr): def deleteMethod(self): setattr(self, attr, None) return deleteMethod + def generateAppendMethod(attr, maxOccurs, factory, infos): def appendMethod(self, value): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) @@ -1597,6 +1633,7 @@ raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) return appendMethod + def generateInsertMethod(attr, maxOccurs, factory, infos): def insertMethod(self, index, value): infos["elmt_type"] = FindTypeInfos(factory, infos["elmt_type"]) @@ -1612,15 +1649,18 @@ raise ValueError("There can't be more than %d values in \"%s\"!" % (maxOccurs, attr)) return insertMethod + def generateGetChoicesMethod(choice_types): def getChoicesMethod(self): return [choice["name"] for choice in choice_types] return getChoicesMethod + def generateSetChoiceByTypeMethod(factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) + def setChoiceMethod(self, content_type): - if not choices.has_key(content_type): + if content_type not in choices: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) new_content = choices[content_type]["elmt_type"]["initial"]() @@ -1629,10 +1669,12 @@ return new_content return setChoiceMethod + def generateAppendChoiceByTypeMethod(maxOccurs, factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) + def appendChoiceMethod(self, content_type): - if not choices.has_key(content_type): + if content_type not in choices: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) choices[content_type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) if maxOccurs == "unbounded" or len(self.content) < maxOccurs: @@ -1644,10 +1686,12 @@ raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) return appendChoiceMethod + def generateInsertChoiceByTypeMethod(maxOccurs, factory, choice_types): choices = dict([(choice["name"], choice) for choice in choice_types]) + def insertChoiceMethod(self, index, content_type): - if not choices.has_key(content_type): + if content_type not in choices: raise ValueError("Unknown \"%s\" choice type for \"content\"!" % content_type) choices[type]["elmt_type"] = FindTypeInfos(factory, choices[content_type]["elmt_type"]) if maxOccurs == "unbounded" or len(self.content) < maxOccurs: @@ -1659,6 +1703,7 @@ raise ValueError("There can't be more than %d values in \"content\"!" % maxOccurs) return insertChoiceMethod + def generateRemoveMethod(attr, minOccurs): def removeMethod(self, index): attr_list = getattr(self, attr) @@ -1668,52 +1713,52 @@ raise ValueError("There can't be less than %d values in \"%s\"!" % (minOccurs, attr)) return removeMethod + def generateCountMethod(attr): def countMethod(self): return len(getattr(self, attr)) return countMethod -""" -This function generate a xml parser from a class factory -""" NAMESPACE_PATTERN = re.compile("xmlns(?:\:[^\=]*)?=\"[^\"]*\" ") + class DefaultElementClass(etree.ElementBase): - + StructurePattern = re.compile("$") - + def _init_(self): pass - + def getLocalTag(self): return etree.QName(self.tag).localname - + def tostring(self): return NAMESPACE_PATTERN.sub("", etree.tostring(self, pretty_print=True, encoding='utf-8')).decode('utf-8') + class XMLElementClassLookUp(etree.PythonElementClassLookup): - + def __init__(self, classes, *args, **kwargs): etree.PythonElementClassLookup.__init__(self, *args, **kwargs) self.LookUpClasses = classes - + def GetElementClass(self, element_tag, parent_tag=None, default=DefaultElementClass): element_class = self.LookUpClasses.get(element_tag, (default, None)) if not isinstance(element_class, DictType): if isinstance(element_class[0], (StringType, UnicodeType)): return self.GetElementClass(element_class[0], default=default) return element_class[0] - + element_with_parent_class = element_class.get(parent_tag, default) if isinstance(element_with_parent_class, (StringType, UnicodeType)): return self.GetElementClass(element_with_parent_class, default=default) return element_with_parent_class - + def lookup(self, document, element): parent = element.getparent() - element_class = self.GetElementClass(element.tag, - parent.tag if parent is not None else None) + element_class = self.GetElementClass( + element.tag, parent.tag if parent is not None else None) if isinstance(element_class, ListType): children = "".join([ "%s " % etree.QName(child.tag).localname @@ -1726,6 +1771,7 @@ return element_class[0] return element_class + class XMLClassParser(etree.XMLParser): def __init__(self, namespaces, default_namespace_format, base_class, xsd_schema, *args, **kwargs): @@ -1741,24 +1787,24 @@ self.RootNSMAP = namespaces self.BaseClass = base_class self.XSDSchema = xsd_schema - + def set_element_class_lookup(self, class_lookup): etree.XMLParser.set_element_class_lookup(self, class_lookup) self.ClassLookup = class_lookup - + def LoadXMLString(self, xml_string): tree = etree.fromstring(xml_string, self) if not self.XSDSchema.validate(tree): error = self.XSDSchema.error_log.last_error return tree, (error.line, error.message) - return tree, None - + return tree, None + def Dumps(self, xml_obj): return etree.tostring(xml_obj, encoding='utf-8') - + def Loads(self, xml_string): return etree.fromstring(xml_string, self) - + def CreateRoot(self): if self.BaseClass is not None: root = self.makeelement( @@ -1767,42 +1813,46 @@ root._init_() return root return None - + def GetElementClass(self, element_tag, parent_tag=None): return self.ClassLookup.GetElementClass( - self.DefaultNamespaceFormat % element_tag, - self.DefaultNamespaceFormat % parent_tag - if parent_tag is not None else parent_tag, + self.DefaultNamespaceFormat % element_tag, + self.DefaultNamespaceFormat % parent_tag + if parent_tag is not None else parent_tag, None) - + def CreateElement(self, element_tag, parent_tag=None, class_idx=None): element_class = self.GetElementClass(element_tag, parent_tag) if isinstance(element_class, ListType): if class_idx is not None and class_idx < len(element_class): new_element = element_class[class_idx]() else: - raise ValueError, "No corresponding class found!" + raise ValueError("No corresponding class found!") else: new_element = element_class() DefaultElementClass.__setattr__(new_element, "tag", self.DefaultNamespaceFormat % element_tag) new_element._init_() return new_element - + + def GenerateParser(factory, xsdstring): + """ + This function generate a xml parser from a class factory + """ + ComputedClasses = factory.CreateClasses() - + if factory.FileName is not None: ComputedClasses = ComputedClasses[factory.FileName] BaseClass = [(name, XSDclass) for name, XSDclass in ComputedClasses.items() if XSDclass.IsBaseClass] - + parser = XMLClassParser( factory.NSMAP, factory.etreeNamespaceFormat, BaseClass[0] if len(BaseClass) == 1 else None, etree.XMLSchema(etree.fromstring(xsdstring)), - strip_cdata = False, remove_blank_text=True) + strip_cdata=False, remove_blank_text=True) class_lookup = XMLElementClassLookUp(factory.ComputedClassesLookUp) parser.set_element_class_lookup(class_lookup) - + return parser - diff -r c1298e7ffe3a -r 8391c11477f4 xmlclass/xsdschema.py --- a/xmlclass/xsdschema.py Fri Mar 24 12:07:47 2017 +0000 +++ b/xmlclass/xsdschema.py Tue Jan 30 16:06:58 2018 +0100 @@ -22,21 +22,27 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -import os, re + +from __future__ import absolute_import +from __future__ import print_function +import os +import re import datetime from xml.dom import minidom from types import * -from xmlclass import * +from xmlclass.xmlclass import * + def GenerateDictFacets(facets): return dict([(name, (None, False)) for name in facets]) + def GenerateSimpleTypeXMLText(function): def generateXMLTextMethod(value, name=None, indent=0): text = "" if name is not None: - ind1, ind2 = getIndent(indent, name) + ind1, _ind2 = getIndent(indent, name) text += ind1 + "<%s>" % name text += function(value) if name is not None: @@ -44,13 +50,16 @@ return text return generateXMLTextMethod -def GenerateFloatXMLText(extra_values=[], decimal=None): + +def GenerateFloatXMLText(extra_values=None, decimal=None): float_format = (lambda x: "{:.{width}f}".format(x, width=decimal).rstrip('0') if decimal is not None else str) + extra_values = [] if extra_values is None else extra_values + def generateXMLTextMethod(value, name=None, indent=0): text = "" if name is not None: - ind1, ind2 = getIndent(indent, name) + ind1, _ind2 = getIndent(indent, name) text += ind1 + "<%s>" % name if isinstance(value, IntType): text += str(value) @@ -62,46 +71,47 @@ text += "\n" % name return text return generateXMLTextMethod - + + DEFAULT_FACETS = GenerateDictFacets(["pattern", "whiteSpace", "enumeration"]) NUMBER_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["maxInclusive", "maxExclusive", "minInclusive", "minExclusive"]) DECIMAL_FACETS = GenerateDictFacets(NUMBER_FACETS.keys() + ["totalDigits", "fractionDigits"]) STRING_FACETS = GenerateDictFacets(DEFAULT_FACETS.keys() + ["length", "minLength", "maxLength"]) -ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", - "maxExclusive", "minInclusive", "minExclusive", "totalDigits", - "fractionDigits", "length", "minLength", "maxLength"] - - -#------------------------------------------------------------------------------- +ALL_FACETS = ["pattern", "whiteSpace", "enumeration", "maxInclusive", + "maxExclusive", "minInclusive", "minExclusive", "totalDigits", + "fractionDigits", "length", "minLength", "maxLength"] + + +# ------------------------------------------------------------------------------- # Structure reducing functions -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Documentation elements def ReduceAppInfo(factory, attributes, elements): - return {"type": "appinfo", "source": attributes.get("source", None), + return {"type": "appinfo", "source": attributes.get("source", None), "content": "\n".join(elements)} def ReduceDocumentation(factory, attributes, elements): - return {"type": "documentation", "source": attributes.get("source", None), + return {"type": "documentation", "source": attributes.get("source", None), "language": attributes.get("lang", "any"), "content": "\n".join(elements)} def ReduceAnnotation(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) + _annotations, children = factory.ReduceElements(elements) annotation = {"type": "annotation", "appinfo": [], "documentation": {}} for child in children: if child["type"] == "appinfo": annotation["appinfo"].append((child["source"], child["content"])) elif child["type"] == "documentation": if child["source"] is not None: - text = "(source: %(source)s):\n%(content)s\n\n"%child + text = "(source: %(source)s):\n%(content)s\n\n" % child else: text = child["content"] + "\n\n" - if not annotation["documentation"].has_key(child["language"]): + if not child["language"] in annotation["documentation"]: annotation["documentation"] = text else: annotation["documentation"] += text @@ -109,10 +119,11 @@ # Simple type elements + def GenerateFacetReducing(facetname, canbefixed): def ReduceFacet(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - if attributes.has_key("value"): + annotations, _children = factory.ReduceElements(elements) + if "value" in attributes: facet = {"type": facetname, "value": attributes["value"], "doc": annotations} if canbefixed: facet["fixed"] = attributes.get("fixed", False) @@ -124,7 +135,7 @@ def ReduceList(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) list = {"type": "list", "itemType": attributes.get("itemType", None), "doc": annotations} - + if len(children) > 0 and children[0]["type"] == SIMPLETYPE: if list["itemType"] is None: list["itemType"] = children[0] @@ -138,10 +149,10 @@ def ReduceUnion(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) union = {"type": "union", "memberTypes": attributes.get("memberTypes", []), "doc": annotations} - + for child in children: if child["type"] == SIMPLETYPE: - union["memberTypes"].appendchild + union["memberTypes"].append(child) if len(union["memberTypes"]) == 0: raise ValueError("No base type has been defined for union!") return union @@ -151,35 +162,35 @@ # Initialize type informations facets = {} simpleType = {"type": SIMPLETYPE, "final": attributes.get("final", [])} - if attributes.has_key("name"): + if "name" in attributes: simpleType["name"] = attributes["name"] - + if typeinfos["type"] in ["restriction", "extension"]: # Search for base type definition if isinstance(typeinfos["base"], (StringType, UnicodeType)): basetypeinfos = factory.FindSchemaElement(typeinfos["base"], SIMPLETYPE) if basetypeinfos is None: - raise "\"%s\" isn't defined!" % typeinfos["base"] + raise "\"%s\" isn't defined!" % typeinfos["base"] else: basetypeinfos = typeinfos["base"] - + # Check that base type is a simple type if basetypeinfos["type"] != SIMPLETYPE: raise ValueError("Base type given isn't a simpleType!") - + simpleType["basename"] = basetypeinfos["basename"] - + # Check that derivation is allowed - if basetypeinfos.has_key("final"): + if "final" in basetypeinfos: if "#all" in basetypeinfos["final"]: raise ValueError("Base type can't be derivated!") if "restriction" in basetypeinfos["final"] and typeinfos["type"] == "restriction": raise ValueError("Base type can't be derivated by restriction!") - + # Extract simple type facets for facet in typeinfos.get("facets", []): facettype = facet["type"] - if not basetypeinfos["facets"].has_key(facettype): + if facettype not in basetypeinfos["facets"]: raise ValueError("\"%s\" facet can't be defined for \"%s\" type!" % (facettype, type)) elif basetypeinfos["facets"][facettype][1]: raise ValueError("\"%s\" facet is fixed on base type!" % facettype) @@ -195,107 +206,107 @@ continue else: raise ValueError("\"%s\" facet can't be defined with another facet type!" % facettype) - elif facets.has_key("enumeration"): + elif "enumeration" in facets: raise ValueError("\"enumeration\" facet can't be defined with another facet type!") - elif facets.has_key("pattern"): + elif "pattern" in facets: raise ValueError("\"pattern\" facet can't be defined with another facet type!") - elif facets.has_key(facettype): + elif facettype in facets: raise ValueError("\"%s\" facet can't be defined two times!" % facettype) elif facettype == "length": - if facets.has_key("minLength"): + if "minLength" in facets: raise ValueError("\"length\" and \"minLength\" facets can't be defined at the same time!") - if facets.has_key("maxLength"): + if "maxLength" in facets: raise ValueError("\"length\" and \"maxLength\" facets can't be defined at the same time!") try: value = int(value) - except: + except Exception: raise ValueError("\"length\" must be an integer!") if value < 0: raise ValueError("\"length\" can't be negative!") elif basevalue is not None and basevalue != value: raise ValueError("\"length\" can't be different from \"length\" defined in base type!") elif facettype == "minLength": - if facets.has_key("length"): + if "length" in facets: raise ValueError("\"length\" and \"minLength\" facets can't be defined at the same time!") try: value = int(value) - except: + except Exception: raise ValueError("\"minLength\" must be an integer!") if value < 0: raise ValueError("\"minLength\" can't be negative!") - elif facets.has_key("maxLength") and value > facets["maxLength"]: + elif "maxLength" in facets and value > facets["maxLength"]: raise ValueError("\"minLength\" must be lesser than or equal to \"maxLength\"!") elif basevalue is not None and basevalue < value: raise ValueError("\"minLength\" can't be lesser than \"minLength\" defined in base type!") elif facettype == "maxLength": - if facets.has_key("length"): + if "length" in facets: raise ValueError("\"length\" and \"maxLength\" facets can't be defined at the same time!") try: value = int(value) - except: + except Exception: raise ValueError("\"maxLength\" must be an integer!") if value < 0: raise ValueError("\"maxLength\" can't be negative!") - elif facets.has_key("minLength") and value < facets["minLength"]: + elif "minLength" in facets and value < facets["minLength"]: raise ValueError("\"minLength\" must be lesser than or equal to \"maxLength\"!") elif basevalue is not None and basevalue > value: raise ValueError("\"maxLength\" can't be greater than \"maxLength\" defined in base type!") elif facettype == "minInclusive": - if facets.has_key("minExclusive"): + if "minExclusive" in facets: raise ValueError("\"minExclusive\" and \"minInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("maxInclusive") and value > facets["maxInclusive"][0]: + if "maxInclusive" in facets and value > facets["maxInclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than or equal to \"maxInclusive\"!") - elif facets.has_key("maxExclusive") and value >= facets["maxExclusive"][0]: + elif "maxExclusive" in facets and value >= facets["maxExclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than \"maxExclusive\"!") elif facettype == "minExclusive": - if facets.has_key("minInclusive"): + if "minInclusive" in facets: raise ValueError("\"minExclusive\" and \"minInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("maxInclusive") and value >= facets["maxInclusive"][0]: + if "maxInclusive" in facets and value >= facets["maxInclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxInclusive\"!") - elif facets.has_key("maxExclusive") and value >= facets["maxExclusive"][0]: + elif "maxExclusive" in facets and value >= facets["maxExclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxExclusive\"!") elif facettype == "maxInclusive": - if facets.has_key("maxExclusive"): + if "maxExclusive" in facets: raise ValueError("\"maxExclusive\" and \"maxInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("minInclusive") and value < facets["minInclusive"][0]: + if "minInclusive" in facets and value < facets["minInclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than or equal to \"maxInclusive\"!") - elif facets.has_key("minExclusive") and value <= facets["minExclusive"][0]: + elif "minExclusive" in facets and value <= facets["minExclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxInclusive\"!") elif facettype == "maxExclusive": - if facets.has_key("maxInclusive"): + if "maxInclusive" in facets: raise ValueError("\"maxExclusive\" and \"maxInclusive\" facets can't be defined at the same time!") value = basetypeinfos["extract"](facet["value"], False) - if facets.has_key("minInclusive") and value <= facets["minInclusive"][0]: + if "minInclusive" in facets and value <= facets["minInclusive"][0]: raise ValueError("\"minInclusive\" must be lesser than \"maxExclusive\"!") - elif facets.has_key("minExclusive") and value <= facets["minExclusive"][0]: + elif "minExclusive" in facets and value <= facets["minExclusive"][0]: raise ValueError("\"minExclusive\" must be lesser than \"maxExclusive\"!") elif facettype == "whiteSpace": if basevalue == "collapse" and value in ["preserve", "replace"] or basevalue == "replace" and value == "preserve": - raise ValueError("\"whiteSpace\" is incompatible with \"whiteSpace\" defined in base type!") + raise ValueError("\"whiteSpace\" is incompatible with \"whiteSpace\" defined in base type!") elif facettype == "totalDigits": - if facets.has_key("fractionDigits") and value <= facets["fractionDigits"][0]: + if "fractionDigits" in facets and value <= facets["fractionDigits"][0]: raise ValueError("\"fractionDigits\" must be lesser than or equal to \"totalDigits\"!") elif basevalue is not None and value > basevalue: raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!") elif facettype == "fractionDigits": - if facets.has_key("totalDigits") and value <= facets["totalDigits"][0]: + if "totalDigits" in facets and value <= facets["totalDigits"][0]: raise ValueError("\"fractionDigits\" must be lesser than or equal to \"totalDigits\"!") elif basevalue is not None and value > basevalue: raise ValueError("\"totalDigits\" can't be greater than \"totalDigits\" defined in base type!") facets[facettype] = (value, facet.get("fixed", False)) - - # Report not redefined facet from base type to new created type + + # Report not redefined facet from base type to new created type for facettype, facetvalue in basetypeinfos["facets"].items(): - if not facets.has_key(facettype): + if facettype not in facets: facets[facettype] = facetvalue - + # Generate extract value for new created type def ExtractSimpleTypeValue(attr, extract=True): value = basetypeinfos["extract"](attr, extract) - for facetname, (facetvalue, facetfixed) in facets.items(): + for facetname, (facetvalue, _facetfixed) in facets.items(): if facetvalue is not None: if facetname == "enumeration" and value not in facetvalue: raise ValueError("\"%s\" not in enumerated values" % value) @@ -311,13 +322,13 @@ raise ValueError("value must be greater than %s" % str(facetvalue)) elif facetname == "maxInclusive" and value > facetvalue: raise ValueError("value must be lesser than or equal to %s" % str(facetvalue)) - elif facetname == "maxExclusive" and value >= facetvalue: + elif facetname == "maxExclusive" and value >= facetvalue: raise ValueError("value must be lesser than %s" % str(facetvalue)) elif facetname == "pattern": model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue))) result = model.match(value) if result is None: - if len(facetvalue) > 1: + if len(facetvalue) > 1: raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue)) else: raise ValueError("value doesn't follow the pattern %s" % facetvalue[0]) @@ -327,9 +338,9 @@ elif facetvalue == "collapse": value = GetToken(value, False) return value - + def CheckSimpleTypeValue(value): - for facetname, (facetvalue, facetfixed) in facets.items(): + for facetname, (facetvalue, _facetfixed) in facets.items(): if facetvalue is not None: if facetname == "enumeration" and value not in facetvalue: return False @@ -345,39 +356,39 @@ return False elif facetname == "maxInclusive" and value > facetvalue: return False - elif facetname == "maxExclusive" and value >= facetvalue: + elif facetname == "maxExclusive" and value >= facetvalue: return False elif facetname == "pattern": model = re.compile("(?:%s)?$" % "|".join(map(lambda x: "(?:%s)" % x, facetvalue))) result = model.match(value) if result is None: - if len(facetvalue) > 1: + if len(facetvalue) > 1: raise ValueError("value doesn't follow any of the patterns %s" % ",".join(facetvalue)) else: raise ValueError("value doesn't follow the pattern %s" % facetvalue[0]) return True - + def SimpleTypeInitialValue(): - for facetname, (facetvalue, facetfixed) in facets.items(): + for facetname, (facetvalue, _facetfixed) in facets.items(): if facetvalue is not None: if facetname == "enumeration": return facetvalue[0] elif facetname == "length": return " "*facetvalue elif facetname == "minLength": - return " "*minLength + return " "*facetvalue elif facetname == "minInclusive" and facetvalue > 0: return facetvalue elif facetname == "minExclusive" and facetvalue >= 0: return facetvalue + 1 elif facetname == "maxInclusive" and facetvalue < 0: return facetvalue - elif facetname == "maxExclusive" and facetvalue <= 0: + elif facetname == "maxExclusive" and facetvalue <= 0: return facetvalue - 1 return basetypeinfos["initial"]() - + GenerateSimpleType = basetypeinfos["generate"] - + elif typeinfos["type"] == "list": # Search for item type definition if isinstance(typeinfos["itemType"], (StringType, UnicodeType)): @@ -386,41 +397,42 @@ raise "\"%s\" isn't defined!" % typeinfos["itemType"] else: itemtypeinfos = typeinfos["itemType"] - + # Check that item type is a simple type if itemtypeinfos["type"] != SIMPLETYPE: - raise ValueError, "Item type given isn't a simpleType!" - + raise ValueError("Item type given isn't a simpleType!") + simpleType["basename"] = "list" - + # Check that derivation is allowed - if itemtypeinfos.has_key("final"): - if itemtypeinfos["final"].has_key("#all"): + if "final" in itemtypeinfos: + if "#all" in itemtypeinfos["final"]: raise ValueError("Item type can't be derivated!") - if itemtypeinfos["final"].has_key("list"): + if "list" in itemtypeinfos["final"]: raise ValueError("Item type can't be derivated by list!") - + # Generate extract value for new created type - def ExtractSimpleTypeValue(attr, extract = True): + def ExtractSimpleTypeValue(attr, extract=True): values = [] for value in GetToken(attr, extract).split(" "): values.append(itemtypeinfos["extract"](value, False)) return values - + def CheckSimpleTypeValue(value): for item in value: result = itemtypeinfos["check"](item) if not result: return result return True - - SimpleTypeInitialValue = lambda: [] - + + def SimpleTypeInitialValue(): + return [] + GenerateSimpleType = GenerateSimpleTypeXMLText(lambda x: " ".join(map(itemtypeinfos["generate"], x))) - + facets = GenerateDictFacets(["length", "maxLength", "minLength", "enumeration", "pattern"]) facets["whiteSpace"] = ("collapse", False) - + elif typeinfos["type"] == "union": # Search for member types definition membertypesinfos = [] @@ -431,53 +443,49 @@ raise ValueError("\"%s\" isn't defined!" % membertype) else: infos = membertype - + # Check that member type is a simple type if infos["type"] != SIMPLETYPE: raise ValueError("Member type given isn't a simpleType!") - + # Check that derivation is allowed - if infos.has_key("final"): - if infos["final"].has_key("#all"): + if "final" in infos: + if "#all" in infos["final"]: raise ValueError("Item type can't be derivated!") - if infos["final"].has_key("union"): + if "union" in infos["final"]: raise ValueError("Member type can't be derivated by union!") - + membertypesinfos.append(infos) - + simpleType["basename"] = "union" - + # Generate extract value for new created type - def ExtractSimpleTypeValue(attr, extract = True): - if extract: - value = GetAttributeValue(attr) - else: - value = attr + def ExtractSimpleTypeValue(attr, extract=True): for infos in membertypesinfos: try: return infos["extract"](attr, False) - except: + except Exception: pass raise ValueError("\"%s\" isn't valid for type defined for union!") - + def CheckSimpleTypeValue(value): for infos in membertypesinfos: result = infos["check"](value) if result: return result return False - + SimpleTypeInitialValue = membertypesinfos[0]["initial"] - + def GenerateSimpleTypeFunction(value): if isinstance(value, BooleanType): return {True: "true", False: "false"}[value] else: return str(value) GenerateSimpleType = GenerateSimpleTypeXMLText(GenerateSimpleTypeFunction) - + facets = GenerateDictFacets(["pattern", "enumeration"]) - + simpleType["facets"] = facets simpleType["extract"] = ExtractSimpleTypeValue simpleType["initial"] = SimpleTypeInitialValue @@ -485,25 +493,27 @@ simpleType["generate"] = GenerateSimpleType return simpleType + def ReduceSimpleType(factory, attributes, elements): # Reduce all the simple type children annotations, children = factory.ReduceElements(elements) - + simpleType = CreateSimpleType(factory, attributes, children[0]) simpleType["doc"] = annotations - + return simpleType # Complex type + def ExtractAttributes(factory, elements, base=None): attrs = [] attrnames = {} if base is not None: basetypeinfos = factory.FindSchemaElement(base) if not isinstance(basetypeinfos, (UnicodeType, StringType)) and basetypeinfos["type"] == COMPLEXTYPE: - attrnames = dict(map(lambda x:(x["name"], True), basetypeinfos["attributes"])) - + attrnames = dict(map(lambda x: (x["name"], True), basetypeinfos["attributes"])) + for element in elements: if element["type"] == ATTRIBUTE: if attrnames.get(element["name"], False): @@ -534,7 +544,7 @@ raise ValueError("Only one base type can be defined for restriction!") if restriction["base"] is None: raise ValueError("No base type has been defined for restriction!") - + while len(children) > 0 and children[0]["type"] in ALL_FACETS: restriction["facets"].append(children.pop(0)) restriction["attributes"] = ExtractAttributes(factory, children, restriction["base"]) @@ -543,7 +553,7 @@ def ReduceExtension(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - if not attributes.has_key("base"): + if "base" not in attributes: raise ValueError("No base type has been defined for extension!") extension = {"type": "extension", "attributes": [], "elements": [], "base": attributes["base"], "doc": annotations} if len(children) > 0: @@ -557,8 +567,8 @@ content["name"] = "content" extension["elements"].append(content) elif group["type"] == "group": - elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if elmtgroup.has_key("elements"): + elmtgroup = factory.FindSchemaElement(group["ref"], ELEMENTSGROUP) + if "elements" in elmtgroup: extension["elements"] = elmtgroup["elements"] extension["order"] = elmtgroup["order"] else: @@ -571,24 +581,24 @@ def ReduceSimpleContent(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + simpleContent = children[0].copy() - + basetypeinfos = factory.FindSchemaElement(simpleContent["base"]) if basetypeinfos["type"] == SIMPLETYPE: contenttypeinfos = simpleContent.copy() simpleContent.pop("base") - elif basetypeinfos["type"] == COMPLEXTYPE and \ - len(basetypeinfos["elements"]) == 1 and \ - basetypeinfos["elements"][0]["name"] == "content" and \ - basetypeinfos["elements"][0].has_key("elmt_type") and \ - basetypeinfos["elements"][0]["elmt_type"]["type"] == SIMPLETYPE: + elif (basetypeinfos["type"] == COMPLEXTYPE and + len(basetypeinfos["elements"]) == 1 and + basetypeinfos["elements"][0]["name"] == "content" and + "elmt_type" in basetypeinfos["elements"][0] and + basetypeinfos["elements"][0]["elmt_type"]["type"] == SIMPLETYPE): contenttypeinfos = simpleContent.copy() contenttypeinfos["base"] = basetypeinfos["elements"][0]["elmt_type"] else: raise ValueError("No compatible base type defined for simpleContent!") contenttypeinfos = CreateSimpleType(factory, attributes, contenttypeinfos) - + simpleContent["elements"] = [{"name": "content", "type": ELEMENT, "elmt_type": contenttypeinfos, "doc": annotations, "minOccurs": 1, "maxOccurs": 1}] @@ -597,7 +607,7 @@ def ReduceComplexContent(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) + _annotations, children = factory.ReduceElements(elements) complexContent = children[0].copy() complexContent["type"] = "complexContent" return complexContent @@ -605,7 +615,7 @@ def ReduceComplexType(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + if len(children) > 0: if children[0]["type"] in ["simpleContent", "complexContent"]: complexType = children[0].copy() @@ -640,8 +650,8 @@ content["name"] = "content" complexType["elements"].append(content) elif group["type"] == "group": - elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if elmtgroup.has_key("elements"): + elmtgroup = factory.FindSchemaElement(group["ref"], ELEMENTSGROUP) + if "elements" in elmtgroup: complexType["elements"] = elmtgroup["elements"] complexType["order"] = elmtgroup["order"] else: @@ -661,36 +671,36 @@ # Attribute elements def ReduceAnyAttribute(factory, attributes, elements): - return {"type" : "anyAttribute"} + return {"type": "anyAttribute"} def ReduceAttribute(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - - if attributes.has_key("default"): - if attributes.has_key("fixed"): + + if "default" in attributes: + if "fixed" in attributes: raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!") elif attributes.get("use", "optional") != "optional": raise ValueError("if \"default\" present, \"use\" can only have the value \"optional\"!") - + attribute = {"type": ATTRIBUTE, "attr_type": attributes.get("type", None), "doc": annotations} if len(children) > 0: if attribute["attr_type"] is None: attribute["attr_type"] = children[0] else: raise ValueError("Only one type can be defined for attribute!") - - if attributes.has_key("ref"): - if attributes.has_key("name"): + + if "ref" in attributes: + if "name" in attributes: raise ValueError("\"ref\" and \"name\" can't be defined at the same time!") - elif attributes.has_key("form"): + elif "form" in attributes: raise ValueError("\"ref\" and \"form\" can't be defined at the same time!") elif attribute["attr_type"] is not None: raise ValueError("if \"ref\" is present, no type can be defined!") elif attribute["attr_type"] is None: raise ValueError("No type has been defined for attribute \"%s\"!" % attributes["name"]) - - if attributes.has_key("type"): + + if "type" in attributes: tmp_attrs = attributes.copy() tmp_attrs.pop("type") attribute.update(tmp_attrs) @@ -701,7 +711,7 @@ def ReduceAttributeGroup(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - if attributes.has_key("ref"): + if "ref" in attributes: return {"type": "attributeGroup", "ref": attributes["ref"], "doc": annotations} else: return {"type": ATTRIBUTESGROUP, "attributes": ExtractAttributes(factory, children), "doc": annotations} @@ -710,15 +720,16 @@ # Elements groups def ReduceAny(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + annotations, _children = factory.ReduceElements(elements) + any = {"type": ANY, "doc": annotations} any.update(attributes) return any + def ReduceElement(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + types = [] constraints = [] for child in children: @@ -726,19 +737,19 @@ constraints.append(child) else: types.append(child) - - if attributes.has_key("default") and attributes.has_key("fixed"): + + if "default" in attributes and "fixed" in attributes: raise ValueError("\"default\" and \"fixed\" can't be defined at the same time!") - - if attributes.has_key("ref"): + + if "ref" in attributes: for attr in ["name", "default", "fixed", "form", "block", "type"]: - if attributes.has_key(attr): + if attr in attributes: raise ValueError("\"ref\" and \"%s\" can't be defined at the same time!" % attr) - if attributes.has_key("nillable"): + if "nillable" in attributes: raise ValueError("\"ref\" and \"nillable\" can't be defined at the same time!") if len(types) > 0: raise ValueError("No type and no constraints can be defined where \"ref\" is defined!") - + infos = factory.FindSchemaElement(attributes["ref"], ELEMENT) if infos is not None: element = infos.copy() @@ -747,9 +758,9 @@ element["maxOccurs"] = attributes["maxOccurs"] return element else: - raise ValueError("\"%s\" base type isn't defined or circular referenced!" % name) - - elif attributes.has_key("name"): + raise ValueError("\"%s\" base type isn't defined or circular referenced!" % attributes["ref"]) + + elif "name" in attributes: element = {"type": ELEMENT, "elmt_type": attributes.get("type", None), "constraints": constraints, "doc": annotations} if len(types) > 0: if element["elmt_type"] is None: @@ -759,8 +770,8 @@ elif element["elmt_type"] is None: element["elmt_type"] = "tag" element["type"] = TAG - - if attributes.has_key("type"): + + if "type" in attributes: tmp_attrs = attributes.copy() tmp_attrs.pop("type") element.update(tmp_attrs) @@ -770,20 +781,21 @@ else: raise ValueError("\"Element\" must have at least a \"ref\" or a \"name\" defined!") + def ReduceAll(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + for child in children: - if children["maxOccurs"] == "unbounded" or children["maxOccurs"] > 1: + if child["maxOccurs"] == "unbounded" or child["maxOccurs"] > 1: raise ValueError("\"all\" item can't have \"maxOccurs\" attribute greater than 1!") - + return {"type": "all", "elements": children, "minOccurs": attributes["minOccurs"], "maxOccurs": attributes["maxOccurs"], "order": False, "doc": annotations} def ReduceChoice(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + choices = [] for child in children: if child["type"] in [ELEMENT, ANY, TAG]: @@ -791,12 +803,12 @@ elif child["type"] == "sequence": child["minOccurs"] = child["maxOccurs"] = 1 choices.append(child) - #raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!") + # raise ValueError("\"sequence\" in \"choice\" is not supported. Create instead a new complex type!") elif child["type"] == CHOICE: choices.extend(child["choices"]) elif child["type"] == "group": - elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if not elmtgroup.has_key("choices"): + elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) + if "choices" not in elmtgroup: raise ValueError("Only group composed of \"choice\" can be referenced in \"choice\" element!") choices_tmp = [] for choice in elmtgroup["choices"]: @@ -810,18 +822,18 @@ else: choices_tmp.append(choice) choices.extend(choices_tmp) - + for choice in choices: attributes["minOccurs"] = min(attributes["minOccurs"], choice["minOccurs"]) choice["minOccurs"] = 1 - + return {"type": CHOICE, "choices": choices, "minOccurs": attributes["minOccurs"], "maxOccurs": attributes["maxOccurs"], "doc": annotations} def ReduceSequence(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - + sequence = [] for child in children: if child["type"] in [ELEMENT, ANY, TAG, CHOICE]: @@ -830,12 +842,12 @@ sequence.extend(child["elements"]) elif child["type"] == "group": elmtgroup = factory.FindSchemaElement(child["ref"], ELEMENTSGROUP) - if not elmtgroup.has_key("elements") or not elmtgroup["order"]: + if "elements" not in elmtgroup or not elmtgroup["order"]: raise ValueError("Only group composed of \"sequence\" can be referenced in \"sequence\" element!") elements_tmp = [] for element in elmtgroup["elements"]: if not isinstance(element["elmt_type"], (UnicodeType, StringType)) and element["elmt_type"]["type"] == COMPLEXTYPE: - elmt_type = "%s_%s"%(elmtgroup["name"], element["name"]) + elmt_type = "%s_%s" % (elmtgroup["name"], element["name"]) if factory.TargetNamespace is not None: elmt_type = "%s:%s" % (factory.TargetNamespace, elmt_type) new_element = element.copy() @@ -844,15 +856,15 @@ else: elements_tmp.append(element) sequence.extend(elements_tmp) - + return {"type": "sequence", "elements": sequence, "minOccurs": attributes["minOccurs"], "maxOccurs": attributes["maxOccurs"], "order": True, "doc": annotations} - - + + def ReduceGroup(factory, attributes, elements): annotations, children = factory.ReduceElements(elements) - - if attributes.has_key("ref"): + + if "ref" in attributes: return {"type": "group", "ref": attributes["ref"], "doc": annotations} else: element = children[0] @@ -860,57 +872,63 @@ if element["type"] == CHOICE: group["choices"] = element["choices"] else: - group.update({"elements": element["elements"], "order": group["order"]}) + group.update({"elements": element["elements"], "order": element["order"]}) group.update(attributes) return group # Constraint elements + def ReduceUnique(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + _annotations, children = factory.ReduceElements(elements) + unique = {"type": CONSTRAINT, "const_type": "unique", "selector": children[0], "fields": children[1:]} unique.update(attributes) return unique - + + def ReduceKey(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + _annotations, children = factory.ReduceElements(elements) + key = {"type": CONSTRAINT, "const_type": "key", "selector": children[0], "fields": children[1:]} key.update(attributes) return key + def ReduceKeyRef(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + _annotations, children = factory.ReduceElements(elements) + keyref = {"type": CONSTRAINT, "const_type": "keyref", "selector": children[0], "fields": children[1:]} keyref.update(attributes) return keyref - + + def ReduceSelector(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + factory.ReduceElements(elements) + selector = {"type": CONSTRAINT, "const_type": "selector"} selector.update(attributes) return selector + def ReduceField(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + factory.ReduceElements(elements) + field = {"type": CONSTRAINT, "const_type": "field"} field.update(attributes) return field - + # Inclusion elements def ReduceImport(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) + factory.ReduceElements(elements) raise ValueError("\"import\" element isn't supported yet!") + def ReduceInclude(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) - + factory.ReduceElements(elements) + if factory.FileName is None: raise ValueError("Include in XSD string not yet supported") filepath = attributes["schemaLocation"] @@ -922,7 +940,7 @@ include_factory = XSDClassFactory(minidom.parse(xsdfile), filepath) xsdfile.close() include_factory.CreateClasses() - + if factory.TargetNamespace == include_factory.TargetNamespace: factory.Namespaces[factory.TargetNamespace].update(include_factory.Namespaces[include_factory.TargetNamespace]) else: @@ -931,9 +949,10 @@ factory.ComputedClassesLookUp.update(include_factory.ComputedClassesLookUp) factory.EquivalentClassesParent.update(include_factory.EquivalentClassesParent) return None - + + def ReduceRedefine(factory, attributes, elements): - annotations, children = factory.ReduceElements(elements) + factory.ReduceElements(elements) raise ValueError("\"redefine\" element isn't supported yet!") @@ -944,23 +963,24 @@ factory.ElementFormDefault = attributes["elementFormDefault"] factory.BlockDefault = attributes["blockDefault"] factory.FinalDefault = attributes["finalDefault"] - + targetNamespace = attributes.get("targetNamespace", None) factory.TargetNamespace = factory.DefinedNamespaces.get(targetNamespace, None) if factory.TargetNamespace is not None: factory.etreeNamespaceFormat = "{%s}%%s" % targetNamespace factory.Namespaces[factory.TargetNamespace] = {} - - annotations, children = factory.ReduceElements(elements, True) - + + _annotations, children = factory.ReduceElements(elements, True) + for child in children: - if child.has_key("name"): + if "name" in child: infos = factory.GetQualifiedNameInfos(child["name"], factory.TargetNamespace, True) if infos is None: factory.Namespaces[factory.TargetNamespace][child["name"]] = child elif not CompareSchema(infos, child): raise ValueError("\"%s\" is defined twice in targetNamespace!" % child["name"]) + def CompareSchema(schema, reference): if isinstance(schema, ListType): if not isinstance(reference, ListType) or len(schema) != len(reference): @@ -975,7 +995,7 @@ return False for name, value in schema.items(): ref_value = reference.get(name, None) - if ref_value is None and value != None: + if ref_value is None and value is not None: return False result = CompareSchema(value, ref_value) if not result: @@ -987,10 +1007,11 @@ else: return True return schema == reference - -#------------------------------------------------------------------------------- + + +# ------------------------------------------------------------------------------- # Base class for XSD schema extraction -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class XSDClassFactory(ClassFactory): @@ -999,7 +1020,7 @@ ClassFactory.__init__(self, document, filepath, debug) self.Namespaces["xml"] = { "lang": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("lang", LANGUAGE_model) } @@ -1007,31 +1028,31 @@ } self.Namespaces["xsi"] = { "noNamespaceSchemaLocation": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("noNamespaceSchemaLocation") } }, "nil": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("nil") } }, "schemaLocation": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("schemaLocation") } }, "type": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": NotSupportedYet("type") } } } - + def ParseSchema(self): for child in self.Document.childNodes: if child.nodeType == self.Document.ELEMENT_NODE: @@ -1061,16 +1082,16 @@ if element is None: if name in self.CurrentCompilations: if self.Debug: - print "Warning : \"%s\" is circular referenced!" % element_name + print("Warning : \"%s\" is circular referenced!" % element_name) else: raise ValueError("\"%s\" isn't defined!" % element_name) if element_type is not None and element["type"] != element_type: raise ValueError("\"%s\" isn't of the expected type!" % element_name) return element - + def CreateSchemaElement(self, element_name, element_type): for type, attributes, elements in self.Schema[2]: - namespace, name = DecomposeQualifiedName(type) + _namespace, name = DecomposeQualifiedName(type) if attributes.get("name", None) == element_name: element_infos = None if element_type in (ATTRIBUTE, None) and name == "attribute": @@ -1090,11 +1111,12 @@ return element_infos return None -""" -This function opens the xsd file and generate a xml parser with class lookup from -the xml tree -""" + def GenerateParserFromXSD(filepath): + """ + This function opens the xsd file and generate a xml parser with class lookup from + the xml tree + """ xsdfile = open(filepath, 'r') xsdstring = xsdfile.read() xsdfile.close() @@ -1104,24 +1126,27 @@ os.chdir(cwd) return parser -""" -This function generate a xml from the xsd given as a string -""" + def GenerateParserFromXSDstring(xsdstring): + """ + This function generate a xml from the xsd given as a string + """ return GenerateParser(XSDClassFactory(minidom.parseString(xsdstring)), xsdstring) -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # XSD schema syntax elements -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- + XSD_NAMESPACE = { -#------------------------------------------------------------------------------- -# Syntax elements definition -#------------------------------------------------------------------------------- - - "all": {"struct": """ + # ------------------------------------------------------------------------------- + # Syntax elements definition + # ------------------------------------------------------------------------------- + + "all": { + "struct": """ Content: (annotation?, element*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("all", ["id", "maxOccurs", "minOccurs"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "all", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element )*)")) }, "reduce": ReduceAll }, - "annotation": {"struct": """ + "annotation": { + "struct": """ Content: (appinfo | documentation)* """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("annotation", ["id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "annotation", ["id"], re.compile("((?:app_info |documentation )*)")) }, "reduce": ReduceAnnotation }, - "any": {"struct": """ + "any": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("any", - ["id", "maxOccurs", "minOccurs", "namespace", "processContents"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "any", + ["id", "maxOccurs", "minOccurs", "namespace", "processContents"], re.compile("((?:annotation )?(?:simpleType )*)")) }, "reduce": ReduceAny }, - "anyAttribute": {"struct": """ + "anyAttribute": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("anyAttribute", - ["id", "namespace", "processContents"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "anyAttribute", ["id", "namespace", "processContents"], ONLY_ANNOTATION) }, "reduce": ReduceAnyAttribute }, - "appinfo": {"struct": """ + "appinfo": { + "struct": """ Content: ({any})* """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("appinfo", ["source"], re.compile("(.*)"), True) }, "reduce": ReduceAppInfo }, - "attribute": {"struct": """ + "attribute": { + "struct": """ Content: (annotation?, simpleType?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("attribute", - ["default", "fixed", "form", "id", "name", "ref", "type", "use"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "attribute", + ["default", "fixed", "form", "id", "name", "ref", "type", "use"], re.compile("((?:annotation )?(?:simpleType )?)")), - "schema": GenerateElement("attribute", - ["default", "fixed", "form", "id", "name", "type"], + "schema": GenerateElement( + "attribute", + ["default", "fixed", "form", "id", "name", "type"], re.compile("((?:annotation )?(?:simpleType )?)")) }, "reduce": ReduceAttribute }, - "attributeGroup": {"struct": """ + "attributeGroup": { + "struct": """ Content: (annotation?, ((attribute | attributeGroup)*, anyAttribute?)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("attributeGroup", + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "attributeGroup", ["id", "ref"], ONLY_ANNOTATION), - "schema": GenerateElement("attributeGroup", - ["id", "name"], + "schema": GenerateElement( + "attributeGroup", + ["id", "name"], re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")) }, "reduce": ReduceAttributeGroup }, - "choice": {"struct": """ + "choice": { + "struct": """ Content: (annotation?, (element | group | choice | sequence | any)*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("choice", ["id", "maxOccurs", "minOccurs"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "choice", + ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)")) }, "reduce": ReduceChoice }, - "complexContent": {"struct": """ + "complexContent": { + "struct": """ Content: (annotation?, (restriction | extension)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("complexContent", ["id", "mixed"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "complexContent", + ["id", "mixed"], re.compile("((?:annotation )?(?:restriction |extension ))")) }, "reduce": ReduceComplexContent }, - "complexType": {"struct": """ + "complexType": { + "struct": """ Content: (annotation?, (simpleContent | complexContent | ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)))) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("complexType", - ["abstract", "block", "final", "id", "mixed", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "complexType", + ["abstract", "block", "final", "id", "mixed", "name"], re.compile("((?:annotation )?(?:simpleContent |complexContent |(?:(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))))")) }, "reduce": ReduceComplexType }, - "documentation": {"struct" : """ + "documentation": { + "struct": """ Content: ({any})* """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("documentation", - ["source", "lang"], re.compile("(.*)"), True) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "documentation", + ["source", "lang"], + re.compile("(.*)"), True) }, "reduce": ReduceDocumentation }, - "element": {"struct": """ + "element": { + "struct": """ Content: (annotation?, ((simpleType | complexType)?, (unique | key | keyref)*)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("element", - ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "maxOccurs", "minOccurs", "name", "nillable", "ref", "substitutionGroup", "type"], re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")), - "schema": GenerateElement("element", - ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"], + "schema": GenerateElement( + "element", + ["abstract", "block", "default", "final", "fixed", "form", "id", "name", "nillable", "substitutionGroup", "type"], re.compile("((?:annotation )?(?:simpleType |complexType )?(?:unique |key |keyref )*)")) }, "reduce": ReduceElement }, - "enumeration": {"struct": """ + "enumeration": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("enumeration", ["id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("enumeration", False) }, - "extension": {"struct": """ + "extension": { + "struct": """ Content: (annotation?, ((group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?))) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("extension", ["base", "id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "extension", + ["base", "id"], re.compile("((?:annotation )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")), - "complexContent": GenerateElement("extension", ["base", "id"], + "complexContent": GenerateElement( + "extension", + ["base", "id"], re.compile("((?:annotation )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?))")) }, "reduce": ReduceExtension }, - "field": {"struct": """ + "field": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("field", ["id", "xpath"], ONLY_ANNOTATION) }, "reduce": ReduceField }, - "fractionDigits": {"struct": """ + "fractionDigits": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("fractionDigits", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "fractionDigits", + ["fixed", "id", "value"], + ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("fractionDigits", True) }, - "group": {"struct": """ + "group": { + "struct": """ Content: (annotation?, (all | choice | sequence)?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("group", - ["id", "maxOccurs", "minOccurs", "ref"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "group", + ["id", "maxOccurs", "minOccurs", "ref"], re.compile("((?:annotation )?(?:all |choice |sequence )?)")), - "schema": GenerateElement("group", - ["id", "name"], + "schema": GenerateElement( + "group", + ["id", "name"], re.compile("((?:annotation )?(?:all |choice |sequence )?)")) }, "reduce": ReduceGroup }, - "import": {"struct": """ + "import": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("import", - ["id", "namespace", "schemaLocation"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "import", + ["id", "namespace", "schemaLocation"], + ONLY_ANNOTATION) }, "reduce": ReduceImport }, - "include": {"struct": """ + "include": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("include", - ["id", "schemaLocation"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "include", + ["id", "schemaLocation"], + ONLY_ANNOTATION) }, "reduce": ReduceInclude }, - "key": {"struct": """ + "key": { + "struct": """ Content: (annotation?, (selector, field+)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("key", ["id", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "key", ["id", "name"], re.compile("((?:annotation )?(?:selector (?:field )+))")) }, "reduce": ReduceKey }, - "keyref": {"struct": """ + "keyref": { + "struct": """ Content: (annotation?, (selector, field+)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("keyref", ["id", "name", "refer"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "keyref", ["id", "name", "refer"], re.compile("((?:annotation )?(?:selector (?:field )+))")) }, "reduce": ReduceKeyRef }, - "length": {"struct" : """ + "length": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("length", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "length", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("length", True) }, - "list": {"struct": """ + "list": { + "struct": """ Content: (annotation?, simpleType?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("list", ["id", "itemType"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "list", ["id", "itemType"], re.compile("((?:annotation )?(?:simpleType )?)$")) }, "reduce": ReduceList }, - "maxExclusive": {"struct": """ + "maxExclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("maxExclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "maxExclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("maxExclusive", True) }, - "maxInclusive": {"struct": """ + "maxInclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("maxInclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "maxInclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("maxInclusive", True) }, - "maxLength": {"struct": """ + "maxLength": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("maxLength", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "maxLength", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("maxLength", True) }, - "minExclusive": {"struct": """ + "minExclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("minExclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "minExclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("minExclusive", True) }, - "minInclusive": {"struct": """ + "minInclusive": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("minInclusive", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "minInclusive", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("minInclusive", True) }, - "minLength": {"struct": """ + "minLength": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("minLength", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "minLength", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("minLength", True) }, - "pattern": {"struct": """ + "pattern": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("pattern", ["id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("pattern", False) }, - "redefine": {"struct": """ + "redefine": { + "struct": """ Content: (annotation | (simpleType | complexType | group | attributeGroup))* """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("refine", ["id", "schemaLocation"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "refine", ["id", "schemaLocation"], re.compile("((?:annotation |(?:simpleType |complexType |group |attributeGroup ))*)")) }, "reduce": ReduceRedefine }, - "restriction": {"struct": """ + "restriction": { + "struct": """ Content: (annotation?, (group | all | choice | sequence)?, ((attribute | attributeGroup)*, anyAttribute?)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("restriction", ["base", "id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "restriction", + ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)))")), - "simpleContent": GenerateElement("restriction", ["base", "id"], + "simpleContent": GenerateElement( + "restriction", + ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:(?:minExclusive |minInclusive |maxExclusive |maxInclusive |totalDigits |fractionDigits |length |minLength |maxLength |enumeration |whiteSpace |pattern )*)?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")), - "complexContent": GenerateElement("restriction", ["base", "id"], + "complexContent": GenerateElement( + "restriction", + ["base", "id"], re.compile("((?:annotation )?(?:(?:simpleType )?(?:group |all |choice |sequence )?(?:(?:attribute |attributeGroup )*(?:anyAttribute )?)))")), }, "reduce": ReduceRestriction }, - "schema": {"struct": """ + "schema": { + "struct": """ Content: ((include | import | redefine | annotation)*, (((simpleType | complexType | group | attributeGroup) | element | attribute | notation), annotation*)*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("schema", - ["attributeFormDefault", "blockDefault", "elementFormDefault", "finalDefault", "id", "targetNamespace", "version", "lang"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "schema", + ["attributeFormDefault", + "blockDefault", + "elementFormDefault", + "finalDefault", + "id", + "targetNamespace", + "version", + "lang"], re.compile("((?:include |import |redefine |annotation )*(?:(?:(?:simpleType |complexType |group |attributeGroup )|element |attribute |annotation )(?:annotation )*)*)")) } }, - "selector": {"struct": """ + "selector": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, + "type": SYNTAXELEMENT, "extract": { "default": GenerateElement("selector", ["id", "xpath"], ONLY_ANNOTATION) }, "reduce": ReduceSelector }, - "sequence": {"struct": """ + "sequence": { + "struct": """ Content: (annotation?, (element | group | choice | sequence | any)*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("sequence", ["id", "maxOccurs", "minOccurs"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "sequence", ["id", "maxOccurs", "minOccurs"], re.compile("((?:annotation )?(?:element |group |choice |sequence |any )*)")) }, "reduce": ReduceSequence }, - "simpleContent": {"struct" : """ + "simpleContent": { + "struct": """ Content: (annotation?, (restriction | extension)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("simpleContent", ["id"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "simpleContent", ["id"], re.compile("((?:annotation )?(?:restriction |extension ))")) }, "reduce": ReduceSimpleContent }, - "simpleType": {"struct" : """ + "simpleType": { + "struct": """ Content: (annotation?, (restriction | list | union)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("simpleType", ["final", "id", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "simpleType", ["final", "id", "name"], re.compile("((?:annotation )?(?:restriction |list |union ))")) }, "reduce": ReduceSimpleType }, - "totalDigits": {"struct" : """ + "totalDigits": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("totalDigits", - ["fixed", "id", "value"], ONLY_ANNOTATION), + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "totalDigits", ["fixed", "id", "value"], ONLY_ANNOTATION), }, "reduce": GenerateFacetReducing("totalDigits", True) }, - "union": {"struct": """ + "union": { + "struct": """ Content: (annotation?, simpleType*) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("union", ["id", "memberTypes"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "union", ["id", "memberTypes"], re.compile("((?:annotation )?(?:simpleType )*)")) }, "reduce": ReduceUnion }, - "unique": {"struct": """ + "unique": { + "struct": """ Content: (annotation?, (selector, field+)) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("unique", ["id", "name"], + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "unique", ["id", "name"], re.compile("((?:annotation )?(?:selector |(?:field )+))")) }, "reduce": ReduceUnique }, - - "whiteSpace": {"struct" : """ + + "whiteSpace": { + "struct": """ Content: (annotation?) """, - "type": SYNTAXELEMENT, - "extract": { - "default": GenerateElement("whiteSpace", - ["fixed", "id", "value"], ONLY_ANNOTATION) + "type": SYNTAXELEMENT, + "extract": { + "default": GenerateElement( + "whiteSpace", ["fixed", "id", "value"], ONLY_ANNOTATION) }, "reduce": GenerateFacetReducing("whiteSpace", True) }, -#------------------------------------------------------------------------------- -# Syntax attributes definition -#------------------------------------------------------------------------------- + # ------------------------------------------------------------------------------- + # Syntax attributes definition + # ------------------------------------------------------------------------------- "abstract": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean }, @@ -1818,31 +1934,32 @@ }, "attributeFormDefault": { - "type": SYNTAXATTRIBUTE, - "extract": { - "default": GenerateEnumeratedExtraction("member attributeFormDefault", ["qualified", "unqualified"]) + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateEnumeratedExtraction( + "member attributeFormDefault", ["qualified", "unqualified"]) }, "default": { "default": "unqualified" } }, - + "base": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member base", QName_model) } }, - + "block": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("block", ["restriction", "extension", "substitution"]) } }, - + "blockDefault": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("block", ["restriction", "extension", "substitution"]) }, @@ -1850,16 +1967,16 @@ "default": "" } }, - + "default": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetAttributeValue } }, "elementFormDefault": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member elementFormDefault", ["qualified", "unqualified"]) }, @@ -1867,9 +1984,9 @@ "default": "unqualified" } }, - + "final": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("final", ["restriction", "extension", "substitution"]), "simpleType": GenerateGetList("final", ["list", "union", "restriction"]) @@ -1877,7 +1994,7 @@ }, "finalDefault": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateGetList("finalDefault", ["restriction", "extension", "list", "union"]) }, @@ -1885,9 +2002,9 @@ "default": "" } }, - + "fixed": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean, "attribute": GetAttributeValue, @@ -1901,35 +2018,35 @@ }, "form": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member form", ["qualified", "unqualified"]) } }, "id": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member id", NCName_model) } }, - + "itemType": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member itemType", QName_model) } }, "memberTypes": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameListExtraction("member memberTypes", QNames_model) }, }, - + "maxOccurs": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateLimitExtraction(), "all": GenerateLimitExtraction(1, 1, False) @@ -1940,9 +2057,9 @@ }, "minOccurs": { - "type": SYNTAXATTRIBUTE, - "extract": { - "default": GenerateLimitExtraction(unbounded = False), + "type": SYNTAXATTRIBUTE, + "extract": { + "default": GenerateLimitExtraction(unbounded=False), "all": GenerateLimitExtraction(0, 1, False) }, "default": { @@ -1951,7 +2068,7 @@ }, "mixed": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean }, @@ -1960,16 +2077,16 @@ "complexType": False } }, - + "name": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member name", NCName_model) } }, - + "namespace": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member namespace", URI_model), "any": GetNamespaces @@ -1981,14 +2098,14 @@ }, "nillable": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetBoolean }, }, - + "processContents": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member processContents", ["lax", "skip", "strict"]) }, @@ -1996,9 +2113,9 @@ "default": "strict" } }, - + "ref": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member ref", QName_model) } @@ -2010,44 +2127,44 @@ "default": GenerateModelNameExtraction("member refer", QName_model) } }, - + "schemaLocation": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member schemaLocation", URI_model) } }, - + "source": { "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member source", URI_model) } }, - + "substitutionGroup": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member substitutionGroup", QName_model) } }, "targetNamespace": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member targetNamespace", URI_model) } }, - + "type": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateModelNameExtraction("member type", QName_model) } }, "use": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GenerateEnumeratedExtraction("member usage", ["required", "optional", "prohibited"]) }, @@ -2057,7 +2174,7 @@ }, "value": { - "type": SYNTAXATTRIBUTE, + "type": SYNTAXATTRIBUTE, "extract": { "default": GetAttributeValue, "fractionDigits": GenerateIntegerExtraction(minInclusive=0), @@ -2077,23 +2194,23 @@ }, "xpath": { - "type": SYNTAXATTRIBUTE, - "extract": { -# "default": NotSupportedYet("xpath") + "type": SYNTAXATTRIBUTE, + "extract": { + # "default": NotSupportedYet("xpath") "default": GetAttributeValue } }, - -#------------------------------------------------------------------------------- -# Simple types definition -#------------------------------------------------------------------------------- + + # ------------------------------------------------------------------------------- + # Simple types definition + # ------------------------------------------------------------------------------- "string": { "type": SIMPLETYPE, "basename": "string", "extract": GetAttributeValue, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x : x), + "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, @@ -2103,61 +2220,61 @@ "basename": "normalizedString", "extract": GetNormalizedString, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x : x), + "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, "token": { "type": SIMPLETYPE, - "basename": "token", + "basename": "token", "extract": GetToken, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x : x), + "generate": GenerateSimpleTypeXMLText(lambda x: x), "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, - + "base64Binary": { - "type": SIMPLETYPE, - "basename": "base64Binary", + "type": SIMPLETYPE, + "basename": "base64Binary", "extract": NotSupportedYet("base64Binary"), "facets": STRING_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, (IntType, LongType)) }, - + "hexBinary": { "type": SIMPLETYPE, - "basename": "hexBinary", + "basename": "hexBinary", "extract": GetHexInteger, "facets": STRING_FACETS, - "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X"%x)/2.)*2))+"X")%x), + "generate": GenerateSimpleTypeXMLText(lambda x: ("%."+str(int(round(len("%X" % x)/2.)*2))+"X") % x), "initial": lambda: 0, "check": lambda x: isinstance(x, (IntType, LongType)) }, "integer": { "type": SIMPLETYPE, - "basename": "integer", + "basename": "integer", "extract": GenerateIntegerExtraction(), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "positiveInteger": { "type": SIMPLETYPE, - "basename": "positiveInteger", + "basename": "positiveInteger", "extract": GenerateIntegerExtraction(minExclusive=0), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 1, "check": lambda x: isinstance(x, IntType) }, - + "negativeInteger": { "type": SIMPLETYPE, "basename": "negativeInteger", @@ -2167,41 +2284,41 @@ "initial": lambda: -1, "check": lambda x: isinstance(x, IntType) }, - + "nonNegativeInteger": { - "type": SIMPLETYPE, - "basename": "nonNegativeInteger", + "type": SIMPLETYPE, + "basename": "nonNegativeInteger", "extract": GenerateIntegerExtraction(minInclusive=0), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "nonPositiveInteger": { "type": SIMPLETYPE, - "basename": "nonPositiveInteger", + "basename": "nonPositiveInteger", "extract": GenerateIntegerExtraction(maxInclusive=0), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "long": { "type": SIMPLETYPE, "basename": "long", - "extract": GenerateIntegerExtraction(minInclusive=-2**63,maxExclusive=2**63), + "extract": GenerateIntegerExtraction(minInclusive=-2**63, maxExclusive=2**63), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, "check": lambda x: isinstance(x, IntType) }, - + "unsignedLong": { "type": SIMPLETYPE, "basename": "unsignedLong", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**64), + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**64), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2211,7 +2328,7 @@ "int": { "type": SIMPLETYPE, "basename": "int", - "extract": GenerateIntegerExtraction(minInclusive=-2**31,maxExclusive=2**31), + "extract": GenerateIntegerExtraction(minInclusive=-2**31, maxExclusive=2**31), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2221,7 +2338,7 @@ "unsignedInt": { "type": SIMPLETYPE, "basename": "unsignedInt", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**32), + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**32), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2231,7 +2348,7 @@ "short": { "type": SIMPLETYPE, "basename": "short", - "extract": GenerateIntegerExtraction(minInclusive=-2**15,maxExclusive=2**15), + "extract": GenerateIntegerExtraction(minInclusive=-2**15, maxExclusive=2**15), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2240,8 +2357,8 @@ "unsignedShort": { "type": SIMPLETYPE, - "basename": "unsignedShort", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**16), + "basename": "unsignedShort", + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**16), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2251,7 +2368,7 @@ "byte": { "type": SIMPLETYPE, "basename": "byte", - "extract": GenerateIntegerExtraction(minInclusive=-2**7,maxExclusive=2**7), + "extract": GenerateIntegerExtraction(minInclusive=-2**7, maxExclusive=2**7), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2261,7 +2378,7 @@ "unsignedByte": { "type": SIMPLETYPE, "basename": "unsignedByte", - "extract": GenerateIntegerExtraction(minInclusive=0,maxExclusive=2**8), + "extract": GenerateIntegerExtraction(minInclusive=0, maxExclusive=2**8), "facets": DECIMAL_FACETS, "generate": GenerateSimpleTypeXMLText(str), "initial": lambda: 0, @@ -2285,7 +2402,7 @@ "facets": NUMBER_FACETS, "generate": GenerateFloatXMLText(["INF", "-INF", "NaN"]), "initial": lambda: 0., - "check": lambda x: {"INF" : True, "-INF" : True, "NaN" : True}.get(x, isinstance(x, (IntType, FloatType))) + "check": lambda x: {"INF": True, "-INF": True, "NaN": True}.get(x, isinstance(x, (IntType, FloatType))) }, "double": { @@ -2295,7 +2412,7 @@ "facets": NUMBER_FACETS, "generate": GenerateFloatXMLText(["INF", "-INF", "NaN"]), "initial": lambda: 0., - "check": lambda x: {"INF" : True, "-INF" : True, "NaN" : True}.get(x, isinstance(x, (IntType, FloatType))) + "check": lambda x: {"INF": True, "-INF": True, "NaN": True}.get(x, isinstance(x, (IntType, FloatType))) }, "boolean": { @@ -2303,10 +2420,10 @@ "basename": "boolean", "extract": GetBoolean, "facets": GenerateDictFacets(["pattern", "whiteSpace"]), - "generate": GenerateSimpleTypeXMLText(lambda x:{True : "true", False : "false"}[x]), + "generate": GenerateSimpleTypeXMLText(lambda x: {True: "true", False: "false"}[x]), "initial": lambda: False, "check": lambda x: isinstance(x, BooleanType) - }, + }, "duration": { "type": SIMPLETYPE, @@ -2324,7 +2441,7 @@ "extract": GetDateTime, "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(datetime.datetime.isoformat), - "initial": lambda: datetime.datetime(1,1,1,0,0,0,0), + "initial": lambda: datetime.datetime(1, 1, 1, 0, 0, 0, 0), "check": lambda x: isinstance(x, datetime.datetime) }, @@ -2334,17 +2451,17 @@ "extract": GetDate, "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(datetime.date.isoformat), - "initial": lambda: datetime.date(1,1,1), + "initial": lambda: datetime.date(1, 1, 1), "check": lambda x: isinstance(x, datetime.date) }, - + "time": { "type": SIMPLETYPE, "basename": "time", "extract": GetTime, "facets": NUMBER_FACETS, "generate": GenerateSimpleTypeXMLText(datetime.time.isoformat), - "initial": lambda: datetime.time(0,0,0,0), + "initial": lambda: datetime.time(0, 0, 0, 0), "check": lambda x: isinstance(x, datetime.time) }, @@ -2407,7 +2524,7 @@ "initial": lambda: "", "check": lambda x: isinstance(x, (StringType, UnicodeType)) }, - + "QName": { "type": SIMPLETYPE, "basename": "QName", @@ -2529,6 +2646,5 @@ }, # Complex Types - "anyType": {"type": COMPLEXTYPE, "extract": lambda x:None}, + "anyType": {"type": COMPLEXTYPE, "extract": lambda x: None}, } -