Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/changelog.d/37.miscellaneous.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix: enhance robustness
19 changes: 12 additions & 7 deletions src/ansys/scade/python_wrapper/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@

"""Provides the Ansys SCADE Python Wrapper's Settings page."""

from typing import List
from collections.abc import Callable
from typing import Any, List, Tuple

import scade
from scade.model.project.stdproject import Configuration, Project
Expand Down Expand Up @@ -88,24 +89,28 @@ class PageUtils:
"""Utilities for settings pages."""

def __init__(self):
scade.output('initialized controls\n')
# scade is a CPython module defined dynamically
scade.output('initialized controls\n') # type: ignore
# self.controls = []

def add_edit(self, y: int, text: str) -> EditBox:
"""Add an edit box."""
edit = LabelEditBox(self, text, wl1, x=xl1, y=y, w=wl1 + wf1)
self.controls.append(edit)
# self.controls is defined in sub-class
self.controls.append(edit) # type: ignore
return edit

def add_cb(self, y: int, text: str) -> CheckBox:
"""Add a check box."""
cb = CheckBoxEx(self, text, x=xl1, y=y, w=wl1 + wf1)
self.controls.append(cb)
# self.controls is defined in sub-class
self.controls.append(cb) # type: ignore
return cb

def layout_controls(self):
"""Layout the controls."""
for control in self.controls:
# self.controls is defined in sub-class
for control in self.controls: # type: ignore
control.on_layout()


Expand All @@ -118,7 +123,7 @@ def __init__(self, *args):
# runtime properties
self.controls = []
# get, set, prop, prop_default
self.properties: List[callable, callable, str, str] = []
self.properties: List[Tuple[Callable[[], Any], Callable[[Any], None], Any, Any]] = []

def on_layout(self):
"""Layout the page."""
Expand Down Expand Up @@ -186,7 +191,7 @@ def on_build(self):
# remove the option, requirements to be refined
# self.cb_pep8 = self.add_cb(y, '&Apply PEP8 naming rules'); y += dy

