--- a/svghmi/svghmi.c Wed Sep 11 11:20:11 2019 +0200
+++ b/svghmi/svghmi.c Wed Sep 11 12:24:30 2019 +0200
@@ -15,8 +15,8 @@
%(extern_variables_declarations)s
-#define ticktime_ns %(PLC_ticktime)d;
-uint16_t ticktime_ms (ticktime_ns>1000000)?
+#define ticktime_ns %(PLC_ticktime)d +uint16_t ticktime_ms = (ticktime_ns>1000000)? @@ -28,13 +28,13 @@
int global_write_dirty = 0;
/* zero means not subscribed */
uint16_t refresh_period_ms;
@@ -42,7 +42,7 @@
@@ -82,7 +82,7 @@
- if(dsc->wstate == buf_set)
+ if(dsc->wstate == buf_set){ /* if being subscribed */
if(dsc->refresh_period_ms){
if(dsc->age_ms + ticktime_ms < dsc->refresh_period_ms){
@@ -115,10 +115,10 @@
while(AtomicCompareExchange(&dsc->wlock, 0, 1)) sched_yield();
// check for variable being modified
- if(dsc->wstat == buf_tosend){
+ if(dsc->wstate == buf_tosend){ - // TODO write to some socket
+ // TODO call the python callback @@ -143,10 +143,7 @@
bzero(rbuf,sizeof(rbuf));
bzero(wbuf,sizeof(wbuf));
- // create - connection endpoint
+ // TODO - sending pthread condition variable @@ -165,16 +162,21 @@
traverse_hmi_tree(write_iterator);
- // TODO : set emaphore to wakeup sending thread
+ // TODO : set condition variable to wakeup sending collector -void sending_thread_proc(void* args){
+void* collect_updates_to_send(void* args){ + // TODO : get callback from args
- // - next autonomous send thread wakeup. (impl as wait timeout ?)
+ // - condition variable + // TODO add arg to traverse_hmi_tree to pass callback traverse_hmi_tree(send_iterator);
--- a/svghmi/svghmi.py Wed Sep 11 11:20:11 2019 +0200
+++ b/svghmi/svghmi.py Wed Sep 11 12:24:30 2019 +0200
@@ -199,7 +199,7 @@
# "programs_declarations": "\n".join(["extern %(type)s %(C_path)s;" %
# p for p in self._ProgramList]),
- # TODO generate C code to observe/access HMI tree variables
+ # 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()
@@ -209,7 +209,7 @@
"extern_variables_declarations": "\n".join(extern_variables_declarations),
"buffer_size": buf_index,
"var_access_code": targets.GetCode("var_access.c"),
- "PLC_ticktime": self.GetCTRoot().GetTicktime()
+ "PLC_ticktime": self.GetCTR().GetTicktime() gen_svghmi_c_path = os.path.join(buildpath, "svghmi.c")
@@ -217,10 +217,21 @@
gen_svghmi_c.write(svghmi_c_code)
- return (["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), ""
+ # 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) + return ((["svghmi"], [(gen_svghmi_c_path, IECCFLAGS)], True), "", + ("runtime_svghmi0.py", open(runtimefile_path, "rb"))) - XSD = """<?xml version="1.0" encoding="ISO-8859-1" ?>
+ XSD = """<?xml version="1.0" encoding="utf-8" ?> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:element name="SVGHMI">
@@ -245,6 +256,10 @@
"tooltip": _("Edit HMI"),
"method": "_StartInkscape"
+ # TODO : HMITree button + # - can drag'n'drop variabes to Inkscape def _getSVGpath(self, project_path=None):
@@ -294,7 +309,18 @@
@return: [(C_file_name, CFLAGS),...] , LDFLAGS_TO_APPEND
+ location_str = "_".join(map(str, self.GetCurrentLocation())) + view_name = self.BaseParams.getName() svgfile = self._getSVGpath()
+ 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__
@@ -309,6 +335,7 @@
# call xslt transform on Inkscape's SVG to generate XHTML
result = transform.transform(svgdom)
+ result.write(target_file, encoding="utf-8") # print(transform.xslt.error_log)
@@ -318,23 +345,29 @@
# TODO : use default svg that expose the HMI tree as-is
+ target_file.write("""<!DOCTYPE html> +<h1> No SVG file provided </h1>
- targetpath = os.path.join(self._getBuildPath(), "target.xhtml")
- targetfile = open(targetpath, 'w')
- targetfile.write("TODO")
- res += (("target.js", open(targetpath, "rb")),)
+ runtimefile_path = os.path.join(buildpath, "runtime_svghmi1_%s.py" % location_str) + runtimefile = open(runtimefile_path, 'w') +def _runtime_svghmi1_%(location)s_start(): + svghmi_root.putChild('%(view_name)s',File('%(xhtml)s')) - # TODO add C code to expose HMI tree variables to shared memory
- # TODO generate a description of shared memory (xml or CSV)
- # that can be loaded by svghmi QTWeb* app or svghmi server
+ """ % {"location": location_str, + "view_name": view_name}) + res += (("runtime_svghmi1_%s.py" % location_str, open(runtimefile_path, "rb")),) --- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/svghmi/svghmi_server.py Wed Sep 11 12:24:30 2019 +0200
@@ -0,0 +1,90 @@
+# 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 session list lock +class HMISession(object): + def __init__(self, protocol_instance): + svghmi_sessions.append(self) + # get a unique bit index amont other svghmi_sessions, + # so that we can match flags passed by C->python callback + svghmi_sessions.remove(self) + # TODO : pass it to the C side recieve_message() + # - refresh rates / subsriptions + # TODO multiclient : pass client index as well +class HMIProtocol(WebSocketServerProtocol): + def __init__(self, *args, **kwargs): + self._hmi_session = None + WebSocketServerProtocol.__init__(self, *args, **kwargs) + self._hmi_session = HMISession(self) + def onClose(self, wasClean, code, reason): + self._hmi_session = None + def onMessage(self, msg, isBinary): + self._hmi_session.onMessage(msg) + #self.sendMessage(msg, binary) +# Called by PLCObject at start +def _runtime_svghmi0_start(): + global svghmi_listener, svghmi_root + svghmi_root = Resource() + wsfactory = WebSocketServerFactory() + wsfactory.protocol = HMIProtocol + # svghmi_root.putChild("",File(".svg")) + svghmi_root.putChild("ws",WebSocketResource(wsfactory)) + sitefactory = Site(svghmi_root) + svghmi_listener = reactor.listenTCP(8008, sitefactory) + # start a thread that call the C part of SVGHMI +# Called by PLCObject at stop +def _runtime_svghmi0_stop(): + svghmi_listener.stopListening --- a/tests/svghmi/beremiz.xml Wed Sep 11 11:20:11 2019 +0200
+++ b/tests/svghmi/beremiz.xml Wed Sep 11 12:24:30 2019 +0200
@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='utf-8'?>
-<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+<BeremizRoot xmlns:xsd="http://www.w3.org/2001/XMLSchema" URI_location="LOCAL://"> <Libraries Enable_SVGHMI_Library="true"/>
--- a/tests/svghmi/plc.xml Wed Sep 11 11:20:11 2019 +0200
+++ b/tests/svghmi/plc.xml Wed Sep 11 12:24:30 2019 +0200
@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<project xmlns:ns1="http://www.plcopen.org/xml/tc6_0201" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.plcopen.org/xml/tc6_0201">
<fileHeader companyName="Unknown" productName="Unnamed" productVersion="1" creationDateTime="2019-08-06T14:23:42"/>
- <contentHeader name="Unnamed" modificationDateTime="2019-08-14T11:10:01">
+ <contentHeader name="Unnamed" modificationDateTime="2019-09-11T11:54:14">