dialogs/FBDBlockDialog.py
author Andrey Skvortsov <andrej.skvortzov@gmail.com>
Sun, 06 Jan 2019 03:11:39 +0300
changeset 2501 eba2bbb2dd9a
parent 1853 47a3f39bead0
child 2587 c67488bd8134
permissions -rw-r--r--
Make online debug optional

It could be useful for very small targets like Atmega (Arduino) and
for target bring-up there developer want to have running PLC program,
but has not implemented runtime communication yet.


TARGET_DEBUG_AND_RETAIN_DISABLE - completely disable debug and retain
functionality. Previously named TARGET_DEBUG_DISABLE.

TARGET_ONLINE_DEBUG_DISABLE - can be used to enable retain
functionality (no define TARGET_DEBUG_AND_RETAIN_DISABLE is used), but disable
online debug with corresponding RAM/FLASH overhead.

TARGET_LOGGING_DISABLE - disables logging functionality from runtime and PLC program

TARGET_EXT_SYNC_DISABLE - disables PLC program synchronization with
external events. For example, it could be used to synchronize several
PLCs that control motors for different axes.

By default all these options are off.

To test generate program for Generic target, put following files in
project files directory and run build.sh after generating PLC program.
This is very easy to integrate into makefile (Generic target).

[------------- build.sh --------------------------]
files=$(find $PWD/../build -iname '*.c' | grep -v POUS.c)
arm-none-eabi-gcc \
-DTARGET_DEBUG_AND_RETAIN_DISABLE \
-DTARGET_ONLINE_DEBUG_DISABLE \
-DTARGET_LOGGING_DISABLE \
-DTARGET_EXT_SYNC_DISABLE \
-flto -ffunction-sections -fdata-sections -I../../../../matiec/lib/C \
$files \
main.c \
-Wl,--Map=./program.map,--cref \
-nodefaultlibs --specs=nano.specs -Wl,--static -Wl,--gc-section -Wl,--start-group -lc -lm -lnosys -lgcc -Wl,--end-group
[------------------------------------------------]

[------------- main.c --------------------------]
#ifndef TARGET_DEBUG_AND_RETAIN_DISABLE
void Retain(void){}
void InValidateRetainBuffer(void){}
void ValidateRetainBuffer(void){}
#endif

extern void __run(void);
int main(void)
{
for(;;) {
__run();
// sleep common_ticktime__ ns
// add common_ticktime__ ns to __CURRENT_TIME
}
return 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) 2007: Edouard TISSERANT and Laurent BESSARD
# Copyright (C) 2017: Andrey Skvortsov <andrej.skvortzov@gmail.com>
#
# 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

import wx

from graphics.FBD_Objects import FBD_Block
from controls.LibraryPanel import LibraryPanel
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 FBDBlockDialog(BlockPreviewDialog):
    """
    Class that implements a dialog for defining parameters of a FBD block graphic
    element
    """

    def __init__(self, parent, controller, tagname):
        """
        Constructor
        @param parent: Parent wx.Window of dialog for modal
        @param controller: Reference to project controller
        @param tagname: Tagname of project POU edited
        """
        BlockPreviewDialog.__init__(self, parent, controller, tagname,
                                    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)

        # 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)

        # 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)

        # Create spin control for defining extended block input number
        self.Inputs = wx.SpinCtrl(self, min=2, max=20,
                                  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)

        # 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.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)

        # Create check box to enable block execution control
        self.ExecutionControl = wx.CheckBox(self)
        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)

        # Dictionary containing correspondence between parameter exchanged and
        # control to fill with parameter value
        self.ParamsControl = {
            "extension": self.Inputs,
            "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
        @param values: Block parameters values
        """
        # Extract block type defined in parameters
        blocktype = values.get("type", None)

        # Select block type in library panel
        if blocktype is not None:
            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()

    def GetValues(self):
        """
        Return block parameters defined in dialog
        @return: {parameter_name: parameter_value,...}
        """
        values = self.LibraryPanel.GetSelectedBlock()
        if self.BlockName.IsEnabled() and self.BlockName.GetValue() != "":
            values["name"] = self.BlockName.GetValue()
        values["width"], values["height"] = self.Element.GetSize()
        values.update({
            name: control.GetValue()
            for name, control in self.ParamsControl.iteritems()})
        return values

    def OnOK(self, event):
        """
        Called when dialog OK button is pressed
        Test if parameters defined are valid
        @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
        @param event: wx.TreeEvent
        """
        # Get type selected in library panel
        values = self.LibraryPanel.GetSelectedBlock()

        # Get block type informations
        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"]))
            self.Inputs.Enable(blocktype["extensible"])
        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
                # block type
                default_name_model = GetBlockTypeDefaultNameModel(values["type"])
                block_name = (
                    self.DefaultElementName
                    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
        @param event: wx.TextEvent
        """
        if self.BlockName.IsEnabled():
            # Save block name typed by user
            self.CurrentBlockName = self.BlockName.GetValue()
            self.RefreshPreview()
        event.Skip()

    def OnInputsChanged(self, event):
        """
        Called when block inputs number changed
        @param event: wx.SpinEvent
        """
        if self.Inputs.IsEnabled():
            self.RefreshPreview()
        event.Skip()

    def OnExecutionOrderChanged(self, event):
        """
        Called when block execution order value changed
        @param event: wx.SpinEvent
        """
        self.RefreshPreview()
        event.Skip()

    def OnExecutionControlChanged(self, event):
        """
        Called when block execution control value changed
        @param event: wx.SpinEvent
        """
        self.RefreshPreview()
        event.Skip()

    def RefreshPreview(self):
        """
        Refresh preview panel of graphic element
        Override BlockPreviewDialog function
        """
        # 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())

        # Reset graphic element displayed
        else:
            self.Element = None

        # Call BlockPreviewDialog function
        BlockPreviewDialog.RefreshPreview(self)