502 lines
25 KiB
Plaintext
502 lines
25 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta name="generator" content="HTML Tidy for HTML5 for Apple macOS version 5.8.0">
|
|
<!-- Copyright Bob Jacobsen 2008 -->
|
|
|
|
<title>JMRI: DecoderPro User Guide - Use XSLT Transformation for complex decoders File</title>
|
|
<!--#include virtual="/help/en/parts/Style.shtml" -->
|
|
</head>
|
|
<body>
|
|
<!--#include virtual="/help/en/parts/Header.shtml" -->
|
|
|
|
<div id="mBody">
|
|
<!--#include virtual="Sidebar.shtml" -->
|
|
|
|
<div id="mainContent">
|
|
<h1>JMRI: DecoderPro User Guide</h1>
|
|
|
|
<h2>Use XSLT Transformation for complex decoders</h2>
|
|
|
|
<p>Some decoders contain <strong>repeated blocks</strong> of CVs, for example to define
|
|
behaviour of several accessories, each controlled by multiple CVs. An advanced turnout
|
|
decoder may for example define multiple paths, each containing several turnouts and their
|
|
desired position to form the travel path on the layout.</p>
|
|
|
|
<p>Although the decoder file must define dozens or even hundreds of CVs and their appearance
|
|
on panes in total, only a fraction of the CVs or displays are actually unique: the rest can
|
|
be <strong>generated from a template</strong>. While creating template, and the
|
|
transformation recipe is <strong>a lot more complex</strong> than copy-pasting CV
|
|
definitions, the benefit is <strong>a lot easier maintenance</strong> once the hard part is
|
|
done: each change propagates consistently to all generated parts.</p>
|
|
|
|
<p>To give some example of simplification possible - let's take the decoder file
|
|
<code>Public_Domain_dccdoma_ARD_SCOM_MX.xml</code>. It configures a decoder, capable of
|
|
displaying signal aspects on several signal masts. The configuration contains over 500 of CVs
|
|
- yet the basic idea behind the configuration is dead simple:</p>
|
|
|
|
<ul>
|
|
<li>a default aspect for each signal mast</li>
|
|
|
|
<li>for each signal mast AND for each one of 32 possible aspects, the number of signal to
|
|
be displayed, interpreted by the decoder itself</li>
|
|
</ul>
|
|
|
|
<p>A few statistics:</p>
|
|
|
|
<ul>
|
|
<li>original decoder's definition: <strong>870 kByte</strong> 20608 lines.</li>
|
|
|
|
<li>stylesheet file: <strong>12 kByte</strong>, 257 lines.</li>
|
|
|
|
<li>decoder file template: <strong>18 kByte</strong>, 390 lines.</li>
|
|
</ul>
|
|
|
|
<p>For JMRI itself or the speed of DecoderPro operation, these two approaches are the same:
|
|
the file template is internally transformed (expanded) to the decoder definition XML and
|
|
processed as if it was written entirely by hand. For <strong>maintenance</strong>, it is a
|
|
way easier to maintain ~600 lines of XML than 20600.</p>
|
|
|
|
<p>JMRI provides an option to apply a <strong>XSLT stylesheet</strong> to a decoder file,
|
|
<strong>before</strong> the file is loaded into DecoderPro and before it is interpreted as CV
|
|
variables and panels. This allows to hand-write unique CV definitions and their panes, and
|
|
<strong>add generated</strong> content where appropriate.</p>
|
|
|
|
<h2>Example files</h2>
|
|
|
|
<p>To illustrate the techniques described here, a few example files are provided; all the
|
|
files are licensed under GNU GPL.</p>
|
|
|
|
<ul>
|
|
<li><strong><a href="resources/decoder-template.xml">decoder-template.xml</a></strong> -
|
|
the decoder definition <strong>template</strong></li>
|
|
|
|
<li><strong><a href="resources/scom.xsl">scom.xsl</a></strong> - the stylesheet</li>
|
|
</ul>
|
|
|
|
<p>The decoder template should be placed into the <strong>xml/decoders</strong> folder of the
|
|
JMRI installation. It is <strong>based on Petr Sidlo's dccdoma.cz - ARD-SCOM-MX
|
|
decoder</strong> - generates the same decoder panels as the original one (as of 12/2019). The
|
|
stylesheet (<strong>scom.xsl</strong>) should be placed also into
|
|
<strong><code>xml/decoders</code></strong> folder of the JMRI installation.</p>
|
|
|
|
<p>The template can be processed from the commandline to generate the decoder XML, so you can
|
|
inspect effects of changing the stylesheet and/or data embedded in the decoder template. The
|
|
commandline for Linux:</p>
|
|
|
|
<pre>
|
|
xsltproc scom.xsl decoder-template.xml > decoder-gen.xml
|
|
</pre>
|
|
<p>Remember to replace the files with their actual names or locations; for experimenting from
|
|
the commandline, the best is to place the decoder file template AND its stylesheet to some
|
|
directory and work in there. Later, move the stylesheet and the template to the folders as
|
|
described above.</p>
|
|
|
|
<h2>Apply stylesheet to the decoder file.</h2>
|
|
|
|
<p>An <strong>instruction to process the file as a template</strong> must be present in the
|
|
file, in order to act like a template. Otherwise, JMRI would pick it as just "ordinary"
|
|
decoder definition - all the display items (see below) "misused" to hold data for template
|
|
processing would appear in the UI !</p>
|
|
|
|
<p>The processing instruction must appear at the start of the decoder definition:</p>
|
|
|
|
<pre>
|
|
<?transform-xslt href="http://jmri.org/xml/decoders/scom.xsl"?>
|
|
</pre>
|
|
<p>So the decoder template's header would look like:</p>
|
|
|
|
<pre>
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<?transform-xslt href="http://jmri.org/xml/decoders/scom.xsl"?>
|
|
<decoder-config xmlns:xi="http://www.w3.org/2001/XInclude" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
xsi:noNamespaceSchemaLocation="http://jmri.org/xml/schema/decoder.xsd" showEmptyPanes="no" >
|
|
|
|
<decoder>
|
|
...
|
|
</pre>
|
|
<h2>Provide metadata to the stylesheet</h2>
|
|
|
|
<p>One of the critical points is how to generate CV numbers or other variable parts: XSLT
|
|
language provides simple numeric computation, but more sophisticated functions are typically
|
|
not accessible (by default). Some generated content is composed from a list of strings (i.e.
|
|
signal aspect names are repeated for each signal masts), and we have to provide such input to
|
|
the stylesheet. The decoder file is <strong>the only input</strong> provided for the
|
|
stylesheet by the JMRI framework.</p>
|
|
|
|
<p>The decoder template file is <strong>still interpreted as a decoder definition</strong>
|
|
and must adhere strictly to the <code>decoder.xsd</code> XML schema. For parts that we want
|
|
to generate from the template, the prescribed elements have to be <strong>carefully
|
|
misused</strong> to provide</p>
|
|
|
|
<ul>
|
|
<li>anchor points, where the generated content will be inserted</li>
|
|
|
|
<li>input data for the stylesheet</li>
|
|
</ul>
|
|
|
|
<p>There is a number of ways how to approach the problem, I will present a way I see as more
|
|
or less clean (although it misuses elements to provide data different than they formally
|
|
should !). The guide should be seen as a recommendation to keep the generated decoders
|
|
somewhat consistent. Please <strong>do not hesitate to contribute and provide simpler
|
|
approaches</strong>.</p>
|
|
|
|
<h3>Adding Variables</h3>
|
|
|
|
<p>Just adding variables is simple, and requires <strong>no extra placeholder</strong> in the
|
|
decoder file. However, the <strong><code><variables></code></strong> element must be
|
|
present, so the technique described below for generating variables works. The element could
|
|
look like this example:</p>
|
|
|
|
<pre>
|
|
<variables>
|
|
<variable CV="1" item="Short Address" default="100" >
|
|
<splitVal highCV="9" upperMask="XXXXXVVV" factor="1" offset="0" />
|
|
<label>Decoder Address:</label>
|
|
<tooltip>Accessory decoder address. CV1 - LSB. CV9 - MSB.</tooltip>
|
|
</variable>
|
|
</variables>
|
|
</pre>
|
|
<p>Additional generated content will be <strong>appended</strong> inside that element.</p>
|
|
|
|
<h3>Data holder pane</h3>
|
|
|
|
<p>While <strong><code>variable</code></strong> element's definition is rather strict, UI
|
|
definitions seems most relaxed, so we abuse them. The following section describes some
|
|
typical kind of data, how they can be <strong>represented</strong> in decoder template file,
|
|
so the text conforms to the mandatory <code>decoder.xsd</code> rules. And finally how they
|
|
can be <strong>accessed</strong> from the stylesheet.</p>
|
|
|
|
<p><strong>All the data</strong> (not UI panel definitions) will be placed in a
|
|
<strong>single <pane> element</strong>. All panes must be named - the name can be
|
|
arbitrary, but should be <strong>unique</strong> so a system-defined pane or a custom
|
|
<strong>real</strong> pane is not replaced accidentally. In our example,
|
|
<strong>__Aspects</strong> name is used. I recommend to prefix the panel name with two
|
|
underscores. The pane's name <strong>must be used</strong> in selectors - so if you invent
|
|
your own name, replace the text in examples with whatever name you choose.</p>
|
|
|
|
<h4>Passing root of the data</h4>
|
|
|
|
<p>Each time, a value needs to be read by the stylesheet, it must be
|
|
<strong>selected</strong> by an XPath expression. For example:</p>
|
|
|
|
<pre>
|
|
<xsl:template name="generate-masts">
|
|
<xsl:variable name="cvStart" select="string(//pane[name/text() ='__Aspects']/display[@item='mastcount']/@tooltip)"/>
|
|
<xsl:variable name="outputs" select="string(//pane[name/text() ='__Aspects']/display[@item='outputs']/@label)"/>
|
|
<xsl:for-each select="//pane[name/text() ='__Aspects']/display[@item='masts']/label">
|
|
...
|
|
</xsl:for-each>
|
|
</xsl:template>
|
|
</pre>
|
|
<p>The selector always contains the common prefix part, which finds the "data holder" pane
|
|
within the decoder template file. We can save the typing by passing that element as a
|
|
<strong>parameter</strong>:</p>
|
|
|
|
<pre>
|
|
<xsl:template name="generate-masts">
|
|
<xsl:param name="root"/>
|
|
<xsl:variable name="cvStart" select="string($root/display[@item='mastcount']/@tooltip)"/>
|
|
<xsl:variable name="outputs" select="string($root/display[@item='outputs']/@label)"/>
|
|
<xsl:for-each select="$root/display[@item='masts']/label">
|
|
...
|
|
</xsl:for-each>
|
|
</xsl:template>
|
|
</pre>
|
|
<p>The invocation of such a generating template <strong>must pass the parameter</strong>:</p>
|
|
|
|
<pre>
|
|
<xsl:call-template name="generate-masts">
|
|
<xsl:with-param name="root" select="//pane[name/text() ='__Aspects']//display[position() = 1]/.."/>
|
|
</xsl:call-template>
|
|
</pre>
|
|
<p>Note the strange suffix. This is because the display items can not be nested directly in
|
|
the <strong>pane</strong> element, they have to be in some kind of column, row, group etc.
|
|
The strange selector at the end will find <strong>first nested display element</strong> and
|
|
will take its <strong>parent element</strong> as the data root.</p>
|
|
|
|
<p>A <strong>global variable</strong> can be defined in a similar way - place this element
|
|
directly as top-level element in the stylesheet:</p>
|
|
|
|
<pre>
|
|
<xsl:variable name="root" select="//pane[name/text() ='__Aspects']//display[position() = 1]/.."/>
|
|
</pre>
|
|
<p>The templates can now reference the root of data by just <strong>$root</strong>
|
|
expression.</p>
|
|
|
|
<h4>Constants, max/min values, single values</h4>
|
|
|
|
<p>A constant can be used, e.g. as a maximum count of items, specific CV number, ... I
|
|
recommend to use <strong>display</strong> element to define a constant. That element has two
|
|
free-form attributes: <strong>label</strong> and <strong>tooltip</strong>. So we can define
|
|
actually two constants in a single element! This can be useful, if there are values closely
|
|
tied together, for example. Constants, that define maximum number of aspects handled by the
|
|
UI and starting CV can be written as:</p>
|
|
|
|
<pre>
|
|
<display item="mastcount" label="15" tooltip="128"/>
|
|
</pre>
|
|
<p>The "<strong>mastcount</strong>" is an arbitrary (but unique) name. Name it so after the
|
|
value's meaning to your decoder. It will be used in <em>selectors</em> to access the value
|
|
like this:</p>
|
|
|
|
<pre>
|
|
<xsl:variable name="cvStart" select="string($root/display[@item='mastcount']/@tooltip)"/>
|
|
</pre>
|
|
<ul>
|
|
<li><strong>$root</strong> is the parameter / variable that contains root of the data.</li>
|
|
|
|
<li><strong>mastcount</strong> is the name of the <strong>display</strong> element - your
|
|
value.</li>
|
|
|
|
<li><strong>@tooltip</strong> means that the selector will read the
|
|
<strong>tooltip</strong> attribute. You may use @label to access the other one.</li>
|
|
</ul>
|
|
|
|
<h4>Enumerations, sequences, lists</h4>
|
|
|
|
<p>Sometimes a CV (variable, display item) should be generated for e.g. each output identified
|
|
by a name, or number. The list can be coded as a series of <strong><label></strong>
|
|
sub-elements of a <strong><display></strong> element:</p>
|
|
|
|
<pre>
|
|
<display item="masts" tooltip="512">
|
|
<label>0</label><label>1</label><label>2</label><label>3</label><label>4</label><label>5</label><label>6</label><label>7</label>
|
|
<label>8</label><label>9</label><label>10</label><label>11</label><label>12</label><label>13</label><label>14</label><label>15</label>
|
|
</display>
|
|
</pre>
|
|
<p>We then may either iterate those items one by one, or access them by index/position as
|
|
needed. The following examples selects the <strong>masts</strong> data item under the data
|
|
root (see above for data root). For <strong>each of the items</strong> it calls another
|
|
template (not shown here), and passes the item's value (encoded into the label element
|
|
content) to the template as <strong>mast</strong> parameter:</p>
|
|
|
|
<pre>
|
|
<xsl:template name="generate-panes">
|
|
<xsl:param name="root"/>
|
|
|
|
<xsl:for-each select="$root/display[@item='masts']/label">
|
|
<xsl:variable name="mast" select="string(./text())"/>
|
|
<xsl:call-template name="mast-pane">
|
|
<xsl:with-param name="root" select="$root"/>
|
|
<xsl:with-param name="mast" select="$mast"/>
|
|
</xsl:call-template>
|
|
</xsl:for-each>
|
|
</xsl:template>
|
|
</pre>
|
|
<p>Note, that element <strong>content</strong> is used as a value here - this allows to use
|
|
all awkward characters like quotes, doublequotes, ">" and other chars not permitted in
|
|
attributes.</p>
|
|
|
|
<p>Individual items may be accessed by their index (which is passed as a parameter):</p>
|
|
|
|
<pre>
|
|
<xsl:template name="generate-one-panes">
|
|
<xsl:param name="root"/>
|
|
<xsl:param name="index"/>
|
|
|
|
<xsl:variable name="mast" select="string($root/display[@item='masts']/label[position() = $index]/text())"/>
|
|
<xsl:call-template name="mast-pane">
|
|
<xsl:with-param name="root" select="$root"/>
|
|
<xsl:with-param name="mast" select="$mast"/>
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
</pre>
|
|
<p>You can easily use the above label list to make a loop from 1 to 15, which directly not
|
|
possible in XSLT. Instead of controlling the loop by a <em>control index variable</em>, we
|
|
control the loop by <em>the data that should apply in individual cycle iterations</em> and
|
|
derive the index variable from them. Here's the modified example:</p>
|
|
|
|
<pre>
|
|
<xsl:template name="generate-panes">
|
|
<xsl:param name="root"/>
|
|
<strong><-- The loop count is controlled by the number of <em>label</em> variables --></strong>
|
|
<xsl:for-each select="$root/display[@item='masts']/label">
|
|
<xsl:variable name="mast" select="string(./text())"/>
|
|
<xsl:call-template name="mast-pane">
|
|
<xsl:with-param name="root" select="$root"/>
|
|
<xsl:with-param name="mast" select="$mast"/>
|
|
<strong><-- We use the current label's element <em>position</em> to derive the
|
|
"loop control variable" value --></strong>
|
|
<xsl:with-param name="index" select="./position()"/>
|
|
</xsl:call-template>
|
|
</xsl:for-each>
|
|
</xsl:template>
|
|
</pre>
|
|
<h3>Cycles and loops</h3>
|
|
|
|
<p>XSLT language is a declarative one, and variables, once assigned, cannot be changed - so
|
|
it does not have a <strong>loop construct</strong> as most programming languages do.
|
|
Sometimes, a cycle can be more illustratively replaced by iteration over the content.
|
|
Sometimes it is not possible: truly some fixed number of iterations need to be done, such as
|
|
<strong>generating sequential CVs</strong> with the same structure - just the sequence number
|
|
and the represented function index will differ.</p>
|
|
|
|
<p>This can be done by <strong>tail recursion</strong>, which replaces loops by invoking a
|
|
template from that template itself. The only caveat is that the number of iterations is
|
|
<strong>limited</strong> to about 100 (?), before the stack space is exhausted. The example
|
|
can be found in <code><a href=
|
|
"https://github.com/JMRI/JMRI/blob/master/xml/decoders/TamValleyDepot_QuadLn_S_11.xsl">TamsValleyDepot_QuadLn_s_11.xsl</a></code>,
|
|
look for template <code>AllLEDGroups</code>:</p>
|
|
|
|
<pre>
|
|
<xsl:template name="AllLEDGroups">
|
|
<strong><-- Use <em>select=""</em> attribute to pick an initial value for the cycle.
|
|
Applies if the template does not get parameter on (first) invocation --></strong>
|
|
<xsl:param name="CV1" select="633"/>
|
|
<xsl:param name="CV2" select="513"/>
|
|
<xsl:param name="CV3" select="537"/>
|
|
<strong><-- This is the loop's control variable --></strong>
|
|
<xsl:param name="index" select="1"/>
|
|
<strong><!-- next line controls count --></strong>
|
|
<xsl:if test="24 >= $index">
|
|
<xsl:call-template name="OneLEDGroup">
|
|
<xsl:with-param name="CV1" select="$CV1"/>
|
|
<xsl:with-param name="CV2" select="$CV2"/>
|
|
<xsl:with-param name="CV3" select="$CV3"/>
|
|
<xsl:with-param name="index" select="$index"/>
|
|
</xsl:call-template>
|
|
<!-- iterate until done -->
|
|
<strong><-- The <em>if</em> a few lines above makes sure, this <em>call-template</em>
|
|
will not be executed for i > 24 --></strong>
|
|
<xsl:call-template name="AllLEDGroups">
|
|
<xsl:with-param name="CV1" select="$CV1 +1"/>
|
|
<xsl:with-param name="CV2" select="$CV2 +1"/>
|
|
<xsl:with-param name="CV3" select="$CV3 +2"/>
|
|
<strong><-- Call itself, but with control variable one higher, therefore counting
|
|
the number of cycles--></strong>
|
|
<xsl:with-param name="index" select="$index+1"/>
|
|
</xsl:call-template>
|
|
</xsl:if>
|
|
</xsl:template>
|
|
</pre>
|
|
<h2>Creating the stylesheet</h2>
|
|
|
|
<h3>A boilerplate</h3>
|
|
|
|
<p>The template is likely to have some boilerplate instructions. The following declarations
|
|
should be at the top, defining how output will be generated:</p>
|
|
|
|
<pre>
|
|
<?xml version="1.0" encoding="utf-8"?>
|
|
<xsl:stylesheet version="1.0"
|
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
xmlns:db="http://docbook.org/ns/docbook"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude"
|
|
|
|
exclude-result-prefixes="db">
|
|
|
|
|
|
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
|
|
<xsl:strip-space elements=""/>
|
|
<xsl:preserve-space elements="text"/>
|
|
</xsl:stylesheet>
|
|
</pre>
|
|
<p>The following will <strong>copy elements, and their attributes</strong> to the output:</p>
|
|
|
|
<pre>
|
|
<xsl:template match="@*|node()">
|
|
<xsl:copy>
|
|
<xsl:apply-templates select="@*|node()"/>
|
|
</xsl:copy>
|
|
</xsl:template>
|
|
</pre>
|
|
<h3>Generating content to the insertion points</h3>
|
|
|
|
<p>Variable definitions are usually generated by the stylesheet. Basic and fixed variables
|
|
should be provided, as usual, in the <code><variables></code> element. The stylesheet
|
|
can then <strong>append generated variables</strong> at the end:</p>
|
|
|
|
<pre>
|
|
<xsl:template match="variables">
|
|
<variables>
|
|
<xsl:copy-of select="node()"/>
|
|
<!-- call-template instructions, that generate the content; example follows -->
|
|
<xsl:call-template name="generate-masts">
|
|
<xsl:with-param name="root" select="//pane[name/text() ='__Aspects']//display[position() = 1]/.."/>
|
|
</xsl:call-template>
|
|
|
|
<xsl:call-template name="generate-aspects">
|
|
<xsl:with-param name="root" select="//pane[name/text() ='__Aspects']//display[position() = 1]/.."/>
|
|
</xsl:call-template>
|
|
</variables>
|
|
</xsl:template>
|
|
</pre>
|
|
<p>Note that, in this example, the <code>pane</code> element with a special name
|
|
(<code>__Aspects</code>) is used as a holder for input data for generation. While
|
|
<code>//pane[name/text() == '__Aspects']</code> selects the data holder, the
|
|
<code>//display[position() = 1]/..</code> selects an element <strong>within</strong> the
|
|
holder pane XML element. <strong>Pay attention to typos</strong> in the strings, otherwise
|
|
the select clauses select empty data, and nothing - or invalid content - will be
|
|
generated.</p>
|
|
|
|
<p>For <strong>UI Panels</strong> I recommend to <strong>replace</strong> the data holder
|
|
with the sequence of generated panels. In my example, data is provided from panel named
|
|
<strong>__Aspects</strong>, which we definitely <strong>do not want</strong> to be displayed
|
|
in DecoderPro as it ... isn't any UI panel, after all. The following will
|
|
<strong>replace</strong> the data holder (a top-level Pane) with panels generated by the
|
|
stylesheet:</p>
|
|
|
|
<pre>
|
|
<xsl:template match="pane[name='__Aspects']" priority="100">
|
|
<!-- call-template instructions for individual groups of panels to be generated; example follows -->
|
|
<xsl:call-template name="generate-panes">
|
|
<xsl:with-param name="root" select="//pane[name/text() ='__Aspects']//display[position() = 1]/.."/>
|
|
</xsl:call-template>
|
|
</xsl:template>
|
|
</pre>
|
|
<p>The <code>match</code> clause will react on the <code>__Aspect</code> data holder pane
|
|
element, but unlike the variables insertion point, <strong>no copy instruction is
|
|
present</strong>. So the old content will be thrown away (entire <code><pane></code>
|
|
element!), replaced by whatever elements the <code>call-template</code> instructions
|
|
generate.</p>
|
|
|
|
<h2>Using XML fragments</h2>
|
|
|
|
<p>If part of the generated content <strong>does not change</strong> from place to place, it
|
|
is possible to prepare it as a <strong>XML fragment</strong> to be included: it won't be a
|
|
part of XSL stylesheet with all those strange <em>xsl:xxx</em> instructions, but will stored
|
|
as a separate, small and clean bit of XML. This can be useful for <strong>choice
|
|
values</strong>, or even repeated <strong>UI panels</strong> without variable content. An
|
|
example of the usage is again in <code><a href=
|
|
"https://github.com/JMRI/JMRI/blob/master/xml/decoders/TamValleyDepot_QuadLn_S_11.xsl">TamsValleyDepot_QuadLn_s_11.xsl</a></code>.
|
|
(LedPaneHeader)</p>
|
|
|
|
<p>Individual variables are generates using <em>xsl:template</em>, but the value part, mostly
|
|
a <em>choice</em> is included from a separate file. Note that the <em>xi:include</em> will be
|
|
generated into the resulting XML, so it is the DecoderPro panel reader, who does the content
|
|
inclusion, not the generator. The template substitutes the variable parts of the
|
|
definition:</p>
|
|
|
|
<pre>
|
|
<variable item="Aspect{$index} LED1 Out" CV="{$CV2}" mask="XXXVVVVV" default="0">
|
|
<xi:include href="http://jmri.org/xml/decoders/tvd/LedOutput.xml"/>
|
|
</variable>
|
|
</pre>
|
|
<p>There are two important things. When using <em>xi:</em> prefix, that <strong>prefix must
|
|
be declared</strong> at the top of the document (maybe in any parent element, but
|
|
conventionally prefixes are collected at the start). Use exactly the same URL (attribute
|
|
value), otherwise the instruction won't be recognized.</p>
|
|
|
|
<pre>
|
|
<xsl:stylesheet version="1.0"
|
|
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
|
|
xmlns:db="http://docbook.org/ns/docbook"
|
|
xmlns:xi="http://www.w3.org/2001/XInclude" <strong>-- this is the prefix declaration</strong>
|
|
>
|
|
</pre>
|
|
<p>The second issue is that the <em>xi:include</em> must use URLs that JMRI is able to
|
|
<strong>resolve locally</strong>. Otherwise, the DecoderPro would attempt to download parts
|
|
of the definition from the Internet, which requires an online connection - and is slow. The
|
|
prefix <strong>http://jmri.org/xml</strong> is guaranteed to resolve to the
|
|
<strong>xml</strong> directory of your local JMRI installation. For more mapping, please see
|
|
other JMRI documentation.</p>
|
|
<!--#include virtual="/help/en/parts/Footer.shtml" -->
|
|
</div>
|
|
<!-- close #mainContent -->
|
|
</div>
|
|
<!-- close #mBody -->
|
|
<script src="/js/help.js"></script>
|
|
</body>
|
|
</html>
|