Commit a4f5e213 authored by Michael Büsch's avatar Michael Büsch

Add basic AWL-library

Signed-off-by: Michael Büsch's avatarMichael Buesch <[email protected]>
parent b65214ce
......@@ -2,11 +2,11 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import awlsim.cython_helper as __cython
if __cython.shouldUseCython(): #@nocy
if __cython.shouldUseCython(__name__): #@nocy
#if True: #@cy
try:
from awlsim_cython.common.all_modules import * #<no-cython-patch
except ImportError as e:
__cython.cythonImportError("common", str(e))
if not __cython.shouldUseCython(): #@nocy
__cython.cythonImportError(__name__, str(e))
if not __cython.shouldUseCython(__name__): #@nocy
from awlsim.common.all_modules import * #@nocy
# -*- coding: utf-8 -*-
#
# AWL simulator - Python library importer
#
# Copyright 2014 Michael Buesch <[email protected]>
#
# 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 division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *
def importModule(moduleName):
"""Import a module with the name string 'moduleName'.
Returns the module object.
May raise importError."""
import awlsim.cython_helper as cython_helper
import importlib
if cython_helper.shouldUseCython(moduleName):
moduleName = cython_helper.cythonModuleName(moduleName)
return importlib.import_module(moduleName)
......@@ -2,11 +2,11 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import awlsim.cython_helper as __cython
if __cython.shouldUseCython(): #@nocy
if __cython.shouldUseCython(__name__): #@nocy
#if True: #@cy
try:
from awlsim_cython.core.all_modules import * #<no-cython-patch
except ImportError as e:
__cython.cythonImportError("core", str(e))
if not __cython.shouldUseCython(): #@nocy
__cython.cythonImportError(__name__, str(e))
if not __cython.shouldUseCython(__name__): #@nocy
from awlsim.core.all_modules import * #@nocy
......@@ -403,6 +403,7 @@ class CodeBlock(Block):
isFC = False
isFB = False
isSystemBlock = False
isLibraryBlock = False
def __init__(self, insns, index, interface):
Block.__init__(self, index)
......@@ -418,6 +419,48 @@ class CodeBlock(Block):
def __repr__(self):
return "CodeBlock %d" % self.index
class StaticCodeBlock(CodeBlock):
"""Base class for static code blocks. (system and library blocks)."""
# Static interface definition.
# To be overridden by the subclass.
interfaceFields = {
BlockInterfaceField.FTYPE_IN : (),
BlockInterfaceField.FTYPE_OUT : (),
BlockInterfaceField.FTYPE_INOUT : (),
BlockInterfaceField.FTYPE_STAT : (),
}
# Set to True by the subclass, if the implementation is incomplete.
broken = False
def __init__(self, insns, index, interface):
CodeBlock.__init__(self, insns, index, interface)
# Register the interface.
for ftype in (BlockInterfaceField.FTYPE_IN,
BlockInterfaceField.FTYPE_OUT,
BlockInterfaceField.FTYPE_INOUT,
BlockInterfaceField.FTYPE_STAT):
try:
fields = self.interfaceFields[ftype]
except KeyError:
continue
for field in fields:
if ftype == BlockInterfaceField.FTYPE_IN:
self.interface.addField_IN(field)
elif ftype == BlockInterfaceField.FTYPE_OUT:
self.interface.addField_OUT(field)
elif ftype == BlockInterfaceField.FTYPE_INOUT:
self.interface.addField_INOUT(field)
elif ftype == BlockInterfaceField.FTYPE_STAT:
self.interface.addField_STAT(field)
else:
assert(0)
def __repr__(self):
return "StaticCodeBlock %d" % self.index
class OBInterface(BlockInterface):
startupTempAllocation = 20
......
......@@ -32,6 +32,9 @@ import random
#from awlsim.core.instructions.all_insns cimport * #@cy
from awlsim.common.cpuspecs import *
from awlsim.library import *
from awlsim.core.parser import *
from awlsim.core.symbolparser import *
from awlsim.core.datatypes import *
......@@ -360,6 +363,10 @@ class S7CPU(object): #+cdef
db = translator.translateDB(rawDB)
self.dbs[dbNumber] = db
def loadLibraryBlock(self, libraryName, fcBlock,
blockIndex, effectiveBlockIndex):
pass#TODO
def loadSymbolTable(self, symbolTable):
self.symbolTable.merge(symbolTable)
......
......@@ -23,14 +23,13 @@ from __future__ import division, absolute_import, print_function, unicode_litera
from awlsim.common.compat import *
from awlsim.common.project import *
from awlsim.common.dynamic_import import *
from awlsim.core.util import *
from awlsim.core.parser import *
from awlsim.core.cpu import *
from awlsim.core.hardware import *
import importlib
class AwlSim(object):
def __init__(self, profileLevel=0):
......@@ -206,19 +205,12 @@ class AwlSim(object):
'name' is the name of the module to load (without 'awlsimhw_' prefix).
Returns the HardwareInterface class."""
import awlsim.cython_helper as cython_helper
# Construct the python module name
moduleName = "awlsimhw_%s" % name
if cython_helper.shouldUseCython():
cyModuleName = "awlsimhw_%s_cython" % name
printInfo("Awlsim-cython: Loading '%s' instead of '%s'" %\
(cyModuleName, moduleName))
moduleName = cyModuleName
# Try to import the module
try:
mod = importlib.import_module(moduleName)
mod = importModule(moduleName)
except ImportError as e:
raise AwlSimError("Failed to import hardware interface "
"module '%s' (import name '%s'): %s" %\
......
......@@ -29,52 +29,20 @@ from awlsim.core.blocks import *
from awlsim.core.translator import *
class SystemBlock(CodeBlock):
class SystemBlock(StaticCodeBlock):
# The block identification. To be overridden by the subclass.
# The tuple is: (number, name, short_description)
name = (-1, "<unknown>", None)
# Interface fields. To be overridden by the subclass.
interfaceFields = {
BlockInterfaceField.FTYPE_IN : (),
BlockInterfaceField.FTYPE_OUT : (),
BlockInterfaceField.FTYPE_INOUT : (),
BlockInterfaceField.FTYPE_STAT : (),
}
# Set to True by the subclass, if the implementation is incomplete.
broken = False
isSystemBlock = True
def __init__(self, cpu, interface):
insns = [
AwlInsn_GENERIC_CALL(cpu, self.run),
]
CodeBlock.__init__(self, insns, self.name[0], interface)
StaticCodeBlock.__init__(self, insns, self.name[0], interface)
self.cpu = cpu
# Register the interface.
for ftype in (BlockInterfaceField.FTYPE_IN,
BlockInterfaceField.FTYPE_OUT,
BlockInterfaceField.FTYPE_INOUT,
BlockInterfaceField.FTYPE_STAT):
try:
fields = self.interfaceFields[ftype]
except KeyError:
continue
for field in fields:
if ftype == BlockInterfaceField.FTYPE_IN:
self.interface.addField_IN(field)
elif ftype == BlockInterfaceField.FTYPE_OUT:
self.interface.addField_OUT(field)
elif ftype == BlockInterfaceField.FTYPE_INOUT:
self.interface.addField_INOUT(field)
elif ftype == BlockInterfaceField.FTYPE_STAT:
self.interface.addField_STAT(field)
else:
assert(0)
def run(self):
# Reimplement this method
raise NotImplementedError
......
......@@ -2,11 +2,11 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import awlsim.cython_helper as __cython
if __cython.shouldUseCython(): #@nocy
if __cython.shouldUseCython(__name__): #@nocy
#if True: #@cy
try:
from awlsim_cython.coreclient.all_modules import * #<no-cython-patch
except ImportError as e:
__cython.cythonImportError("coreclient", str(e))
if not __cython.shouldUseCython(): #@nocy
__cython.cythonImportError(__name__, str(e))
if not __cython.shouldUseCython(__name__): #@nocy
from awlsim.coreclient.all_modules import * #@nocy
......@@ -2,11 +2,11 @@ from __future__ import division, absolute_import, print_function, unicode_litera
import awlsim.cython_helper as __cython
if __cython.shouldUseCython(): #@nocy
if __cython.shouldUseCython(__name__): #@nocy
#if True: #@cy
try:
from awlsim_cython.coreserver.all_modules import * #<no-cython-patch
except ImportError as e:
__cython.cythonImportError("coreserver", str(e))
if not __cython.shouldUseCython(): #@nocy
__cython.cythonImportError(__name__, str(e))
if not __cython.shouldUseCython(__name__): #@nocy
from awlsim.coreserver.all_modules import * #@nocy
......@@ -3,9 +3,15 @@ from __future__ import division, absolute_import, print_function, unicode_litera
__USE_CYTHON_NO = 0 # Do not use Cython.
__USE_CYTHON_TRY = 1 # Try to use Cython, if available.
__USE_CYTHON_FORCE = 2 # Use Cython. Abort if not available.
__USE_CYTHON_VERBOSE = 3 # Use Cython. Abort if not available. Be verbose.
__useCython = None
def shouldUseCython():
def cythonModuleName(modname):
elems = modname.split(".")
elems[0] = elems[0] + "_cython"
return ".".join(elems)
def __checkCython():
global __useCython
import os
......@@ -15,11 +21,18 @@ def shouldUseCython():
str(__USE_CYTHON_NO)))
if __useCython not in (__USE_CYTHON_NO,
__USE_CYTHON_TRY,
__USE_CYTHON_FORCE):
__USE_CYTHON_FORCE,
__USE_CYTHON_VERBOSE):
raise ValueError
except ValueError:
__useCython = __USE_CYTHON_NO
return __useCython
def shouldUseCython(modname=None):
__checkCython()
if __useCython == __USE_CYTHON_VERBOSE and modname:
print("Awlsim-cython: Importing '%s' instead of '%s'" %\
(cythonModuleName(modname), modname))
return __useCython != __USE_CYTHON_NO
def cythonImportError(modname, message):
global __useCython
......@@ -31,7 +44,7 @@ def cythonImportError(modname, message):
sys.stderr.write("--> Falling back to standard Python modules...\n")
sys.stderr.flush()
__useCython = False
elif __useCython == __USE_CYTHON_FORCE:
elif __useCython in (__USE_CYTHON_FORCE, __USE_CYTHON_VERBOSE):
sys.stderr.write("ERROR: Failed to import awlsim CYTHON module '%s': "
"%s\n" % (modname, message))
sys.stderr.write("Aborting.\n")
......
......@@ -27,6 +27,7 @@ from awlsim.gui.icons import *
from awlsim.core.systemblocks.system_sfc import *
from awlsim.core.systemblocks.system_sfb import *
from awlsim.library import *
class GenericActionWidget(QWidget):
......@@ -61,6 +62,29 @@ class GenericActionWidget(QWidget):
ret.append("")
self.paste.emit("\n".join(ret))
def _blockToInterfaceText(self, blockIdent, blockSym,
verboseText,
interfaceFields):
desc = [ "%s \"%s\"" % (blockIdent, blockSym) ]
if verboseText:
desc.append(verboseText)
desc.append("")
for ftype, fname in ((BlockInterfaceField.FTYPE_IN, "VAR_INPUT"),
(BlockInterfaceField.FTYPE_OUT, "VAR_OUTPUT"),
(BlockInterfaceField.FTYPE_INOUT, "VAR_IN_OUT")):
try:
fields = interfaceFields[ftype]
except KeyError:
continue
if not fields:
continue
desc.append(" " + fname)
for field in fields:
field.fieldType = ftype
desc.append(" %s : %s;" % (field.name,
str(field.dataType)))
return "\n".join(desc)
def defaultPaste(self):
pass
......@@ -77,35 +101,26 @@ class SysActionWidget(GenericActionWidget):
self.desc.setFont(font)
self.layout().addWidget(self.desc, 0, 0)
self.layout().setRowStretch(1, 1)
self.pasteCallButton = QPushButton(self)
self.layout().addWidget(self.pasteCallButton, 1, 0)
self.layout().addWidget(self.pasteCallButton, 2, 0)
self.pasteCallSymButton = QPushButton(self)
self.layout().addWidget(self.pasteCallSymButton, 2, 0)
self.layout().addWidget(self.pasteCallSymButton, 3, 0)
self.pasteCallButton.released.connect(self.__pasteCall)
self.pasteCallSymButton.released.connect(self.__pasteCallSym)
def update(self, prefix, systemBlockCls):
def updateData(self, prefix, systemBlockCls):
self.systemBlockCls = systemBlockCls
self.blockPrefix = prefix
blockNumber, blockName, blockDesc = systemBlockCls.name
desc = [ "%s %d \"%s\"" % (prefix, blockNumber, blockName) ]
for ftype, fname in ((BlockInterfaceField.FTYPE_IN, "VAR_INPUT"),
(BlockInterfaceField.FTYPE_OUT, "VAR_OUTPUT"),
(BlockInterfaceField.FTYPE_INOUT, "VAR_IN_OUT")):
try:
fields = systemBlockCls.interfaceFields[ftype]
except KeyError:
continue
if not fields:
continue
desc.append(" " + fname)
for field in fields:
field.fieldType = ftype
desc.append(" %s : %s;" % (field.name, str(field.dataType)))
self.desc.setText("\n".join(desc))
desc = self._blockToInterfaceText("%s %d" % (prefix, blockNumber),
blockName, blockDesc,
systemBlockCls.interfaceFields)
self.desc.setText(desc)
self.pasteCallButton.setText("Paste CALL %s %d" %\
(prefix, blockNumber))
self.pasteCallSymButton.setText("Paste CALL \"%s\"" % blockName)
......@@ -128,9 +143,50 @@ class SysActionWidget(GenericActionWidget):
def defaultPaste(self):
self.__pasteCall()
class LibActionWidget(GenericActionWidget):
def __init__(self, parent=None):
GenericActionWidget.__init__(self, parent)
self.libEntryCls = None
self.desc = QLabel(self)
font = self.desc.font()
setFixedFontParams(font)
self.desc.setFont(font)
self.layout().addWidget(self.desc, 0, 0)
self.layout().setRowStretch(1, 1)
self.pasteCodeButton = QPushButton(self)
self.layout().addWidget(self.pasteCodeButton, 2, 0)
self.pasteCodeButton.released.connect(self.__pasteCode)
def updateData(self, libEntryCls):
self.libEntryCls = libEntryCls
prefix = "FC" if libEntryCls.isFC else "FB"
blockName = "%s %d" % (prefix, libEntryCls.staticIndex)
desc = self._blockToInterfaceText(blockName,
libEntryCls.symbolName,
libEntryCls.description,
libEntryCls.interfaceFields)
self.desc.setText(desc)
self.pasteCodeButton.setText("Paste %s body" % blockName)
def __pasteCode(self):
self.paste.emit(self.libEntryCls().getCode())
self.finish.emit()
def defaultPaste(self):
pass#TODO
class LibraryDialog(QDialog):
ITEM_SFC = QListWidgetItem.UserType + 0
ITEM_SFB = QListWidgetItem.UserType + 1
ITEM_LIB_BASE = QListWidgetItem.UserType + 100
BLOCK_OFFSET = QListWidgetItem.UserType + 0xFFFF
......@@ -143,17 +199,28 @@ class LibraryDialog(QDialog):
self.withExtensions = withExtensions
self.pasteText = None
self.currentActionWidget = None
self.__nr2lib = {}
self.__nr2entry = {}
self.libList = QListWidget(self)
QListWidgetItem("System functions (SFC)", self.libList, self.ITEM_SFC)
QListWidgetItem("System function blocks (SFB)", self.libList, self.ITEM_SFB)
self.layout().addWidget(self.libList, 0, 0, 2, 1)
for i, libName in enumerate(("IEC",)):
try:
lib = AwlLib.getByName(libName)
except AwlSimError as e:
MessageBox.handleAwlSimError(self, "Library error", e)
continue
self.__nr2lib[self.ITEM_LIB_BASE + i] = lib
QListWidgetItem(lib.description, self.libList,
self.ITEM_LIB_BASE + i)
self.layout().addWidget(self.libList, 0, 0, 3, 1)
self.libElemList = QListWidget(self)
font = self.libElemList.font()
setFixedFontParams(font)
self.libElemList.setFont(font)
self.layout().addWidget(self.libElemList, 0, 1, 2, 1)
self.layout().addWidget(self.libElemList, 0, 1, 3, 1)
self.iconLabel = QLabel(self)
self.iconLabel.setAlignment(Qt.AlignHCenter | Qt.AlignTop)
......@@ -164,11 +231,17 @@ class LibraryDialog(QDialog):
self.sysAction.hide()
self.layout().addWidget(self.sysAction, 1, 2)
self.libAction = LibActionWidget(self)
self.libAction.hide()
self.layout().addWidget(self.libAction, 2, 2)
self.libList.currentItemChanged.connect(self.__libItemChanged)
self.libElemList.currentItemChanged.connect(self.__libElemItemChanged)
self.libElemList.itemDoubleClicked.connect(self.__libElemDoubleClicked)
self.sysAction.paste.connect(self.__actionPaste)
self.sysAction.finish.connect(self.accept)
self.libAction.paste.connect(self.__actionPaste)
self.libAction.finish.connect(self.accept)
self.libList.setCurrentRow(0)
......@@ -176,23 +249,27 @@ class LibraryDialog(QDialog):
self.libElemList.setMinimumWidth(380)
self.iconLabel.setMinimumWidth(220)
self.sysAction.setMinimumWidth(self.iconLabel.minimumWidth())
self.libAction.setMinimumWidth(self.iconLabel.minimumWidth())
self.resize(self.size().width(), 360)
def __addLibElemList(self, entries):
# Figure out the padding.
maxA = maxB = ""
for number, textA, textB, textC in entries:
if len(textA) > len(maxA):
maxA = textA
if len(textB) > len(maxB):
maxB = textB
# Pad the entries and add them to the widget.
for number, textA, textB, textC in entries:
textA += " " * (len(maxA) - len(textA))
textB += " " * (len(maxB) - len(textB))
QListWidgetItem("%s %s%s" % (textA, textB, textC),
self.libElemList,
number)
def __addSystemBlockTable(self, prefix, table):
biggestNum = ""
biggestName = ""
for blockCls in table.values():
if blockCls.broken:
continue
number, name, desc = blockCls.name
if number < 0 and not self.withExtensions:
continue
number = "%d" % number
if len(number) > len(biggestNum):
biggestNum = number
if len(name) > len(biggestName):
biggestName = name
entries = []
for blockCls in sorted(table.values(), key=lambda c: c.name[0]):
if blockCls.broken:
continue
......@@ -201,32 +278,53 @@ class LibraryDialog(QDialog):
continue
absName = "%s %d" % (prefix, number)
absName += " " * (len(prefix) + 1 + len(biggestNum) - len(absName))
symName = '"%s"' % name
symName += " " * (len(biggestName) - len(name))
if desc:
desc = " (%s)" % desc
else:
desc = ""
QListWidgetItem("%s %s%s" % (absName, symName, desc),
self.libElemList,
number + self.BLOCK_OFFSET)
entries.append((number + self.BLOCK_OFFSET,
absName, symName, desc))
self.__addLibElemList(entries)
def __addLibraryTable(self, lib):
entries = []
for i, libCls in enumerate(lib.entries()):
if libCls.broken:
continue
absName = "%s %d" % ("FC" if libCls.isFC else "FB",\
libCls.staticIndex)
symName = libCls.symbolName
if libCls.description:
desc = " (%s)" % libCls.description
else:
desc = ""
entries.append((i + self.BLOCK_OFFSET,
absName, symName, desc))
self.__nr2entry[i + self.BLOCK_OFFSET] = libCls
self.__addLibElemList(entries)
def __libItemChanged(self, item, prevItem):
def __hideAllActionWidgets(self):
self.currentActionWidget = None
self.sysAction.hide()
self.libAction.hide()
def __libItemChanged(self, item, prevItem):
self.__hideAllActionWidgets()
self.libElemList.clear()
if item.type() == self.ITEM_SFC:
self.__addSystemBlockTable("SFC", SFC_table)
elif item.type() == self.ITEM_SFB:
self.__addSystemBlockTable("SFB", SFB_table)
elif item.type() >= self.ITEM_LIB_BASE:
lib = self.__nr2lib[item.type()]
self.__addLibraryTable(lib)
else:
assert(0)
def __libElemItemChanged(self, item, prevItem):
if not item:
self.currentActionWidget = None
self.sysAction.hide()
self.__hideAllActionWidgets()
return
libType = self.libList.currentItem().type()
if libType in (self.ITEM_SFC, self.ITEM_SFB):
......@@ -234,9 +332,14 @@ class LibraryDialog(QDialog):
self.sysAction.show()
self.currentActionWidget = self.sysAction
if libType == self.ITEM_SFC:
self.sysAction.update("SFC", SFC_table[blockNum])
self.sysAction.updateData("SFC", SFC_table[blockNum])
else:
self.sysAction.update("SFB", SFB_table[blockNum])
self.sysAction.updateData("SFB", SFB_table[blockNum])
elif libType >= self.ITEM_LIB_BASE:
entryCls = self.__nr2entry[item.type()]
self.libAction.show()
self.currentActionWidget = self.libAction
self.libAction.updateData(entryCls)
else:
assert(0)
......
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim.cython_helper as __cython
if __cython.shouldUseCython(__name__): #@nocy
#if True: #@cy
try:
from awlsim_cython.library.all_modules import * #<no-cython-patch
except ImportError as e:
__cython.cythonImportError(__name__, str(e))
if not __cython.shouldUseCython(__name__): #@nocy
from awlsim.library.all_modules import * #@nocy
from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *
from awlsim.library.libentry import *
from __future__ import division, absolute_import, print_function, unicode_literals
import awlsim.cython_helper as __cython
if __cython.shouldUseCython(__name__): #@nocy
#if True: #@cy
try:
from awlsim_cython.library.iec.all_modules import * #<no-cython-patch
except ImportError as e:
__cython.cythonImportError(__name__, str(e))
if not __cython.shouldUseCython(__name__): #@nocy
from awlsim.library.iec.all_modules import * #@nocy
from __future__ import division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *
from awlsim.library.iec.iec import *
from awlsim.library.iec.fc9_eq_dt import *
# -*- coding: utf-8 -*-
#
# AWL simulator - IEC library - FC 9 "EQ_DT"
#
# Copyright 2014 Michael Buesch <[email protected]>
#
# 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 division, absolute_import, print_function, unicode_literals
from awlsim.common.compat import *
from awlsim.library.libentry import *
class Lib__IEC__FC9_EQ_DT(AwlLibFC):
libraryName = "IEC"
staticIndex = 9
symbolName = "EQ_DT"
description = "Compare two DATE_AND_TIME for equality"
interfaceFields = {
BlockInterfaceField.FTYPE_IN : (
BlockInterfaceField(name = "DT1",
dataType = AwlDataType.makeByName("DATE_AND_TIME")),
BlockInterfaceField(name = "DT2",
dataType = AwlDataType.makeByName("DATE_AND_TIME")),
),
BlockInterfaceField.FTYPE_OUT : (
BlockInterfaceField(name = "RET_VAL",
dataType = AwlDataType.makeByName("BOOL")),
),
}
awlCode = """
LAR1 P##DT1
LAR2 P##DT2
L D [AR1, P#0.0]
L D [AR2, P#0.0]
==D
U(
L D [AR1, P#4.0]
L D [AR2, P#4.0]
==D
)
= #RET_VAL
SET
SAVE
BE