# HG changeset patch
# User Edouard Tisserant
# Date 1569319111 -7200
# Node ID 35eeb1ed105f9e9b6267b548b7b58099fc502a9b
# Parent 5ee6967f721d92cadebc39edfe6a135cbd3c21d1# Parent 39d78c530cbbc9724154d9cea7ab7b5394e76f00
Merge default in SVGHMI branch
diff -r 39d78c530cbb -r 35eeb1ed105f PLCGenerator.py
--- a/PLCGenerator.py Tue Sep 24 11:55:59 2019 +0200
+++ b/PLCGenerator.py Tue Sep 24 11:58:31 2019 +0200
@@ -935,7 +935,7 @@
if invar.getformalParameter() == "EN":
if len(invar.getconnectionPointIn().getconnections()) > 0:
if blk.getinstanceName() is None:
- var_name = "%s%d_ENO" % (blk.gettypeName(), blk.getlocalId())
+ var_name = "_TMP_%s%d_ENO" % (blk.gettypeName(), blk.getlocalId())
else:
var_name = "%s.ENO" % blk.getinstanceName()
return var_name
@@ -1160,7 +1160,7 @@
if variable.getformalParameter() == "":
variable_name = "%s%d" % (type, block.getlocalId())
else:
- variable_name = "%s%d_%s" % (type, block.getlocalId(), parameter)
+ variable_name = "_TMP_%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:
@@ -1253,7 +1253,7 @@
if output_parameter == "":
output_name = "%s%d" % (type, block.getlocalId())
else:
- output_name = "%s%d_%s" % (type, block.getlocalId(), output_parameter)
+ output_name = "_TMP_%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":
diff -r 39d78c530cbb -r 35eeb1ed105f ProjectController.py
--- a/ProjectController.py Tue Sep 24 11:55:59 2019 +0200
+++ b/ProjectController.py Tue Sep 24 11:58:31 2019 +0200
@@ -1053,7 +1053,8 @@
"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)
+ "variable_decl_array": ",\n".join(variable_decl_array),
+ "var_access_code": targets.GetCode("var_access.c")
}
return debug_code
@@ -1163,6 +1164,19 @@
def _Generate_runtime(self):
buildpath = self._getBuildPath()
+ # CTN code gen is expected AFTER Libraries code gen,
+ # at least SVGHMI relies on it.
+
+ # Generate C code and compilation params from liraries
+ try:
+ LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(
+ buildpath)
+ except Exception:
+ self.logger.write_error(
+ _("Runtime library extensions C code generation failed !\n"))
+ self.logger.write_error(traceback.format_exc())
+ return False
+
# Generate C code and compilation params from confnode hierarchy
try:
CTNLocationCFilesAndCFLAGS, CTNLDFLAGS, CTNExtraFiles = self._Generate_C(
@@ -1174,16 +1188,6 @@
self.logger.write_error(traceback.format_exc())
return False
- # Generate C code and compilation params from liraries
- try:
- LibCFilesAndCFLAGS, LibLDFLAGS, LibExtraFiles = self.GetLibrariesCCode(
- buildpath)
- except Exception:
- self.logger.write_error(
- _("Runtime library extensions C code generation failed !\n"))
- self.logger.write_error(traceback.format_exc())
- return False
-
self.LocationCFilesAndCFLAGS = LibCFilesAndCFLAGS + \
CTNLocationCFilesAndCFLAGS
self.LDFLAGS = CTNLDFLAGS + LibLDFLAGS
diff -r 39d78c530cbb -r 35eeb1ed105f XSLTransform.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/XSLTransform.py Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# This file is part of Beremiz.
+# See COPYING file for copyrights details.
+
+from __future__ import absolute_import
+from lxml import etree
+
+class XSLTransform(object):
+ """ a class to handle XSLT queries on project and libs """
+ def __init__(self, xsltpath, xsltext):
+
+ # parse and compile. "beremiz" arbitrary namespace for extensions
+ self.xslt = etree.XSLT(
+ etree.parse(
+ xsltpath,
+ etree.XMLParser()),
+ extensions={("beremiz", name): call for name, call in xsltext})
+
+ def transform(self, root, **kwargs):
+ res = self.xslt(root, **{k: etree.XSLT.strparam(v) for k, v in kwargs.iteritems()})
+ # print(self.xslt.error_log)
+ return res
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f docutil/docsvg.py
--- a/docutil/docsvg.py Tue Sep 24 11:55:59 2019 +0200
+++ b/docutil/docsvg.py Tue Sep 24 11:58:31 2019 +0200
@@ -31,15 +31,19 @@
def get_inkscape_path():
""" Return the Inkscape path """
- from six.moves import winreg
- 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('"', '')
+ if wx.Platform == '__WXMSW__':
+ from six.moves import winreg
+ 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"', '').strip()
+ return svgexepath.replace('"', '')
+ else:
+ # TODO: search path
+ return os.path.join("/usr/bin", "inkscape")
def open_win_svg(svgexepath, svgfile):
@@ -65,7 +69,7 @@
wx.MessageBox("Inkscape is not found or installed !")
return None
else:
- svgexepath = os.path.join("/usr/bin", "inkscape")
+ svgexepath=get_inkscape_path()
if os.path.isfile(svgexepath):
open_lin_svg(svgexepath, svgfile)
else:
diff -r 39d78c530cbb -r 35eeb1ed105f features.py
--- a/features.py Tue Sep 24 11:55:59 2019 +0200
+++ b/features.py Tue Sep 24 11:58:31 2019 +0200
@@ -12,7 +12,8 @@
('Native', 'NativeLib.NativeLibrary', True),
('Python', 'py_ext.PythonLibrary', True),
('Etherlab', 'etherlab.EthercatMaster.EtherlabLibrary', False),
- ('SVGUI', 'svgui.SVGUILibrary', False)]
+ ('SVGUI', 'svgui.SVGUILibrary', False),
+ ('SVGHMI', 'svghmi.SVGHMILibrary', False)]
catalog = [
('canfestival', _('CANopen support'), _('Map located variables over CANopen'), 'canfestival.canfestival.RootClass'),
@@ -22,6 +23,7 @@
('c_ext', _('C extension'), _('Add C code accessing located variables synchronously'), 'c_ext.CFile'),
('py_ext', _('Python file'), _('Add Python code executed asynchronously'), 'py_ext.PythonFile'),
('wxglade_hmi', _('WxGlade GUI'), _('Add a simple WxGlade based GUI.'), 'wxglade_hmi.WxGladeHMI'),
- ('svgui', _('SVGUI'), _('Experimental web based HMI'), 'svgui.SVGUI')]
+ ('svgui', _('SVGUI'), _('Experimental web based HMI'), 'svgui.SVGUI'),
+ ('svghmi', _('SVGHMI'), _('SVG based HMI'), 'svghmi.SVGHMI')]
file_editors = []
diff -r 39d78c530cbb -r 35eeb1ed105f plcopen/Makefile
--- a/plcopen/Makefile Tue Sep 24 11:55:59 2019 +0200
+++ b/plcopen/Makefile Tue Sep 24 11:58:31 2019 +0200
@@ -1,13 +1,23 @@
#! gmake
-yml := ../../yml2
+# Makefile to generate XSLT stylesheets from ysl2 files in the same directory
+
+# This uses YML2.
+# hg clone https://pep.foundation/dev/repos/yml2/
+
+# It should be just fine if yml2 is cloned just asside beremiz
+# otherwise, point yml2path to yml2 source directory
+# make yml2path=path/to/yml/dir
+
+yml2path ?= $(abspath ../../yml2)
+
ysl2files := $(wildcard *.ysl2)
xsltfiles := $(patsubst %.ysl2, %.xslt, $(ysl2files))
all:$(xsltfiles)
-%.xslt: %.ysl2 yslt_noindent.yml2
- $(yml)/yml2c -I $(yml) $< -o $@.tmp
+%.xslt: %.ysl2 ../yslt_noindent.yml2
+ $(yml2path)/yml2c -I $(yml2path):../ $< -o $@.tmp
xmlstarlet fo $@.tmp > $@
rm $@.tmp
diff -r 39d78c530cbb -r 35eeb1ed105f plcopen/XSLTModelQuery.py
--- a/plcopen/XSLTModelQuery.py Tue Sep 24 11:55:59 2019 +0200
+++ b/plcopen/XSLTModelQuery.py Tue Sep 24 11:58:31 2019 +0200
@@ -8,13 +8,14 @@
from lxml import etree
import util.paths as paths
from plcopen.structures import StdBlckLibs
+from XSLTransform import XSLTransform
ScriptDirectory = paths.AbsDir(__file__)
-
-class XSLTModelQuery(object):
+class XSLTModelQuery(XSLTransform):
""" a class to handle XSLT queries on project and libs """
def __init__(self, controller, xsltpath, ext=None):
+
# arbitrary set debug to false, updated later
self.debug = False
@@ -31,19 +32,12 @@
if ext is not None:
xsltext.extend(ext)
- # parse and compile. "beremiz" arbitrary namespace for extensions
- self.xslt = etree.XSLT(
- etree.parse(
- os.path.join(ScriptDirectory, xsltpath),
- etree.XMLParser()),
- extensions={("beremiz", name): call for name, call in xsltext})
-
+ XSLTransform.__init__(self,
+ os.path.join(ScriptDirectory, xsltpath),
+ xsltext)
def _process_xslt(self, root, debug, **kwargs):
self.debug = debug
- res = self.xslt(root, **{k: etree.XSLT.strparam(v) for k, v in kwargs.iteritems()})
- # print(self.xslt.error_log)
- return res
-
+ return self.transform(root, **kwargs)
# -------------------------------------------------------------------------------
# Helpers functions for translating list of arguments
diff -r 39d78c530cbb -r 35eeb1ed105f plcopen/yslt_noindent.yml2
--- a/plcopen/yslt_noindent.yml2 Tue Sep 24 11:55:59 2019 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-include yslt.yml2
-
-in xsl {
- decl istylesheet (
- *output="xml",
- version="1.0",
- xmlns:xsl="http://www.w3.org/1999/XSL/Transform",
- xmlns:exsl='http://exslt.org/common',
- extension-element-prefixes='exsl'
- ) alias stylesheet {
- output *output;
- content;
- };
-
- decl template(match) {
- content;
- };
-
- decl function(name) alias template {
- content;
- };
-
- decl call(name) alias call-template {
- content;
- };
-
- decl apply(select) alias apply-templates {
- content;
- };
-}
-
-
diff -r 39d78c530cbb -r 35eeb1ed105f runtime/PLCObject.py
--- a/runtime/PLCObject.py Tue Sep 24 11:55:59 2019 +0200
+++ b/runtime/PLCObject.py Tue Sep 24 11:58:31 2019 +0200
@@ -319,12 +319,15 @@
return False
- def PythonRuntimeCall(self, methodname, use_evaluator=True):
+ def PythonRuntimeCall(self, methodname, use_evaluator=True, reverse_order=False):
"""
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, []):
+ methods = self.python_runtime_vars.get("_runtime_%s" % methodname, [])
+ if reverse_order:
+ methods = reversed(methods)
+ for method in methods:
if use_evaluator:
_res, exp = self.evaluator(method)
else:
@@ -395,7 +398,7 @@
if self.python_runtime_vars is not None:
self.PythonThreadCommand("Finish")
self.PythonThread.join()
- self.PythonRuntimeCall("cleanup", use_evaluator=False)
+ self.PythonRuntimeCall("cleanup", use_evaluator=False, reverse_order=True)
self.python_runtime_vars = None
@@ -438,7 +441,7 @@
if cmd == "Activate":
self.PythonRuntimeCall("start")
self.PythonThreadLoop()
- self.PythonRuntimeCall("stop")
+ self.PythonRuntimeCall("stop", reverse_order=True)
else: # "Finish"
break
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/Makefile
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/Makefile Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,25 @@
+#! gmake
+
+# Makefile to generate XSLT stylesheets from ysl2 files in the same directory
+
+# This uses YML2.
+# hg clone https://pep.foundation/dev/repos/yml2/
+
+# It should be just fine if yml2 is cloned just asside beremiz
+# otherwise, point yml2path to yml2 source directory
+# make yml2path=path/to/yml/dir
+
+yml2path ?= $(abspath ../../yml2)
+
+ysl2files := $(wildcard *.ysl2)
+xsltfiles := $(patsubst %.ysl2, %.xslt, $(ysl2files))
+
+all:$(xsltfiles)
+
+%.xslt: %.ysl2 ../yslt_noindent.yml2
+ $(yml2path)/yml2c -I $(yml2path):../ $< -o $@.tmp
+ xmlstarlet fo $@.tmp > $@
+ rm $@.tmp
+
+clean:
+ rm -f $(xsltfiles)
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/README
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/README Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,1 @@
+SVG HMI
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/__init__.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/__init__.py Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,10 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz
+# Copyright (C) 2019: Edouard TISSERANT
+#
+# See COPYING file for copyrights details.
+
+from __future__ import absolute_import
+from svghmi.svghmi import *
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/default.svg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/default.svg Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,92 @@
+
+
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/gen_index_xhtml.xslt
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/gen_index_xhtml.xslt Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,145 @@
+
+
+
+
+
+
+
+
+
+
+
+ =HMI=
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+
+ var path, role, name, priv;
+
+ var id = "
+
+ ";
+
+
+ name = "
+
+ ";
+
+
+ /* -------------- */
+
+
+
+
+ /* -------------- */
+
+ res.push({
+
+ path:path,
+
+ role:role,
+
+ name:name,
+
+ priv:priv
+
+ })
+
+ }
+
+
+
+ ID:
+
+ x:
+
+ y:
+
+ w:
+
+ h:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/gen_index_xhtml.ysl2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/gen_index_xhtml.ysl2 Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,122 @@
+include yslt_noindent.yml2
+
+// overrides yslt's output function to set CDATA
+decl output(method, cdata-section-elements="script");
+istylesheet
+ /* From Inkscape */
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns="http://www.w3.org/1999/xhtml"
+
+ /* Our namespace to invoke python code */
+ xmlns:ns="beremiz"
+ extension-element-prefixes="ns"
+ exclude-result-prefixes="ns" {
+
+ /* This retrieves geometry obtained through "inkscape -S"
+ * already parsed by python and presented as a list of
+ *
+ */
+ variable "geometry", "ns:GetSVGGeometry()";
+ variable "hmitree", "ns:GetHMITree()";
+
+ /* Identity template :
+ * - copy every attributes
+ * - copy every sub-elements
+ */
+ template "@* | node()" {
+ /* use real xsl:copy instead copy-of alias from yslt.yml2 */
+ xsl:copy apply "@* | node()";
+ }
+
+ variable "mark" > =HMI=\n
+
+ /* copy root node and add geometry as comment for a test */
+ template "/"
+ html xmlns="http://www.w3.org/1999/xhtml" {
+ head;
+ body style="margin:0;" {
+ xsl:copy {
+ comment {
+ apply "$geometry", mode="testgeo";
+ }
+ comment {
+ apply "$hmitree", mode="testtree";
+ }
+ apply "@* | node()";
+ }
+ script{
+ ||
+ function evaluate_js_from_descriptions() {
+ var Page;
+ var Input;
+ var Display;
+ var res = [];
+ ||
+ variable "midmark" > \n«$mark»
+ apply """//*[contains(child::svg:desc, $midmark) or \
+ starts-with(child::svg:desc, $mark)]""",2
+ mode="code_from_descs";
+ ||
+ return res;
+ }
+ ||
+
+ /*TODO add :
+ - pages content
+ + with ref to elt ?
+ - widgets parameters
+ */
+
+ include text svghmi.js
+ }
+ }
+ }
+
+ template "*", mode="code_from_descs" {
+ ||
+ {
+ var path, role, name, priv;
+ var id = "«@id»";
+ ||
+
+ /* if label is used, use it as default name */
+ if "«@inkscape:label»"
+ |> name = "«@inkscape:label»";
+
+ | /* -------------- */
+
+ // this breaks indent, but fixing indent could break string literals
+ value "substring-after(svg:desc, $mark)";
+ // nobody reads generated code anyhow...
+
+ ||
+
+ /* -------------- */
+ res.push({
+ path:path,
+ role:role,
+ name:name,
+ priv:priv
+ })
+ }
+ ||
+ }
+
+
+ template "bbox", mode="testgeo"{
+ | ID: «@Id» x: «@x» y: «@y» w: «@w» h: «@h»
+ }
+
+ template "*", mode="testtree"{
+ param "indent", "''";
+ | «$indent» «local-name()» «@name» «@type» «@path»
+ apply "*", mode="testtree" {
+ with "indent" value "concat($indent,'>')"
+ };
+ }
+}
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/pous.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/pous.xml Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/svghmi.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/svghmi.c Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,213 @@
+#include
+#include
+#include "iec_types_all.h"
+#include "POUS.h"
+#include "config.h"
+#include "beremiz.h"
+
+#define DEFAULT_REFRESH_PERIOD_MS 100
+#define HMI_BUFFER_SIZE %(buffer_size)d
+#define HMI_ITEM_COUNT %(item_count)d
+
+/* PLC reads from that buffer */
+static char rbuf[HMI_BUFFER_SIZE];
+
+/* PLC writes to that buffer */
+static char wbuf[HMI_BUFFER_SIZE];
+
+%(extern_variables_declarations)s
+
+#define ticktime_ns %(PLC_ticktime)d
+uint16_t ticktime_ms = (ticktime_ns>1000000)?
+ ticktime_ns/1000000:
+ 1;
+
+typedef enum {
+ buf_free = 0,
+ buf_set,
+ buf_tosend
+} buf_state_t;
+
+int global_write_dirty = 0;
+
+typedef struct {
+ void *ptr;
+ __IEC_types_enum type;
+ uint32_t buf_index;
+
+ /* publish/write/send */
+ long wlock;
+ /* zero means not subscribed */
+ uint16_t refresh_period_ms;
+ uint16_t age_ms;
+
+ buf_state_t wstate;
+
+ /* retrieve/read/recv */
+ long rlock;
+ buf_state_t rstate;
+
+} hmi_tree_item_t;
+
+static hmi_tree_item_t hmi_tree_item[] = {
+%(variable_decl_array)s
+};
+
+static char sendbuf[HMI_BUFFER_SIZE];
+
+typedef void(*hmi_tree_iterator)(hmi_tree_item_t*);
+void traverse_hmi_tree(hmi_tree_iterator fp)
+{
+ unsigned int i;
+ for(i = 0; i < sizeof(hmi_tree_item)/sizeof(hmi_tree_item_t); i++){
+ hmi_tree_item_t *dsc = &hmi_tree_item[i];
+ if(dsc->type != UNKNOWN_ENUM)
+ (*fp)(dsc);
+ }
+}
+
+#define __Unpack_desc_type hmi_tree_item_t
+
+%(var_access_code)s
+
+void write_iterator(hmi_tree_item_t *dsc)
+{
+ void *dest_p = &wbuf[dsc->buf_index];
+ void *real_value_p = NULL;
+ char flags = 0;
+
+ void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
+
+ /* Try take lock */
+ long was_locked = AtomicCompareExchange(&dsc->wlock, 0, 1);
+
+ if(was_locked) {
+ /* was locked. give up*/
+ return;
+ }
+
+ if(dsc->wstate == buf_set){
+ /* if being subscribed */
+ if(dsc->refresh_period_ms){
+ if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){
+ dsc->age_ms += ticktime_ms;
+ }else{
+ dsc->wstate = buf_tosend;
+ }
+ }
+ }
+
+ /* if new value differs from previous one */
+ if(memcmp(dest_p, visible_value_p, __get_type_enum_size(dsc->type)) != 0){
+ /* copy and flag as set */
+ memcpy(dest_p, visible_value_p, __get_type_enum_size(dsc->type));
+ if(dsc->wstate == buf_free) {
+ dsc->wstate = buf_set;
+ dsc->age_ms = 0;
+ }
+ global_write_dirty = 1;
+ }
+
+ /* unlock - use AtomicComparExchange to have memory barrier */
+ AtomicCompareExchange(&dsc->wlock, 1, 0);
+}
+
+struct timespec sending_now;
+struct timespec next_sending;
+void send_iterator(hmi_tree_item_t *dsc)
+{
+ while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
+
+ // check for variable being modified
+ if(dsc->wstate == buf_tosend){
+ // send
+
+ // TODO pack data in buffer
+
+ dsc->wstate = buf_free;
+ }
+
+ AtomicCompareExchange(&dsc->wlock, 1, 0);
+}
+
+void read_iterator(hmi_tree_item_t *dsc)
+{
+ void *src_p = &rbuf[dsc->buf_index];
+ void *real_value_p = NULL;
+ char flags = 0;
+
+ void *visible_value_p = UnpackVar(dsc, &real_value_p, &flags);
+
+
+ memcpy(visible_value_p, src_p, __get_type_enum_size(dsc->type));
+}
+
+static pthread_cond_t svghmi_send_WakeCond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t svghmi_send_WakeCondLock = PTHREAD_MUTEX_INITIALIZER;
+
+static int continue_collect;
+
+int __init_svghmi()
+{
+ bzero(rbuf,sizeof(rbuf));
+ bzero(wbuf,sizeof(wbuf));
+ continue_collect = 1;
+
+ return 0;
+}
+
+void __cleanup_svghmi()
+{
+ pthread_mutex_lock(&svghmi_send_WakeCondLock);
+ continue_collect = 0;
+ pthread_cond_signal(&svghmi_send_WakeCond);
+ pthread_mutex_unlock(&svghmi_send_WakeCondLock);
+}
+
+void __retrieve_svghmi()
+{
+ traverse_hmi_tree(read_iterator);
+}
+
+void __publish_svghmi()
+{
+ global_write_dirty = 0;
+ traverse_hmi_tree(write_iterator);
+ if(global_write_dirty) {
+ pthread_cond_signal(&svghmi_send_WakeCond);
+ }
+}
+
+/* PYTHON CALLS */
+int svghmi_send_collect(uint32_t *size, char **ptr){
+
+ int do_collect;
+ pthread_mutex_lock(&svghmi_send_WakeCondLock);
+ do_collect = continue_collect;
+ if(do_collect){
+ pthread_cond_wait(&svghmi_send_WakeCond, &svghmi_send_WakeCondLock);
+ do_collect = continue_collect;
+ }
+ pthread_mutex_unlock(&svghmi_send_WakeCondLock);
+
+
+ if(do_collect) {
+ traverse_hmi_tree(send_iterator);
+ /* TODO set ptr and size to something */
+ return 0;
+ }
+ else
+ {
+ return EINTR;
+ }
+}
+
+int svghmi_recv_dispatch(uint32_t size, char *ptr){
+ printf("%%*s",size,ptr);
+ /* TODO something with ptr and size
+ - subscribe
+ or
+ - spread values
+ */
+}
+
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/svghmi.js
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/svghmi.js Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,22 @@
+// svghmi.js
+
+(function(){
+ // Open WebSocket to relative "/ws" address
+ var ws = new WebSocket(window.location.href.replace(/^http(s?:\/\/[^\/]*)\/.*$/, 'ws$1/ws'));
+
+ // Register message reception handler
+ ws.onmessage = function (evt) {
+ // TODO : dispatch and cache hmi tree updates
+
+ var received_msg = evt.data;
+ alert("Message is received..."+received_msg);
+ };
+
+ // Once connection established
+ ws.onopen = function (evt) {
+ // TODO : enable the HMI (was previously offline, or just starts)
+ // show main page
+
+ ws.send("test");
+ };
+})();
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/svghmi.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/svghmi.py Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,401 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz
+# Copyright (C) 2019: Edouard TISSERANT
+#
+# See COPYING file for copyrights details.
+
+from __future__ import absolute_import
+import os
+import shutil
+from itertools import izip, imap
+from pprint import pprint, pformat
+
+import wx
+
+import util.paths as paths
+from POULibrary import POULibrary
+from docutil import open_svg, get_inkscape_path
+from lxml import etree
+
+from util.ProcessLogger import ProcessLogger
+from runtime.typemapping import DebugTypesSize
+import targets
+
+HMI_TYPES_DESC = {
+ "HMI_CLASS":{},
+ "HMI_LABEL":{},
+ "HMI_STRING":{},
+ "HMI_INT":{},
+ "HMI_REAL":{}
+}
+
+HMI_TYPES = HMI_TYPES_DESC.keys()
+
+from XSLTransform import XSLTransform
+
+ScriptDirectory = paths.AbsDir(__file__)
+
+class HMITreeNode(object):
+ def __init__(self, path, name, nodetype, iectype = None, vartype = None):
+ self.path = path
+ self.name = name
+ self.nodetype = nodetype
+
+ if iectype is not None:
+ self.iectype = iectype
+ self.vartype = vartype
+ if nodetype in ["HMI_LABEL", "HMI_ROOT"]:
+ self.children = []
+
+ def pprint(self, indent = 0):
+ res = ">"*indent + pformat(self.__dict__, indent = indent, depth = 1) + "\n"
+ if hasattr(self, "children"):
+ res += "\n".join([child.pprint(indent = indent + 1)
+ for child in self.children])
+ res += "\n"
+
+ return res
+
+ def place_node(self, node):
+ best_child = None
+ known_best_match = 0
+ for child in self.children :
+ if child.path is not None:
+ in_common = 0
+ for child_path_item, node_path_item in izip(child.path, node.path):
+ if child_path_item == node_path_item:
+ in_common +=1
+ else:
+ break
+ if in_common > known_best_match:
+ known_best_match = in_common
+ best_child = child
+ if best_child is not None and best_child.nodetype == "HMI_LABEL":
+ best_child.place_node(node)
+ else:
+ self.children.append(node)
+
+ def etree(self):
+
+ attribs = dict(name=self.name)
+ if self.path is not None:
+ attribs["path"]=".".join(self.path)
+
+ res = etree.Element(self.nodetype, **attribs)
+
+ if hasattr(self, "children"):
+ for child_etree in imap(lambda c:c.etree(), self.children):
+ res.append(child_etree)
+
+ return res
+
+ def traverse(self):
+ yield self
+ if hasattr(self, "children"):
+ for c in self.children:
+ for yoodl in c.traverse():
+ yield yoodl
+
+# module scope for HMITree root
+# so that CTN can use HMITree deduced in Library
+# note: this only works because library's Generate_C is
+# systematicaly invoked before CTN's CTNGenerate_C
+
+hmi_tree_root = None
+
+class SVGHMILibrary(POULibrary):
+ def GetLibraryPath(self):
+ return paths.AbsNeighbourFile(__file__, "pous.xml")
+
+ def Generate_C(self, buildpath, varlist, IECCFLAGS):
+ global hmi_tree_root
+
+ """
+ PLC Instance Tree:
+ prog0
+ +->v1 HMI_INT
+ +->v2 HMI_INT
+ +->fb0 (type mhoo)
+ | +->va HMI_LABEL
+ | +->v3 HMI_INT
+ | +->v4 HMI_INT
+ |
+ +->fb1 (type mhoo)
+ | +->va HMI_LABEL
+ | +->v3 HMI_INT
+ | +->v4 HMI_INT
+ |
+ +->fb2
+ +->v5 HMI_IN
+
+ HMI tree:
+ hmi0
+ +->v1
+ +->v2
+ +->fb0_va
+ | +-> v3
+ | +-> v4
+ |
+ +->fb1_va
+ | +-> v3
+ | +-> v4
+ |
+ +->v5
+
+ """
+
+ # Filter known HMI types
+ hmi_types_instances = [v for v in varlist if v["derived"] in HMI_TYPES]
+
+ hmi_tree_root = HMITreeNode(None, "/", "HMI_ROOT")
+
+ # add special nodes
+ map(lambda (n,t): hmi_tree_root.children.append(HMITreeNode(None,n,t)), [
+ ("plc_status", "HMI_PLC_STATUS"),
+ ("current_page", "HMI_CURRENT_PAGE")])
+
+ # deduce HMI tree from PLC HMI_* instances
+ for v in hmi_types_instances:
+ path = v["C_path"].split(".")
+ # ignores variables starting with _TMP_
+ if path[-1].startswith("_TMP_"):
+ continue
+ new_node = HMITreeNode(path, path[-1], v["derived"], v["type"], v["vartype"])
+ hmi_tree_root.place_node(new_node)
+
+ variable_decl_array = []
+ extern_variables_declarations = []
+ buf_index = 0
+ item_count = 0
+ for node in hmi_tree_root.traverse():
+ if hasattr(node, "iectype"):
+ sz = DebugTypesSize.get(node.iectype, 0)
+ variable_decl_array += [
+ "{&(" + ".".join(node.path) + "), " + node.iectype + {
+ "EXT": "_P_ENUM",
+ "IN": "_P_ENUM",
+ "MEM": "_O_ENUM",
+ "OUT": "_O_ENUM",
+ "VAR": "_ENUM"
+ }[node.vartype] + ", " +
+ str(buf_index) + ", 0, }"]
+ buf_index += sz
+ item_count += 1
+ if len(node.path) == 1:
+ extern_variables_declarations += [
+ "extern __IEC_" + node.iectype + "_" +
+ "t" if node.vartype is "VAR" else "p"
+ + ".".join(node.path) + ";"]
+
+ # TODO : filter only requiered external declarations
+ for v in varlist :
+ if v["C_path"].find('.') < 0 and v["vartype"] == "FB" :
+ extern_variables_declarations += [
+ "extern %(type)s %(C_path)s;" % v]
+
+ # TODO check if programs need to be declared separately
+ # "programs_declarations": "\n".join(["extern %(type)s %(C_path)s;" %
+ # p for p in self._ProgramList]),
+
+ # C code to observe/access HMI tree variables
+ svghmi_c_filepath = paths.AbsNeighbourFile(__file__, "svghmi.c")
+ svghmi_c_file = open(svghmi_c_filepath, 'r')
+ svghmi_c_code = svghmi_c_file.read()
+ svghmi_c_file.close()
+ svghmi_c_code = svghmi_c_code % {
+ "variable_decl_array": ",\n".join(variable_decl_array),
+ "extern_variables_declarations": "\n".join(extern_variables_declarations),
+ "buffer_size": buf_index,
+ "item_count": item_count,
+ "var_access_code": targets.GetCode("var_access.c"),
+ "PLC_ticktime": self.GetCTR().GetTicktime()
+ }
+
+ gen_svghmi_c_path = os.path.join(buildpath, "svghmi.c")
+ gen_svghmi_c = open(gen_svghmi_c_path, 'w')
+ gen_svghmi_c.write(svghmi_c_code)
+ gen_svghmi_c.close()
+
+ # Python based WebSocket HMITree Server
+ svghmiserverfile = open(paths.AbsNeighbourFile(__file__, "svghmi_server.py"), 'r')
+ svghmiservercode = svghmiserverfile.read()
+ svghmiserverfile.close()
+
+ runtimefile_path = os.path.join(buildpath, "runtime_svghmi.py")
+ runtimefile = open(runtimefile_path, 'w')
+ runtimefile.write(svghmiservercode)
+ runtimefile.close()
+
+ return ((["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), "",
+ ("runtime_svghmi0.py", open(runtimefile_path, "rb")))
+
+class SVGHMI(object):
+ XSD = """
+
+
+
+
+
+
+
+
+
+ """
+
+ ConfNodeMethods = [
+ {
+ "bitmap": "ImportSVG",
+ "name": _("Import SVG"),
+ "tooltip": _("Import SVG"),
+ "method": "_ImportSVG"
+ },
+ {
+ "bitmap": "ImportSVG", # should be something different
+ "name": _("Inkscape"),
+ "tooltip": _("Edit HMI"),
+ "method": "_StartInkscape"
+ },
+
+ # TODO : HMITree button
+ # - can drag'n'drop variabes to Inkscape
+
+ ]
+
+ def _getSVGpath(self, project_path=None):
+ if project_path is None:
+ project_path = self.CTNPath()
+ return os.path.join(project_path, "svghmi.svg")
+
+
+ def OnCTNSave(self, from_project_path=None):
+ if from_project_path is not None:
+ shutil.copyfile(self._getSVGpath(from_project_path),
+ self._getSVGpath())
+ return True
+
+ def GetSVGGeometry(self):
+ # invoke inskscape -S, csv-parse output, produce elements
+ InkscapeGeomColumns = ["Id", "x", "y", "w", "h"]
+
+ inkpath = get_inkscape_path()
+ svgpath = self._getSVGpath()
+ _status, result, _err_result = ProcessLogger(None,
+ inkpath + " -S " + svgpath,
+ no_stdout=True,
+ no_stderr=True).spin()
+ res = []
+ for line in result.split():
+ strippedline = line.strip()
+ attrs = dict(
+ zip(InkscapeGeomColumns, line.strip().split(',')))
+
+ res.append(etree.Element("bbox", **attrs))
+
+ return res
+
+ def GetHMITree(self):
+ global hmi_tree_root
+ res = [hmi_tree_root.etree()]
+ return res
+
+ def CTNGenerate_C(self, buildpath, locations):
+ """
+ Return C code generated by iec2c compiler
+ when _generate_softPLC have been called
+ @param locations: ignored
+ @return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+ """
+
+ location_str = "_".join(map(str, self.GetCurrentLocation()))
+ view_name = self.BaseParams.getName()
+
+ svgfile = self._getSVGpath()
+
+ res = ([], "", False)
+
+ target_fname = "sghmi_"+location_str+".xhtml"
+
+ target_path = os.path.join(self._getBuildPath(), target_fname)
+ target_file = open(target_path, 'w')
+
+ if os.path.exists(svgfile):
+
+ # TODO : move to __init__
+ transform = XSLTransform(os.path.join(ScriptDirectory, "gen_index_xhtml.xslt"),
+ [("GetSVGGeometry", lambda *_ignored:self.GetSVGGeometry()),
+ ("GetHMITree", lambda *_ignored:self.GetHMITree())])
+
+
+ # load svg as a DOM with Etree
+ svgdom = etree.parse(svgfile)
+
+ # call xslt transform on Inkscape's SVG to generate XHTML
+ result = transform.transform(svgdom)
+
+ result.write(target_file, encoding="utf-8")
+ # print(str(result))
+ # print(transform.xslt.error_log)
+
+ # TODO
+ # - Errors on HMI semantics
+ # - ... maybe something to have a global view of what is declared in SVG.
+
+ else:
+ # TODO : use default svg that expose the HMI tree as-is
+ target_file.write("""
+
+
+ No SVG file provided
+
+
+""")
+
+ target_file.close()
+
+ res += ((target_fname, open(target_path, "rb")),)
+
+ runtimefile_path = os.path.join(buildpath, "runtime_svghmi1_%s.py" % location_str)
+ runtimefile = open(runtimefile_path, 'w')
+ runtimefile.write("""
+def _runtime_svghmi1_%(location)s_start():
+ svghmi_root.putChild('%(view_name)s',File('%(xhtml)s'))
+
+def _runtime_svghmi1_%(location)s_stop():
+ svghmi_root.delEntity('%(view_name)s')
+
+ """ % {"location": location_str,
+ "xhtml": target_fname,
+ "view_name": view_name})
+
+ runtimefile.close()
+
+ res += (("runtime_svghmi1_%s.py" % location_str, open(runtimefile_path, "rb")),)
+
+ return res
+
+ def _ImportSVG(self):
+ dialog = wx.FileDialog(self.GetCTRoot().AppFrame, _("Choose a SVG file"), os.getcwd(), "", _("SVG files (*.svg)|*.svg|All files|*.*"), wx.OPEN)
+ if dialog.ShowModal() == wx.ID_OK:
+ svgpath = dialog.GetPath()
+ 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()
+
+ def _StartInkscape(self):
+ svgfile = self._getSVGpath()
+ open_inkscape = True
+ if not self.GetCTRoot().CheckProjectPathPerm():
+ dialog = wx.MessageDialog(self.GetCTRoot().AppFrame,
+ _("You don't have write permissions.\nOpen Inkscape anyway ?"),
+ _("Open Inkscape"),
+ wx.YES_NO | wx.ICON_QUESTION)
+ open_inkscape = dialog.ShowModal() == wx.ID_YES
+ dialog.Destroy()
+ if open_inkscape:
+ if not os.path.isfile(svgfile):
+ svgfile = None
+ open_svg(svgfile)
diff -r 39d78c530cbb -r 35eeb1ed105f svghmi/svghmi_server.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/svghmi_server.py Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,138 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# This file is part of Beremiz
+# Copyright (C) 2019: Edouard TISSERANT
+# See COPYING file for copyrights details.
+
+from __future__ import absolute_import
+
+from twisted.web.server import Site
+from twisted.web.resource import Resource
+from twisted.internet import reactor
+from twisted.web.static import File
+
+from autobahn.twisted.websocket import WebSocketServerFactory, WebSocketServerProtocol
+from autobahn.twisted.resource import WebSocketResource
+
+# TODO multiclient :
+# session list lock
+# svghmi_sessions = []
+
+svghmi_session = None
+
+svghmi_send_collect = PLCBinary.svghmi_send_collect
+svghmi_send_collect.restype = ctypes.c_int # error or 0
+svghmi_send_collect.argtypes = [
+ ctypes.POINTER(ctypes.c_uint32), # size
+ ctypes.POINTER(ctypes.c_char_p)] # data ptr
+# TODO multiclient : switch to arrays
+
+svghmi_recv_dispatch = PLCBinary.svghmi_recv_dispatch
+svghmi_recv_dispatch.restype = ctypes.c_int # error or 0
+svghmi_recv_dispatch.argtypes = [
+ ctypes.c_uint32, # size
+ ctypes.c_char_p] # data ptr
+# TODO multiclient : switch to arrays
+
+class HMISession(object):
+ def __init__(self, protocol_instance):
+ global svghmi_session
+
+ # TODO: kill existing session for robustness
+ assert(svghmi_session is None)
+
+ svghmi_session = self
+ self.protocol_instance = protocol_instance
+
+ # TODO multiclient :
+ # svghmi_sessions.append(self)
+ # get a unique bit index amont other svghmi_sessions,
+ # so that we can match flags passed by C->python callback
+
+ def __del__(self):
+ global svghmi_session
+ assert(svghmi_session)
+ svghmi_session = None
+
+ # TODO multiclient :
+ # svghmi_sessions.remove(self)
+
+ def onMessage(self, msg):
+ # pass message to the C side recieve_message()
+ c_string = ctypes.c_char_p(msg)
+ c_string_pointer = ctypes.c_void_p(ctypes.addressof(c_string))
+ svghmi_recv_dispatch(len(msg), msg)
+
+ # TODO multiclient : pass client index as well
+
+
+ def sendMessage(self, msg):
+ self.sendMessage(msg, True)
+
+class HMIProtocol(WebSocketServerProtocol):
+
+ def __init__(self, *args, **kwargs):
+ self._hmi_session = None
+ WebSocketServerProtocol.__init__(self, *args, **kwargs)
+
+ def onOpen(self):
+ self._hmi_session = HMISession(self)
+ print "open"
+
+ def onClose(self, wasClean, code, reason):
+ del self._hmi_session
+ self._hmi_session = None
+ print "close"
+
+ def onMessage(self, msg, isBinary):
+ self._hmi_session.onMessage(msg)
+ print msg
+ #self.sendMessage(msg, binary)
+
+class HMIWebSocketServerFactory(WebSocketServerFactory):
+ protocol = HMIProtocol
+
+svghmi_root = None
+svghmi_listener = None
+svghmi_send_thread = None
+
+def SendThreadProc():
+ global svghmi_session
+ size = ctypes.c_uint32()
+ ptr = ctypes.c_char_p()
+ res = 0
+ while svghmi_send_collect(ctypes.byref(size), ctypes.byref(ptr)) == 0 and \
+ svghmi_session is not None and \
+ svghmi_session.sendMessage(ctypes.string_at(ptr,size)) == 0:
+ pass
+
+ # TODO multiclient : dispatch to sessions
+
+
+
+# Called by PLCObject at start
+def _runtime_svghmi0_start():
+ global svghmi_listener, svghmi_root, svghmi_send_thread
+
+ svghmi_root = Resource()
+ svghmi_root.putChild("ws", WebSocketResource(HMIWebSocketServerFactory()))
+
+ svghmi_listener = reactor.listenTCP(8008, Site(svghmi_root))
+
+ # start a thread that call the C part of SVGHMI
+ svghmi_send_thread = Thread(target=SendThreadProc, name="SVGHMI Send")
+ svghmi_send_thread.start()
+
+
+# Called by PLCObject at stop
+def _runtime_svghmi0_stop():
+ global svghmi_listener, svghmi_root, svghmi_send_thread
+ svghmi_root.delEntity("ws")
+ svghmi_root = None
+ svghmi_listener.stopListening()
+ svghmi_listener = None
+ # plc cleanup calls svghmi_(locstring)_cleanup and unlocks send thread
+ svghmi_send_thread.join()
+ svghmi_send_thread = None
+
diff -r 39d78c530cbb -r 35eeb1ed105f targets/Linux/plc_Linux_main_retain.c
--- a/targets/Linux/plc_Linux_main_retain.c Tue Sep 24 11:55:59 2019 +0200
+++ b/targets/Linux/plc_Linux_main_retain.c Tue Sep 24 11:58:31 2019 +0200
@@ -105,20 +105,20 @@
/* Compare current hash with hash from file byte by byte. */
int CheckFilehash(void)
{
- int k;
+ int k,ret;
int offset = sizeof(retain_info.retain_size);
rewind(retain_buffer);
fseek(retain_buffer, offset , SEEK_SET);
uint32_t size;
- fread(&size, sizeof(size), 1, retain_buffer);
+ ret = fread(&size, sizeof(size), 1, retain_buffer);
if (size != retain_info.hash_size)
return 0;
for(k = 0; k < retain_info.hash_size; k++){
uint8_t file_digit;
- fread(&file_digit, sizeof(char), 1, retain_buffer);
+ ret = fread(&file_digit, sizeof(char), 1, retain_buffer);
if (file_digit != *(retain_info.hash+k))
return 0;
}
@@ -317,8 +317,9 @@
void Remind(unsigned int offset, unsigned int count, void *p)
{
+ int ret;
/* Remind variable from file. */
fseek(retain_buffer, retain_info.header_offset+offset, SEEK_SET);
- fread((void *)p, count, 1, retain_buffer);
+ ret = fread((void *)p, count, 1, retain_buffer);
}
#endif // !HAVE_RETAIN
diff -r 39d78c530cbb -r 35eeb1ed105f targets/plc_debug.c
--- a/targets/plc_debug.c Tue Sep 24 11:55:59 2019 +0200
+++ b/targets/plc_debug.c Tue Sep 24 11:58:31 2019 +0200
@@ -71,37 +71,9 @@
}
}
-#define __Unpack_case_t(TYPENAME) \
- case TYPENAME##_ENUM :\
- *flags = ((__IEC_##TYPENAME##_t *)varp)->flags;\
- forced_value_p = *real_value_p = &((__IEC_##TYPENAME##_t *)varp)->value;\
- break;
-
-#define __Unpack_case_p(TYPENAME)\
- case TYPENAME##_O_ENUM :\
- *flags = __IEC_OUTPUT_FLAG;\
- case TYPENAME##_P_ENUM :\
- *flags |= ((__IEC_##TYPENAME##_p *)varp)->flags;\
- *real_value_p = ((__IEC_##TYPENAME##_p *)varp)->value;\
- forced_value_p = &((__IEC_##TYPENAME##_p *)varp)->fvalue;\
- break;
-
-void* UnpackVar(dbgvardsc_t *dsc, void **real_value_p, char *flags)
-{
- void *varp = dsc->ptr;
- void *forced_value_p = NULL;
- *flags = 0;
- /* find data to copy*/
- switch(dsc->type){
- __ANY(__Unpack_case_t)
- __ANY(__Unpack_case_p)
- default:
- break;
- }
- if (*flags & __IEC_FORCE_FLAG)
- return forced_value_p;
- return *real_value_p;
-}
+#define __Unpack_desc_type dbgvardsc_t
+
+%(var_access_code)s
void Remind(unsigned int offset, unsigned int count, void * p);
diff -r 39d78c530cbb -r 35eeb1ed105f targets/var_access.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/targets/var_access.c Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,33 @@
+
+#define __Unpack_case_t(TYPENAME) \
+ case TYPENAME##_ENUM :\
+ *flags = ((__IEC_##TYPENAME##_t *)varp)->flags;\
+ forced_value_p = *real_value_p = &((__IEC_##TYPENAME##_t *)varp)->value;\
+ break;
+
+#define __Unpack_case_p(TYPENAME)\
+ case TYPENAME##_O_ENUM :\
+ *flags = __IEC_OUTPUT_FLAG;\
+ case TYPENAME##_P_ENUM :\
+ *flags |= ((__IEC_##TYPENAME##_p *)varp)->flags;\
+ *real_value_p = ((__IEC_##TYPENAME##_p *)varp)->value;\
+ forced_value_p = &((__IEC_##TYPENAME##_p *)varp)->fvalue;\
+ break;
+
+static void* UnpackVar(__Unpack_desc_type *dsc, void **real_value_p, char *flags)
+{
+ void *varp = dsc->ptr;
+ void *forced_value_p = NULL;
+ *flags = 0;
+ /* find data to copy*/
+ switch(dsc->type){
+ __ANY(__Unpack_case_t)
+ __ANY(__Unpack_case_p)
+ default:
+ break;
+ }
+ if (*flags & __IEC_FORCE_FLAG)
+ return forced_value_p;
+ return *real_value_p;
+}
+
diff -r 39d78c530cbb -r 35eeb1ed105f tests/svghmi/beremiz.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svghmi/beremiz.xml Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,5 @@
+
+
+
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f tests/svghmi/plc.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svghmi/plc.xml Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,241 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TargetPressure
+
+
+
+
+
+
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TargetPressure
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Pressure
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 23
+
+
+
+
+
+
+
+
+
+
+
+
+ AddOut
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f tests/svghmi/svghmi_0@svghmi/baseconfnode.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svghmi/svghmi_0@svghmi/baseconfnode.xml Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,2 @@
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f tests/svghmi/svghmi_0@svghmi/confnode.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svghmi/svghmi_0@svghmi/confnode.xml Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,2 @@
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f tests/svghmi/svghmi_0@svghmi/svghmi.svg
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/svghmi/svghmi_0@svghmi/svghmi.svg Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,117 @@
+
+
+
+
diff -r 39d78c530cbb -r 35eeb1ed105f yslt_noindent.yml2
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/yslt_noindent.yml2 Tue Sep 24 11:58:31 2019 +0200
@@ -0,0 +1,40 @@
+include yslt.yml2
+!!
+def indent(level):
+ return ""
+!!
+
+in xsl {
+ decl istylesheet (
+ *output="xml",
+ version="1.0",
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform",
+ xmlns:exsl='http://exslt.org/common',
+ xmlns:regexp="http://exslt.org/regular-expressions",
+ xmlns:str="http://exslt.org/strings",
+ extension-element-prefixes='exsl regexp str'
+ ) alias stylesheet {
+ output *output;
+ content;
+ };
+
+ decl indent() alias -;
+
+ decl template(match) {
+ content;
+ };
+
+ decl function(name) alias template {
+ content;
+ };
+
+ decl call(name) alias call-template {
+ content;
+ };
+
+ decl apply(select) alias apply-templates {
+ content;
+ };
+}
+
+