self.properties = [
self.properties = [ # type: ignore # set/get functions have incorrect type annotations
(
self.ed_module.get_name,
self.ed_module.set_name,
Expand Down
38 changes: 24 additions & 14 deletions src/ansys/scade/python_wrapper/kcg_data_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@

"""Intermediate model for Python proxy for SCADE standalone DLL."""

from typing import List, Optional, Tuple

import scade.code.suite.mapping.c as c
import scade.code.suite.mapping.model as m

Expand All @@ -42,7 +44,7 @@ def parse_from_kcg_mapping(mf) -> data.Model:
return model


def _build_sensor(model: data.Model, m_sensor: m.Sensor) -> data.Feature:
def _build_sensor(model: data.Model, m_sensor: m.Sensor) -> Optional[data.Feature]:
c_sensor = m_sensor.get_generated()
c_type = c_sensor.get_type()
if not c_sensor:
Expand All @@ -58,13 +60,15 @@ def _build_sensor(model: data.Model, m_sensor: m.Sensor) -> data.Feature:
return sensor


def _build_type(model: data.Model, c_type: c.Type):
def _build_type(model: data.Model, c_type: c.Type) -> Tuple[List[int], data.Type]:
"""Return a tuple list<size>, <type>."""
if not c_type:
return [], None
elif c_type.is_typedef():
assert isinstance(c_type, c.TypeDef) # nosec B101 # addresses linter
return _build_type(model, c_type.get_aliased_type())
elif c_type.is_array():
assert isinstance(c_type, c.Array) # nosec B101 # addresses linter
sizes, type_ = _build_type(model, c_type.get_base_type())
return sizes + [c_type.get_size()], type_

Expand All @@ -75,6 +79,7 @@ def _build_type(model: data.Model, c_type: c.Type):
# extract model name from kcg_<name>
type_ = data.Scalar(m_name=c_name.split('_')[-1], c_name=c_name)
elif c_type.is_struct():
assert isinstance(c_type, c.Struct) # nosec B101 # addresses linter
type_ = data.Structure(
m_name=c_type.get_model().get_name() if c_type.get_model() else '',
c_name=c_name,
Expand All @@ -88,7 +93,7 @@ def _build_type(model: data.Model, c_type: c.Type):
else:
c_fields = [_ for _ in c_type.get_fields()]
for c_field in c_fields:
assert c_field.get_model()
assert c_field.get_model() # nosec B101 # addresses linter
c_field_type = c_field.get_type()
field = data.Feature(
m_name=c_field.get_model().get_name(),
Expand All @@ -98,17 +103,18 @@ def _build_type(model: data.Model, c_type: c.Type):
field.sizes, field.type = _build_type(model, c_field_type)
type_.add_field(field)
model.map_item(c_field, field)
elif c_type.is_enum():
else:
assert c_type.is_enum() # nosec B101 # addresses linter
type_ = data.Scalar(
m_name='int32',
c_name='kcg_int32',
path=c_type.get_model().get_scade_path(),
)
else:
assert False

model.add_type(type_)
model.map_item(c_type, type_)
else:
assert isinstance(type_, data.Type) # nosec B101 # addresses linter
return [], type_


Expand All @@ -123,6 +129,7 @@ def _build_operator(model: data.Model, m_op: m.Operator):

# functions
op.set_cycle(data.Function(c_name=c_op.get_cycle().get_name()))
assert op.cycle is not None # nosec B101 # addresses linter
pointers = {_.get_name() for _ in c_op.get_cycle().get_parameters() if _.is_pointer()}
if c_op.get_init():
op.set_init(data.Function(c_name=c_op.get_init().get_name()))
Expand All @@ -133,24 +140,30 @@ def _build_operator(model: data.Model, m_op: m.Operator):
# _add_c_type(model, c_op.get_state_vector())
_, type_ = _build_type(model, c_op.get_input_struct())
if type_:
# must be a structure
assert isinstance(type_, data.Structure) # nosec B101 # addresses linter
# no names
op.set_in_context(data.Context(kind=data.CK.INPUT))
assert op.in_context is not None # nosec B101 # addresses linter
op.in_context.link_type(type_)
op.in_context.c_type = c_op.get_input_struct().get_name()
op.in_context.pointer = True
# TODO?
# _add_c_type(model, c_op.get_output_struct())
_, type_ = _build_type(model, c_op.get_context())
if type_:
# must be a structure
assert isinstance(type_, data.Structure) # nosec B101 # addresses linter
# no names
op.set_context(data.Context(kind=data.CK.CONTEXT))
assert op.context is not None # nosec B101 # addresses linter
op.context.link_type(type_)
op.context.c_type = c_op.get_context().get_name()
op.context.pointer = True
# if assertions fail, remove shortcomings in the implementation
assert len(c_op.get_init().get_parameters()) == 1
# if there is a context, init and op functions must exist
assert op.init is not None # nosec B101 # addresses linter
assert op.reset is not None # nosec B101 # addresses linter
op.init.add_parameter(op.context)
assert len(c_op.get_reset().get_parameters()) == 1
op.reset.add_parameter(op.context)

for m_input in m_op.get_inputs():
Expand All @@ -168,10 +181,9 @@ def _build_operator(model: data.Model, m_op: m.Operator):
io.sizes, io.type = _build_type(model, c_type)
op.add_io(io)
if isinstance(c_input, c.Parameter):
assert op.cycle
op.cycle.add_parameter(io)
else:
assert op.in_context
assert op.in_context is not None # nosec B101 # addresses linter
op.in_context.add_io(io)
model.map_item(c_input, io)

Expand All @@ -190,10 +202,9 @@ def _build_operator(model: data.Model, m_op: m.Operator):
)
io.sizes, io.type = _build_type(model, c_type)
if isinstance(c_output, c.Parameter):
assert op.cycle
op.cycle.add_parameter(io)
else:
assert op.context
assert op.context is not None # nosec B101 # addresses linter
op.context.add_io(io)
model.map_item(c_output, io)
else:
Expand All @@ -214,7 +225,6 @@ def _build_operator(model: data.Model, m_op: m.Operator):

# update the parameters of cycle w.r.t. the contexts
if op.in_context:
assert not op.cycle.parameters
op.cycle.add_parameter(op.in_context)
if op.context:
op.cycle.add_parameter(op.context)
Expand Down
4 changes: 2 additions & 2 deletions src/ansys/scade/python_wrapper/kcgpython.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
from typing import Optional

from scade.code.suite.mapping.c import MappingFile
import scade.code.suite.sctoc as sctoc
import scade.code.suite.sctoc as sctoc # type: ignore # CPython module defined dynamically
from scade.code.suite.wrapgen.c import InterfacePrinter
from scade.code.suite.wrapgen.model import MappingHelpers
from scade.model.project.stdproject import Configuration, Project
Expand Down Expand Up @@ -324,7 +324,7 @@ def _generate_cosim(
f.write('_project = "%s"\n' % Path(project.pathname).as_posix())
f.write('_configuration = "Simulation"\n')
# take the first root
assert wux2.mf
assert wux2.mf is not None # nosec B101 # addresses linter
root = wux2.mf.get_root_operators()[0].get_scade_path().strip('/')
f.write('_root = "%s"\n' % root)
port = project.get_scalar_tool_prop_def('SSM', 'PROXYLISTENPORT', '64064', None)
Expand Down
28 changes: 14 additions & 14 deletions src/ansys/scade/python_wrapper/lib/sdyproxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,40 +39,40 @@ def __init__(self, lib, basename: str, layer_types: List[Tuple[str, SdyLayer]]):
self._lib = lib
# self._lib.py_load_sdy_dlls()

# predefined interface
for name in ['init', 'draw', 'lockio', 'unlockio', 'cancelled']:
exec('self._{0} = self._lib.{1}__{0}'.format(name, basename))
exec('self._{0}.argtypes = []'.format(name))
exec('self._{0}.restype = ctypes.c_int'.format(name))
layer_fct = getattr(self._lib, f'{basename}__{name}')
layer_fct.argtypes = []
layer_fct.restype = ctypes.c_int
setattr(self, f'_{name}', layer_fct)
self.init()
# layer functions
for layer_name, layer_type in layer_types:
layer_fct = eval('self._lib.{0}_L_{1}'.format(basename, layer_name))
layer_fct = getattr(self._lib, f'{basename}_L_{layer_name}')
layer_fct.argtypes = []
layer_fct.restype = ctypes.c_void_p
exec(
'self.{0} = layer_type.from_address(layer_fct())'.format(
layer_name,
)
)
setattr(self, f'{layer_name}', layer_type.from_address(layer_fct()))

def init(self) -> int:
"""Call DLL's ``init`` function."""
return self._init()
return self._init() # type: ignore # method added dynamically
# return self._init()

def draw(self) -> int:
"""Call DLL's ``draw`` function."""
return self._draw()
return self._draw() # type: ignore # method added dynamically

def lockio(self) -> int:
"""Call DLL's ``lockio`` function."""
return self._lockio()
return self._lockio() # type: ignore # method added dynamically

def unlockio(self) -> int:
"""Call DLL's ``unlockio`` function."""
return self._unlockio()
return self._unlockio() # type: ignore # method added dynamically

def cancelled(self) -> bool:
"""Call DLL's ``cancelled`` function."""
return self._cancelled() != 0
return self._cancelled() != 0 # type: ignore # method added dynamically

# def __del__(self):
# self._lib.py_unload_sdy_dlls()
22 changes: 15 additions & 7 deletions src/ansys/scade/python_wrapper/props.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

"""Defines the properties used for the settings."""

from typing import List
from typing import List, Optional

from scade.model.project.stdproject import Annotable, Configuration

Expand Down Expand Up @@ -50,21 +50,21 @@


def get_tool_prop(
object: Annotable, name: str, default: List[str], configuration: Configuration = None
object: Annotable, name: str, default: List[str], configuration: Optional[Configuration] = None
) -> List[str]:
"""Get the values of a property for the current tool."""
return object.get_tool_prop_def(_TOOL, name, default, configuration)


def get_scalar_tool_prop(
object: Annotable, name: str, default: str, configuration: Configuration = None
object: Annotable, name: str, default: str, configuration: Optional[Configuration] = None
) -> str:
"""Get the value of a scalar property for the current tool."""
return object.get_scalar_tool_prop_def(_TOOL, name, default, configuration)


def get_bool_tool_prop(
object: Annotable, name: str, default: bool, configuration: Configuration = None
object: Annotable, name: str, default: bool, configuration: Optional[Configuration] = None
) -> bool:
"""Get the bool value of a property for the current tool."""
return object.get_bool_tool_prop_def(_TOOL, name, default, configuration)
Expand All @@ -75,21 +75,29 @@ def set_tool_prop(
name: str,
values: List[str],
default: List[str],
configuration: Configuration = None,
configuration: Optional[Configuration] = None,
):
"""Set the values of a property for the current tool."""
object.set_tool_prop_def(_TOOL, name, values, default, configuration)


def set_scalar_tool_prop(
object: Annotable, name: str, value: str, default: str, configuration: Configuration = None
object: Annotable,
name: str,
value: str,
default: str,
configuration: Optional[Configuration] = None,
):
"""Get the scalar value of a property for the current tool."""
object.set_scalar_tool_prop_def(_TOOL, name, value, default, configuration)


def set_bool_tool_prop(
object: Annotable, name: str, value: bool, default: bool, configuration: Configuration = None
object: Annotable,
name: str,
value: bool,
default: bool,
configuration: Optional[Configuration] = None,
):
"""Set the bool value of a property for the current tool."""
object.set_bool_tool_prop_def(_TOOL, name, value, default, configuration)
Loading
Loading