Cause: - ResultsWriter required a generic way to generate customer-specific XML result data from TBF measurement results. - The customer reference XML contains example runtime values and repeated result structures, so it cannot be used directly as the generated output. - TBF result variables must be explicitly mapped to destinations in the customer XML structure. - The generated XML result data must support two output targets: - direct creation of an XML file, - delivery of the XML payload to a Microsoft SQL stored procedure. - Preview generation must allow the XML structure and configured mappings to be verified without executing the production database write. - Increase revision to 3.9.3145.100. Solution: 1. Added XML reference analysis - Creates a clean base XML structure. - Extracts the repeating result prototype. - Prevents sample runtime values from the customer reference XML from leaking into generated results. 2. Added configurable TBF-to-XML result mapping - Allows explicit mapping of TBF result variables to customer XML destinations. - Supports one-time and repeating XML destinations. - Keeps the mapping independent of the semantic meaning of customer XML attribute names. 3. Added XML destination viewer and configurator - Shows the current mapping. - Highlights repeating destinations. - Identifies already used one-time destinations. 4. Added runtime XML result generation - Uses the configured TBF-to-XML mappings. - Uses measurement procedure result data. - Builds the output from the clean base XML and repeating XML prototype. - Generates repeated result records according to the executed measurement procedure. 5. Added simulation-based Preview request - Generates a complete XML payload using simulation values. - Allows XML structure and mapping verification before production execution. - Does not execute the production stored procedure. 6. Added direct XML file output support to UniDataStorageWriter - Supports File / .xml as a physical output target. - Creates the generated XML result file in the configured output directory. 7. Added Microsoft SQL stored-procedure XML output - Supports Microsoft SQL / StoredProcedure as a physical output target. - Passes the generated XML payload through the configured stored procedure parameter. 8. Added optional XML payload archiving - Allows generated XML payloads to be stored in the configured Payload archive. - Can be used together with the Microsoft SQL stored-procedure output. 9. Kept ResultsWriter independent of the physical output target - ResultsWriter generates the result payload. - UniDataStorageWriter decides how and where the payload is physically written. - The same ResultsWriter XML generation mechanism is therefore used for both XML file and MSSQL outputs. 10. Increased revision - Updated revision to 3.9.3145.100.
307 lines
8.0 KiB
C#
307 lines
8.0 KiB
C#
///
|
|
/// Copyright (c) 2026 Sensus Slovensko a.s.
|
|
///
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Windows.Forms;
|
|
using System.Xml.Linq;
|
|
|
|
namespace TBF.Rig.Output.DataStorage.UniDataStorageWriter.UI
|
|
{
|
|
/// <summary>
|
|
/// Displays one XML stored-procedure payload in a read-only tree and raw XML view.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This dialog is intended for diagnostic inspection of the exact XML value
|
|
/// that will be supplied to a stored procedure parameter.
|
|
/// </remarks>
|
|
public partial class XmlPayloadViewerDlg : Form
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the stored-procedure parameter name.
|
|
/// </summary>
|
|
public string ParameterName
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the XML payload displayed by the dialog.
|
|
/// </summary>
|
|
public string XmlPayload
|
|
{
|
|
get;
|
|
set;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new XML payload viewer.
|
|
/// </summary>
|
|
public XmlPayloadViewerDlg()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
private void XmlPayloadViewerDlg_Load(
|
|
object sender,
|
|
EventArgs e)
|
|
{
|
|
Text =
|
|
string.IsNullOrWhiteSpace(
|
|
ParameterName)
|
|
? "XML payload viewer"
|
|
: "XML payload viewer - " +
|
|
ParameterName;
|
|
|
|
parameterNameTextBox.Text =
|
|
ParameterName ??
|
|
string.Empty;
|
|
|
|
LoadPayload();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Parses and displays the configured XML payload.
|
|
/// </summary>
|
|
private void LoadPayload()
|
|
{
|
|
structureTreeView.Nodes.Clear();
|
|
rawXmlTextBox.Clear();
|
|
|
|
if (string.IsNullOrWhiteSpace(
|
|
XmlPayload))
|
|
{
|
|
statusLabel.Text =
|
|
"XML payload is empty.";
|
|
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
XDocument document =
|
|
XDocument.Parse(
|
|
XmlPayload,
|
|
LoadOptions.PreserveWhitespace);
|
|
|
|
rawXmlTextBox.Text =
|
|
document.ToString();
|
|
|
|
if (document.Root != null)
|
|
{
|
|
TreeNode rootNode =
|
|
CreateElementNode(
|
|
document.Root);
|
|
|
|
structureTreeView.Nodes.Add(
|
|
rootNode);
|
|
|
|
rootNode.Expand();
|
|
|
|
ExpandInitialLevels(
|
|
rootNode,
|
|
2);
|
|
}
|
|
|
|
statusLabel.Text =
|
|
"Valid XML payload.";
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
//
|
|
// Preserve the exact supplied text even when it is invalid XML.
|
|
//
|
|
rawXmlTextBox.Text =
|
|
XmlPayload;
|
|
|
|
statusLabel.Text =
|
|
"Invalid XML: " +
|
|
ex.Message;
|
|
|
|
tabControl.SelectedTab =
|
|
rawXmlTabPage;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a tree node for one XML element.
|
|
/// </summary>
|
|
private TreeNode CreateElementNode(
|
|
XElement element)
|
|
{
|
|
TreeNode elementNode =
|
|
new TreeNode(
|
|
element.Name.LocalName);
|
|
|
|
foreach (XAttribute attribute
|
|
in element.Attributes())
|
|
{
|
|
string attributeName =
|
|
attribute.IsNamespaceDeclaration
|
|
? "xmlns" +
|
|
(attribute.Name.LocalName == "xmlns"
|
|
? string.Empty
|
|
: ":" + attribute.Name.LocalName)
|
|
: "@" +
|
|
attribute.Name.LocalName;
|
|
|
|
TreeNode attributeNode =
|
|
new TreeNode(
|
|
string.Format(
|
|
"{0} = {1}",
|
|
attributeName,
|
|
FormatValue(
|
|
attribute.Value)));
|
|
|
|
attributeNode.ToolTipText =
|
|
attribute.Value ??
|
|
string.Empty;
|
|
|
|
elementNode.Nodes.Add(
|
|
attributeNode);
|
|
}
|
|
|
|
foreach (XElement child
|
|
in element.Elements())
|
|
{
|
|
elementNode.Nodes.Add(
|
|
CreateElementNode(
|
|
child));
|
|
}
|
|
|
|
if (!element.Elements().Any())
|
|
{
|
|
string textValue =
|
|
string.Concat(
|
|
element.Nodes()
|
|
.OfType<XText>()
|
|
.Select(
|
|
node =>
|
|
node.Value));
|
|
|
|
if (!string.IsNullOrWhiteSpace(
|
|
textValue))
|
|
{
|
|
TreeNode textNode =
|
|
new TreeNode(
|
|
"#text = " +
|
|
FormatValue(
|
|
textValue));
|
|
|
|
textNode.ToolTipText =
|
|
textValue;
|
|
|
|
elementNode.Nodes.Add(
|
|
textNode);
|
|
}
|
|
}
|
|
|
|
return elementNode;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Formats a potentially long XML value for the tree.
|
|
/// </summary>
|
|
private string FormatValue(
|
|
string value)
|
|
{
|
|
if (value == null)
|
|
return string.Empty;
|
|
|
|
const int maxLength =
|
|
160;
|
|
|
|
string singleLine =
|
|
value
|
|
.Replace(
|
|
"\r",
|
|
" ")
|
|
.Replace(
|
|
"\n",
|
|
" ");
|
|
|
|
if (singleLine.Length <=
|
|
maxLength)
|
|
{
|
|
return singleLine;
|
|
}
|
|
|
|
return singleLine.Substring(
|
|
0,
|
|
maxLength) +
|
|
"...";
|
|
}
|
|
|
|
/// <summary>
|
|
/// Expands the first levels of the XML tree without expanding a large
|
|
/// payload completely.
|
|
/// </summary>
|
|
private void ExpandInitialLevels(
|
|
TreeNode node,
|
|
int remainingLevels)
|
|
{
|
|
if (node == null ||
|
|
remainingLevels < 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
node.Expand();
|
|
|
|
if (remainingLevels == 0)
|
|
return;
|
|
|
|
foreach (TreeNode child
|
|
in node.Nodes)
|
|
{
|
|
if (child.Nodes.Count > 0)
|
|
{
|
|
ExpandInitialLevels(
|
|
child,
|
|
remainingLevels - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void expandAllButton_Click(
|
|
object sender,
|
|
EventArgs e)
|
|
{
|
|
structureTreeView.ExpandAll();
|
|
}
|
|
|
|
private void collapseAllButton_Click(
|
|
object sender,
|
|
EventArgs e)
|
|
{
|
|
structureTreeView.CollapseAll();
|
|
|
|
if (structureTreeView.Nodes.Count > 0)
|
|
{
|
|
structureTreeView.Nodes[0]
|
|
.Expand();
|
|
}
|
|
}
|
|
|
|
private void copyXmlButton_Click(
|
|
object sender,
|
|
EventArgs e)
|
|
{
|
|
if (!string.IsNullOrEmpty(
|
|
rawXmlTextBox.Text))
|
|
{
|
|
Clipboard.SetText(
|
|
rawXmlTextBox.Text);
|
|
}
|
|
}
|
|
|
|
private void closeButton_Click(
|
|
object sender,
|
|
EventArgs e)
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
} |