# HG changeset patch # User Edouard Tisserant # Date 1614330014 -3600 # Node ID 2db69e2c567363b4bc04bd1ed67a99e253bf25b8 # Parent ea4a61b4a325908a605f3ee21093b3f98144015a SVGHMI: Optimized overlapping geometry (widget ot page belonging) computation. Added human readable messages for progress. Includes updated XSLT. diff -r ea4a61b4a325 -r 2db69e2c5673 XSLTransform.py --- a/XSLTransform.py Thu Feb 25 11:22:10 2021 +0100 +++ b/XSLTransform.py Fri Feb 26 10:00:14 2021 +0100 @@ -17,8 +17,8 @@ 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()}) + def transform(self, root, profile_run=False, **kwargs): + res = self.xslt(root, profile_run=profile_run, **{k: etree.XSLT.strparam(v) for k, v in kwargs.iteritems()}) # print(self.xslt.error_log) return res diff -r ea4a61b4a325 -r 2db69e2c5673 svghmi/detachable_pages.ysl2 --- a/svghmi/detachable_pages.ysl2 Thu Feb 25 11:22:10 2021 +0100 +++ b/svghmi/detachable_pages.ysl2 Fri Feb 26 10:00:14 2021 +0100 @@ -44,14 +44,40 @@ } } +// variable "overlapping_geometry" was added for optimization. +// It avoids calling func:overlapping_geometry 3 times for each page +// (apparently libxml doesn't cache exslt function results) +// in order to optimize further, func:overlapping_geometry +// should be implemented in python or even C, +// as this is still the main bottleneck here +const "_overlapping_geometry" { + const "k", "'overlapping'"; + const "m", "'computing belonging of widgets to pages'"; + value "ns:ProgressStart($k, $m)"; + foreach "$hmi_pages | $keypads" { + const "k2", "concat('overlapping:', @id)"; + const "m2", "concat('collecting membership of ', @inkscape:label)"; + value "ns:ProgressStart($k2, $m2)"; + elt { + attrib "id" > «@id» + copy "func:overlapping_geometry(.)"; + } + value "ns:ProgressEnd($k2, $m2)"; + } + value "ns:ProgressEnd($k, $m)"; +} + +const "overlapping_geometry", "exsl:node-set($_overlapping_geometry)"; + def "func:all_related_elements" { param "page"; - const "page_overlapping_geometry", "func:overlapping_geometry($page)"; + const "page_overlapping_geometry", "$overlapping_geometry/elt[@id = $page/@id]/*"; const "page_overlapping_elements", "//svg:*[@id = $page_overlapping_geometry/@Id]"; const "page_sub_elements", "func:refered_elements($page | $page_overlapping_elements)"; result "$page_sub_elements"; } + def "func:required_elements" { param "pages"; choose{ @@ -114,7 +140,10 @@ template "svg:*", mode="page_desc" { if "ancestor::*[@id = $hmi_pages/@id]" error > HMI:Page «@id» is nested in another HMI:Page + const "desc", "func:widget(@id)"; + const "msg", "concat('generating page description ', $desc/arg[1]/@value)"; + value "ns:ProgressStart($msg)"; const "page", "."; const "p", "$geometry[@Id = $page/@id]"; @@ -127,8 +156,11 @@ // Take closest ancestor in detachable_elements // since nested detachable elements are filtered out + const "sumarized_page", + """func:sumarized_elements($page_all_elements)"""; + const "required_detachables", - """func:sumarized_elements($page_all_elements)/ + """$sumarized_page/ ancestor-or-self::*[@id = $detachable_elements/@id]"""; | "«$desc/arg[1]/@value»": { @@ -164,6 +196,7 @@ with "page_desc", "$desc"; } | }`if "position()!=last()" > ,` + value "ns:ProgressEnd($msg)"; } emit "definitions:page-desc" { @@ -186,4 +219,6 @@ foreach "$in_forEach_widget_ids"{ | «.» } -} + | Overlapping + apply "$overlapping_geometry", mode="testtree"; +} diff -r ea4a61b4a325 -r 2db69e2c5673 svghmi/gen_index_xhtml.xslt --- a/svghmi/gen_index_xhtml.xslt Thu Feb 25 11:22:10 2021 +0100 +++ b/svghmi/gen_index_xhtml.xslt Fri Feb 26 10:00:14 2021 +0100 @@ -19,7 +19,6 @@ - /* @@ -62,7 +61,6 @@ - @@ -280,7 +278,6 @@ - /* @@ -305,12 +302,10 @@ - - /* @@ -337,7 +332,6 @@ - @@ -378,9 +372,9 @@ + - @@ -413,7 +407,6 @@ - /* @@ -430,7 +423,6 @@ - @@ -448,9 +440,28 @@ + + + + + + + + + + + + + + + + + + + - + @@ -490,7 +501,6 @@ - /* @@ -521,7 +531,6 @@ - @@ -535,13 +544,16 @@ + + - + + " ": { @@ -634,10 +646,10 @@ + - /* @@ -655,12 +667,10 @@ - - /* @@ -687,9 +697,11 @@ - - - + Overlapping + + + + @@ -909,7 +921,6 @@ - /* @@ -926,11 +937,9 @@ - - /* @@ -957,7 +966,6 @@ - @@ -983,7 +991,6 @@ - /* @@ -1046,7 +1053,6 @@ - @@ -1149,7 +1155,6 @@ - /* @@ -1293,11 +1298,9 @@ - - /* @@ -1692,13 +1695,11 @@ - - /* @@ -1711,7 +1712,6 @@ - class @@ -1727,9 +1727,9 @@ + - /* @@ -1745,7 +1745,6 @@ - @@ -1755,7 +1754,7 @@ - + @@ -2995,7 +2994,6 @@ - /* @@ -3472,7 +3470,6 @@ - function numb_event(e) { @@ -5183,7 +5180,6 @@ - /* @@ -5210,11 +5206,9 @@ - - /* @@ -5247,7 +5241,6 @@ - class KeypadWidget extends Widget{ diff -r ea4a61b4a325 -r 2db69e2c5673 svghmi/gen_index_xhtml.ysl2 --- a/svghmi/gen_index_xhtml.ysl2 Thu Feb 25 11:22:10 2021 +0100 +++ b/svghmi/gen_index_xhtml.ysl2 Fri Feb 26 10:00:14 2021 +0100 @@ -7,13 +7,13 @@ decl emit(*name) alias - { *name; template *name { - const "_unused", "ns:ProgressStart(name())"; + // value "ns:ProgressStart(name())"; | | /* «local-name()» */ | content; | - const "__unused", "ns:ProgressEnd(name())"; + // value "ns:ProgressEnd(name())"; } }; diff -r ea4a61b4a325 -r 2db69e2c5673 svghmi/geometry.ysl2 --- a/svghmi/geometry.ysl2 Thu Feb 25 11:22:10 2021 +0100 +++ b/svghmi/geometry.ysl2 Fri Feb 26 10:00:14 2021 +0100 @@ -131,12 +131,13 @@ } } +const "groups", "/svg:svg | //svg:g"; + // return overlapping geometry for a given element // all intersercting element are returned // except groups, that must be contained to be counted in def "func:overlapping_geometry" { param "elt"; - const "groups", "/svg:svg | //svg:g"; const "g", "$geometry[@Id = $elt/@id]"; const "candidates", "$geometry[@Id != $elt/@id]"; result """$candidates[(@Id = $groups/@id and (func:intersect($g, .) = 9)) or diff -r ea4a61b4a325 -r 2db69e2c5673 svghmi/svghmi.py --- a/svghmi/svghmi.py Thu Feb 25 11:22:10 2021 +0100 +++ b/svghmi/svghmi.py Fri Feb 26 10:00:14 2021 +0100 @@ -430,6 +430,13 @@ #self.HMITreeView = HMITreeView(self) return HMITreeSelector(parent) +def _ProgressArgs(args): + if len(args) == 2: + key, message = args + else: + key, = args + message = key + return str(key), str(message), time.time() class SVGHMI(object): XSD = """ @@ -499,6 +506,7 @@ return True def GetSVGGeometry(self): + t = time.time() # invoke inskscape -S, csv-parse output, produce elements InkscapeGeomColumns = ["Id", "x", "y", "w", "h"] @@ -523,14 +531,20 @@ res.append(etree.Element("bbox", **attrs)) + self.GetCTRoot().logger.write(" Start collecting SVG geometry (Inkscape)\n") + self.GetCTRoot().logger.write(" Finished collecting SVG geometry (Inkscape) in %.3fs\n"%(time.time()-t)) return res def GetHMITree(self): global hmi_tree_root + t = time.time() res = [hmi_tree_root.etree(add_hash=True)] + self.GetCTRoot().logger.write(" Start getting HMI tree\n") + self.GetCTRoot().logger.write(" Fnished getting HMI tree in %.3fs\n"%(time.time()-t)) return res def GetTranslations(self, _context, msgs): + t = time.time() messages = EtreeToMessages(msgs) if len(messages) == 0: @@ -543,19 +557,25 @@ langs,translated_messages = MatchTranslations(translations, messages, errcallback=self.GetCTRoot().logger.write_warning) - return TranslationToEtree(langs,translated_messages) + ret = TranslationToEtree(langs,translated_messages) + + self.GetCTRoot().logger.write(" Start getting Translations\n") + self.GetCTRoot().logger.write(" Finished getting Translations in %.3fs\n"%(time.time()-t)) + + return ret times = {} - def ProgressStart(self, _context, message): - t = time.time() - s = str(message) - self.times[s] = t - - def ProgressEnd(self, _context, message): - t = time.time() - s = str(message) - self.GetCTRoot().logger.write(" %s: %.3f\n"%(message, t - self.times[s])) - self.times[s] = t + + def ProgressStart(self, _context, *args): + k,m,t = _ProgressArgs(args) + self.times[k] = t + # self.GetCTRoot().logger.write(" Start %s: %.3f\n"%(m, t - self.transform_begin)) + self.GetCTRoot().logger.write(" Start %s\n"%m) + + def ProgressEnd(self, _context, *args): + k,m,t = _ProgressArgs(args) + self.times[k] = t + self.GetCTRoot().logger.write(" Finished %s in %.3f\n"%(m, t - self.times[k])) def CTNGenerate_C(self, buildpath, locations): @@ -583,17 +603,18 @@ ("ProgressStart", self.ProgressStart), ("ProgressEnd", self.ProgressEnd)]) - t = time.time() # load svg as a DOM with Etree svgdom = etree.parse(svgfile) - self.GetCTRoot().logger.write(" Source SVG parsing: %.3f\n"%(time.time()-t)) + self.GetCTRoot().logger.write(" Source SVG parsing: %.3f\n"%(time.time()-t)) # call xslt transform on Inkscape's SVG to generate XHTML try: - result = transform.transform(svgdom) + self.transform_begin = time.time() + result = transform.transform(svgdom) # , profile_run=True) + self.GetCTRoot().logger.write(" XSLT transform: %.3f\n"%(time.time()-self.transform_begin)) except XSLTApplyError as e: self.FatalError("SVGHMI " + view_name + ": " + e.message) finally: @@ -604,6 +625,7 @@ result.write(target_file, encoding="utf-8") # print(str(result)) # print(transform.xslt.error_log) + # print(etree.tostring(result.xslt_profile,pretty_print=True)) # TODO # - Errors on HMI semantics