65.9K
CodeProject 正在变化。 阅读更多。
Home

工程网站的快速开发

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.95/5 (10投票s)

2012年11月1日

CPOL

12分钟阅读

viewsIcon

21837

downloadIcon

366

从桌面到 Web。

有用链接

1. 引言

2002年,我曾预言我的软件项目应该有Web版本。最近(2012年10月),我开始实现这个想法。然而,十年来我开发的软件已经使其易于适应Web。所以我又一次
重新发明
时间机器。我关于快速开发工程Web应用的想法与LabVIEW的想法相似。LabVIEW同时支持业务逻辑和用户界面的图形化编程。
 


LabVIEW Graphical Programming

我的方法也是如此。下图说明了这一点。

Bisiness + UI

桌面窗口应用程序使我们能够开发业务逻辑。然后XAML设计器实现业务逻辑与Silverlight用户界面的互操作性。所以这个软件包含桌面窗口应用程序和Web应用程序。两者都仅为评估目的而开发。因此,Visual Studio Express 2012 for Windows Desktop
Visual Studio Express 2012 for Web被用于开发这些应用程序。  评估Web客户端是原始文章的框架。原始文章包含桌面应用程序源代码和其他有用资源。

2. 背景

在这里,我们将考虑业务逻辑的一般元素和相应的Silverlight UI元素。

2.1 异常处理

许多软件包含如下代码:

void z_error (char *m)
{ 
         fprintf(stderr, "%s\n", m); 
         exit(1); 
} 
     

             try
            {
                // Do something ...
            }
            catch (Exception exception)
            {
                MessageBox.Show(exception.Message);
            }

上述代码不够灵活,无法轻松用于Web应用程序。所以我更喜欢一种具有纯抽象异常处理和扩展方法的构造。

     /// <summary>
    /// The error handler
    /// </summary>
    public interface IErrorHandler
    {
        /// <summary>
        /// Shows error
        /// </summary>
        /// <param name="exception">Exception</param>
        /// <param name="obj">Attached object</param>
        void ShowError(Exception exception, object obj);
 
        /// <summary>
        /// Shows message
        /// </summary>
        /// <param name="message">The message to show</param>
        /// <param name="obj">Attached object</param>
        void ShowMessage(string message, object obj);
    }     

 

         /// <summary>
        /// Shows exception (extension method)
        /// </summary>
        /// <param name="exception">Exception</param>
        /// <param name="obj">Attached object</param>
        static public void ShowError(this Exception exception, object obj = null)
        {
            if (errorHandler != null) // Static exception handler
            {
                errorHandler.ShowError(exception, obj);
            }
        } 

现在异常处理看起来是这样的:

             try
            {
                // Do something ...
            }
            catch (Exception exception)
            {
                  exception.ShowError();
            }

桌面应用程序可以具有以下IErrorHandler接口的实现:

         void IErrorHandler.ShowError(Exception exception, object obj)
        {
            MessageBox.Show(this, exception.Message);
        }

然而,我更喜欢以下UI的错误处理:

Error windows message

上述错误处理的Web版本看起来是这样的:

<>
N 信号强度 Message 堆栈跟踪
1 10 NaN 在EngineeringInitializer.BasicEngineeringInitializer中。b__0(Object o) in c:\AUsers\1MySoft\CSharp\src\src\EngineeringInitializer\BasicEngineeringInitializer.cs:line 72 at Calculation.Calculate.Update() in c:\DWASFiles\sites\orbitalforecast\Temp\agffdids.0.cs:line 90 at DataPerformer.DifferentialEquationSolver.CalculateDerivations()
2 10 NaN at Diagram.UI.StaticExtensionDiagramUI.Throw(Object o, Exception exception) at DataPerformer.DataConsumer.UpdateChildrenData() at DataPerformer.VectorFormulaConsumer.UpdateMeasurements()
3 10 NaN at Diagram.UI.StaticExtensionDiagramUI.Throw(Object o, Exception exception) at DataPerformer.DataConsumer.UpdateChildrenData() at DataPerformer.VectorFormulaConsumer.UpdateMeasurements()
4 10 NaN at Diagram.UI.StaticExtensionDiagramUI.Throw(Object o, Exception exception) at DataPerformer.DataConsumer.UpdateChildrenData() at DataPerformer.VectorFormulaConsumer.UpdateMeasurements()

这种错误处理具有服务器和客户端两层。以下代码是服务器端的实现。

以下代码包含服务器端实现:
         /// <summary>
        /// List of exceptions
        /// </summary>
        private List<object[]> exceptions = new List<object[]>();
 
          
         #region IErrorHandler Menbers
 
        /// <summary>
        /// Shows error
        /// </summary>
        /// <param name="exception">Exception</param>
        /// <param name="obj">Attached object</param>
        public virtual void ShowError(Exception exception, object obj)
        {
            exceptions.Add(new object[] { exception, obj + "" });
            onException(exception, obj);
        }
 
        #endregion
 
        /// <summary>
        /// Creates error report
        /// </summary>
        /// <param name="doc">report doocument</param>
        public void CreateErrorReport(XmlDocument doc)
        {
            if (exceptions.Count != 0)
            {
                XmlElement el = doc.CreateElement("Exceptions");
                doc.DocumentElement.AppendChild(el);
                foreach (object[] o in exceptions)
                {
                    XmlElement e = doc.CreateElement("Exception");
                    el.AppendChild(e);
                    XmlElement eo = doc.CreateElement("Object");
                    eo.InnerText = o[1] + "";
                    e.AppendChild(eo);
                    XmlElement eex = doc.CreateElement("StackTrace");
                    e.AppendChild(eex);
                    eex.InnerText = (o[0] as Exception).StackTrace;
                    XmlElement em = doc.CreateElement("ExceptionMessage");
                    e.AppendChild(em);
                    em.InnerText = (o[0] as Exception).Message;
                    XmlElement et = doc.CreateElement("ExceptionType");
                    e.AppendChild(et);
                    et.InnerText = (o[0] as Exception).GetType() + "";
                }
                exceptions.Clear(); // Clears list of exceptions
            }
        }

服务器端将异常收集到列表中,然后将异常信息附加到XML文档中。然后将文档发送到客户端。
客户端也有自己的错误处理接口:

     /// <summary>
    /// Shows errors
    /// </summary>
    public interface IErrorHandler
    {
        /// <summary>
        /// Shows errors
        /// </summary>
        /// <param name="x">Errors</param>
        void ShowErrorReport(XElement x);
    }
 

实现了此接口的每个客户端对象都会显示错误报告。以下代码包含该接口的实现。

     /// <summary>
    /// HTML Error hander
    /// </summary>
    public partial class SilverlightControlHtmlErrorHandler : UserControl, IErrorHandler
    {
        #region Fields
 
        /// <summary>
        /// Id of html element
        /// </summary>
        string elementID;
 
        /// <summary>
        /// Name of script function
        /// </summary>
        string scriptFunction;
 
        #endregion
 
        #region Ctor
 
        /// <summary>
        /// Constructor
        /// </summary>
        public SilverlightControlHtmlErrorHandler()
        {
            InitializeComponent();
        }
 

        #endregion
 
        #region IErrorHandler Members
 
        void IErrorHandler.ShowErrorReport(XElement x)
        {
            XElement xe = x.Element("Exceptions");
            if (xe == null)
            {
                return;
            }
            IEnumerable<XElement> l = xe.Elements("Exception");
            // Creates table text
            string text = "<table border=\"1\"><tr><td>N</td><td>Level</td><td>Message</td><td>Stack trace</td></tr>";
            int i = 1;
            foreach (XElement e in l)
            {
                text += "<tr><td>" + i + "</td><td>" + e.Element("Object").Value + "</td><td>" + 
                    e.Element("ExceptionMessage").Value + "</td><td>" + e.Element("StackTrace").Value + "</td></tr>";
                ++i;
            }
            text += "</table>";
            Action<string> acttext = WriteText; // If script function does not exist
            if (scriptFunction != null)
            {
                if (scriptFunction.Length > 0)
                {
                    acttext = WriteScript; // If script function exists 
                }
            }
           Action act = () =>
                {
                    acttext(text);
                };
            Dispatcher.BeginInvoke(act);
 
        }
 
        #endregion
 
        #region Members
 
        #region Public
 
        /// <summary>
        /// Script Function
        /// </summary>
        public string ScriptFunction
        {
            get
            {
                return scriptFunction;
            }
            set
            {
                scriptFunction = value;
            }
        }
 

 
        /// <summary>
        /// Id of HTML output
        /// </summary>
        public string HtmlId
        {
            get
            {
                return elementID;
            }
            set
            {
                elementID = value;
            }
        }
 
        #endregion
 
        #region Private
 
        /// <summary>
        /// Writes inner HTML
        /// </summary>
        /// <param name="text">Text to write</param>
        void WriteText(string text)   
        {
            HtmlDocument doc = HtmlPage.Document;
            HtmlElement element = doc.GetElementById(elementID);
            if (element != null)
            {
                element.SetAttribute("innerHTML", text);
            }
        }
        
        /// <summary>
        /// Calls script function
        /// </summary>
        /// <param name="text">Argument of function</param>
        void WriteScript(string text)
        {
            HtmlPage.Window.Invoke(scriptFunction, text);
        }
 
        #endregion
 
        #endregion

此组件将异常报告显示为内部HTML或调用脚本函数。以下片段包含错误处理函数的示例。

  <script type="text/javascript">
 
      // Shows error 
      // text is error text
      function showScriptError(text) {
          var div_preview = document.getElementById("Errors");
          div_preview.innerHTML = text;
      }
</script>
 

<div id="Errors"></div>
 
一些Web客户端元素应与错误处理程序关联。这些元素实现以下接口:
     /// <summary>
    /// Consumer of error handler
    /// </summary>
    public interface IErrorHandlerConsumer
    {
        /// <summary>
        /// Consumer of error handler
        /// </summary>
        IErrorHandler ErrorHandler
        {
            get;
            set;
        }
    }
 

开发人员不应显式关联错误处理程序。此操作由以下函数隐式执行:

         /// <summary>
        /// Recursive action
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="obj">Object</param>
        /// <param name="action">Action</param>
        public static void RecursiveAction<T>(this DependencyObject obj, Action<T> action) where T : class
        {
            if (obj is T)
            {
                action(obj as T);
            }
            int childrenCount = VisualTreeHelper.GetChildrenCount(obj);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(obj, i);
                if (child is DependencyObject)
                {
                    (child as DependencyObject).RecursiveAction<T>(action);
                }
            }
        }
 
        /// <summary>
        /// Gets root of objet
        /// </summary>
        /// <param name="element">The element</param>
        /// <returns>The root</returns>
        public static DependencyObject GetRoot(this DependencyObject element)
        {
            if (element is FrameworkElement)
            {
                DependencyObject dob = (element as FrameworkElement).Parent;
                if (!(dob is FrameworkElement))
                {
                    return element;
                }
                return GetRoot(dob);
            }
            return null;
        }
 
                              /// <summary>
        /// Root Recursive action
        /// </summary>
        /// <typeparam name="T">Type</typeparam>
        /// <param name="obj">Object</param>
        /// <param name="action">Action</param>
        public static void RootRecursiveAction<T>(this DependencyObject obj, Action<T> action)
            where T : class
        {
            DependencyObject d = obj.GetRoot();
            d.RecursiveAction<T>(action);
        }
                       /// <summary>
        /// Finds error handler
        /// </summary>
        /// <param name="consumer">Consumer of error handler</param>
        public static void FindErrorHandler(this ) consumer)
        {
            DependencyObject dob = consumer as DependencyObject;
            IErrorHandler eh = null;
            Action<IErrorHandler> find = (IErrorHandler h) =>
            {
                if (eh == null)
                {
                    eh = h;
                    return;
                }
            };
            dob.RootRecursiveAction<IErrorHandler>(find);
            if (eh != null)
            {
                consumer.ErrorHandler = eh;
            }
        } 

以下XAML代码:

<UserControl xmlns:SilverlightDiagramUI="clr-namespace:Diagram.UI.SilverlightDiagramUI;
    assembly=Diagram.UI.SilverlightDiagramUI" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:DataPerformer.UI.SilverlightDataPerformer" 
    x:Class="DataPerformer.UI.SilverlightDataPerformer.SilverlightControlChartInputOutput"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"><StackPanel>
         <Grid x:Name="LayoutRoot" ShowGridLines="True">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
             <SilverlightDiagramUI:SilverlightControlAlias x:Name="Aliases" Grid.Column="0" 
             Grid.Row="0"  HorizontalAlignment="Stretch" Margin="0,0,0,0" />
            <local:SilverlightControlHtmlOutput x:Name="Chart"  Grid.Column="0" Grid.Row="1" />
            <Button click="Button_Click" content="Start" grid.row="2" 
            horizontalalignment="Stretch" margin="0,0,0,0" width="75" x:name="Start"/>
            <local:SilverlightControlHtmlErrorHandler x:Name="Errors" 
            HorizontalAlignment="Left" Height="0" Grid.Row="2" Width="0" Visibility="Collapsed"/>
        </Grid>
    </StackPanel>
</UserControl>

包含一个IErrorHandler(即SilverlightControlHtmlErrorHandler)和两个IErrorHandlerConsumer对象(SilverlightControlAliasSilverlightControlHtmlOutput。这两个消费者都自动与错误处理程序关联,因为这三个元素有共同的根。下图展示了IErrorHandlerIErrorHandlerConsumer之间的互操作性。以下序列图解释了异常处理程序的用法。

Error handler sequence

2.2 客户端接口

2.2.1 初始化接口

一些UI元素在出现后应进行初始化。以下模式代表此类初始化:

Initialization sequence

所有需要此类初始化的对象都实现IPostLoadService接口。

2.2.2 附加信息接口

许多用户控件包含子控件。通常,对服务器的请求包含所有子控件中的信息。以下控件:

Children controls

有两个子控件。“绿色”子控件生成以下XML:

 <Aliases Desktop="Exponent.cfa">
    <Alias>
      <Name>Exp.a</Name>
      <Value>0.7</Value>
      <Type>System.Double</Type>
    </Alias>
    <Alias>
      <Name>Exp.b</Name>
      <Value>0.3</Value>
      <Type>System.Double</Type>
    </Alias>
  </Aliases>

“蓝色”组件生成:

<ChartDocument Chart="ExponentChart.xml" Desktop="Exponent.cfa">
  <Parameters>
    <Start>0</Start>
    <Step>1000</Step>
    <Finish>20000</Finish>
    <Xslt>Exponent.xslt</Xslt>
  </Parameters>
</ChartDocument>
结果,将以下XML发送到服务器:
<ChartDocument Chart="ExponentChart.xml" Desktop="Exponent.cfa">
  <Aliases Desktop="Exponent.cfa">
    <Alias>
      <Name>Exp.a</Name>
      <Value>0.7</Value>
      <Type>System.Double</Type>
    </Alias>
    <Alias>
      <Name>Exp.b</Name>
      <Value>0.3</Value>
      <Type>System.Double</Type>
    </Alias>
  </Aliases>
  <Parameters>
    <Start>0</Start>
    <Step>1000</Step>
    <Finish>20000</Finish>
    <Xslt>Exponent.xslt</Xslt>
  </Parameters>
</ChartDocument>

因此,“蓝色”控件的信息将附加到“绿色”控件的信息中。“蓝色控件实现以下接口:

     /// <summary>
    /// Adds information to request
    /// </summary>
    public interface IAddRequest
    {
        /// <summary>
        /// Adds request
        /// </summary>
        /// <param name="x">Request element</param>
        void Add(XElement x);
    }

所有IAddRequest对象都会将信息添加到请求中。下图说明了请求的逻辑:

IAddRequest Business Logic

请注意,不应显式调用add request操作。它由以下函数隐式调用:

        /// <summary>
        /// Adds information for request
        /// </summary>
        /// <param name="obj">Object</param>
        /// <param name="x">Request Xml</param>
        public static void AddRequest(this DependencyObject obj, XElement x)
        {
            Action<IAddRequest> action = (IAddRequest a) => { a.Add(x);};
            obj.RootRecursiveAction<IAddRequest>(action);
        }

2.3 常量输入

通常,每项工程任务都有输入常量。例如,计算函数f(t)=aebt有两个参数ab,可以被视为常量。软件应支持常量输入。

2.3.1 业务逻辑

下图代表上述任务的业务逻辑。

Simple Business Logic

其中,Exp组件计算f(t)=aebt。整个上图称为Desktop。任何桌面都对应一个实现IDesktop接口的对象。Exp的属性如下所示。

Exp properties

上图右下角包含ab常量的编辑器。Exp对象实现以下接口:

     /// <summary>
    /// Collection on named data units
    /// </summary>
    public interface IAlias : IAliasBase
    {
        /// <summary>
        /// Names of all data units
        /// </summary>
        IList<string> AliasNames
        {
            get;
        }
 
        /// <summary>
        /// Access to data unit by name
        /// </summary>
        object this[string name]
        {
            get;
            set;
        }
 
        /// <summary>
        /// Gets unit type
        /// </summary>
        /// <param name="name">Unit name</param>
        /// <returns>Type of unit</returns>
        object GetType(string name);
    }

以下代码解释了该接口的用法。

            IAlias alias = Exp as IAlias;
            IList<string> names = alias.AliasNames; // {"a", "b"};
            object a = alias["a"]; // a = 0.7 {double}
            object b = alias["b"]; // a = 0.3 {double}
            object t = alias.GetType("a"); // t = (double)0;
            double x = 0.8;
            alias["a"] = x;
            a = alias["a"]; // a = 0.8 {double}

可以通过以下方式从桌面访问别名:

            IDesktop desktop = ...;
            object a = desktop.GetAliasValue("Exp.a"); // a = 0.7 {double}
            object t = desktop.GetAliasType("Exp.a");  // t = (double)0;
            double x = 0.8;
            desktop.SetAliasValue("Exp.a", x);
            a = desktop.GetAliasValue("Exp.a");         // a = 0.8 {double} 

也可以通过以下函数设置别名:

        /// <summary>
        /// Set aliases of desktop
        /// </summary>
        /// <param name="desktop">Desktop</param>
        /// <param name="document">Document</param>
        public static void SetAliases(this IDesktop desktop, XmlDocument document)

如果文档(上述函数的第二个参数)包含以下元素:

  <Aliases Desktop="Exponent.cfa">
    <Alias>
      <Name>Exp.a</Name>
      <Value>0.7</Value>
      <Type>System.Double</Type>
    </Alias>
    <Alias>
      <Name>Exp.b</Name>
      <Value>0.3</Value>
      <Type>System.Double</Type>
    </Alias>
  </Aliases>

则调用该函数等同于以下代码:

desktop.SetAliasValue("Exp.a", (double)0.7);
desktop.SetAliasValue("Exp.b", (double)0.3);

常量输入的业务是调用具有适当参数的SetAliases函数。

2.3.2 客户端组件

以下用户控件隐式执行远程调用SetAliases。“隐式”一词意味着此任务不是独立的。每次设置别名都是主要任务的一个子任务。

     /// <summary>
    /// Alias control
    /// </summary>
    public partial class SilverlightControlAlias : UserControl, IPostLoadService, 
            IAddRequest, IErrorHandlerConsumer

它具有以下关键属性:

<SilverlightDiagramUI:SilverlightControlAlias Desktop="Exponent.cfa" Aliases="ExponentAliases.xml"  />

其中Desktop="Exponent.cfa"(相应地Aliases="ExponentAliases.xml")是桌面(相应地别名)的文件名。作为IPostLoadService,它发送以下初始化请求:

<Aliases Desktop="Exponent.cfa" Aliases="ExponentAliases.xml" />

根据文件"Exponent.cfa"ExponentAliases.xml"的内容,服务器生成以下响应:

<Aliases>
    <Item>
        <Number>1</Number>
        <Name> The "a" coefficient </Name>
        <Alias>Exp.a</Alias>
        <Value>0.7</Value>
        <Type>System.Double</Type>
    </Item>
    <Item>
        <Number>2</Number>
        <Name> The "b" coefficient </Name>
        <Alias>Exp.b</Alias>
        <Value>0.3</Value>
        <Type>System.Double</Type>
    </Item>
</Aliases>

然后,用户控件接收初始化响应,其外观会发生以下变化:

UI initialization


此组件实现IAddRequest接口。因此,它提供有关别名值的信息以供主要任务使用。附加信息在2.2.2中讨论。



2.3 图表输出

2.3.1 业务逻辑

我们的示例

SimpleBusinessLogic

包含Chart组件。除了图形模式,它还有文本模式。

Chart text mode

此模式生成以下XML:

<Root>
  <Parameters>
    <Parameter Name="Value" Value="0.7" />
    <Parameter Name="Argument" Value="0" />
  </Parameters>
  <Parameters>
    <Parameter Name="Value" Value="0.702103153152364" />
    <Parameter Name="Argument" Value="0.01" />
  </Parameters>
  <Parameters>
    <Parameter Name="Value" Value="0.704212625237845" />
    <Parameter Name="Argument" Value="0.02" />
  </Parameters>
  <Parameters>
    <Parameter Name="Value" Value="0.706328435241708" />
    <Parameter Name="Argument" Value="0.03" />
  </Parameters>
</Root>

此XML由以下函数返回:

        /// <summary>
        /// Creates Xml document
        /// </summary>
        /// <param name="desktop">Desktop</param>
        /// <param name="input">Input</param>
        /// <returns>Document</returns>
        static public XmlDocument CreateXmlDocument(this IDesktop desktop, XmlDocument input)

其中input由以下文本表示:

      <ChartDocument>
            <Interval>
                <Start>0</Start>
                <Step>0.01</Step>
                <Finish>0.03</Finish></Interval>
         <ChartName>Chart</ChartName>
         <Parameters>
             <Parameter>
                 <Name>Exp.Formula_1</Name>
                 <Value>Value</Value>
            </Parameter>
             <Parameter>
                 <Name>Exp.Formula_2</Name>
                 <Value>Argument</Value>
             </Parameter>
         </Parameters>
    </ChartDocument>     
 



2.3.2 客户端组件

客户端具有以下用户控件:

      /// <summary>
    /// Html output of chart
    /// </summary>
    public partial class SilverlightControlHtmlOutput : UserControl, IRequest, 
        IErrorHandlerConsumer, IPostLoadService

此控件具有以下关键属性:

<SilverlightControlHtmlOutput  Desktop="Exponent.cfa" Xslt="Exponent.xslt" 
    Chart="Exponent.xml" ScriptFunction="showOutput" />

此处Desktop="Exponent.cfa"是序列化桌面的文件名。Exponent.xml

<WriteText>;
  <Interval>
    <Start>0</Start>
    <Step>500</Step>
    <Finish>20000</Finish>
  </Interval>
  <ChartName>Chart</ChartName>
  <Argument>Time</Argument>
  <Parameters>
    <Parameter>
      <Name>Exp.Formula_1</Name>
      <Value>Value</Value>
    </Parameter>
    <Parameter>
      <Name>Exp.Formula_2</Name>
      <Value>Argument</Value>
    </Parameter>
  </Parameters>
</WriteText>

 

此XML负责用户控件的初始外观
 

并调用CreateXmlDocument函数。因此,此XML文档的结构为输出文档。

<>
N输出参数输出XML中的名称
1Exp.Formula_1
1Exp.Formula_2参数

SilverlightControlHtmlOutput作为IPostLoadService向服务器发送以下请求:

<Chart>ExponentChart.xml</Chart>

服务器响应是ExponentChart.xml的内容。初始化后,用户控件获得以下视觉外观:

Chart initial

该类还实现了以下接口:

    /// <summary>
    /// Request object
    /// </summary>
    public interface IRequest
    {
        /// <summary>
        /// Request
        /// </summary>
        void Request();
    }

Request向服务器发送以下请求:

 
<ChartDocument Chart="ExponentChart.xml" ShowFunction="showOutput" Desktop="Exponent.cfa">
  <Parameters>
    <Start>0</Start>
    <Step>1000</Step>
    <Finish>20000</Finish>
    <Xslt>Exponent.xslt</Xslt>
  </Parameters>
</ChartDocument>

此请求和"ExponentChart.xml"的内容生成以下XML:

<ChartDocument Chart="ExponentChart.xml" Desktop="Exponent.cfa">
    <Interval>
        <Start>0</Start>
        <Step>1000</Step>
        <Finish>20000</Finish>
        <Xslt>Exponent.xslt</Xslt>
    </Interval>
    <ChartName>Chart</ChartName>
    <Parameters>
        <Parameter>
            <Name>Exp.Formula_1</Name>
            <Value>Value</Value>
        </Parameter>
        <Parameter>
            <Name>Exp.Formula_2</Name>
            <Value>Argument</Value>
        </Parameter>
    </Parameters>
</ChartDocument>

此XML用作CreateXmlDocument函数的参数,该函数返回以下文档:

<Root>
    <Parameters>
        <Parameter Name="Value" Value="0.7" />
        <Parameter Name="Argument" Value="0" />
    </Parameters>
    <Parameters>
        <Parameter Name="Value" Value="1.35969847666886E+130" />
        <Parameter Name="Argument" Value="1000" /></Parameters>
    <Parameters><Parameter Name="Value" Value="2.64111421065087E+260" />
        <Parameter Name="Value" Value="2.64111421065087E+260" />
        <Parameter Name="Argument" Value="2000" />
    </Parameters>
</Root>

然后,此XML由包含在Exponent.xslt中的XSLT进行转换。
文件

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"
	xmlns:user="urn:my-scripts">
  <xsl:key name="parameter-search" match="Parameter" use="@Name"/>
  <xsl:template match="/">
    <text>
      <h1>
        Table of <em>
          ae<sup>bx</sup>
        </em>
      </h1>
      <table border="1">
        <tr>
          <td>
            <em>x</em>
          </td>
          <td>
            <em>
              ae<sup>bx</sup>
            </em>
          </td>
        </tr>
        <xsl:apply-templates/>
      </table>
    </text>
  </xsl:template>
  <xsl:template match="Parameters">
        <tr>
          <td>
            <xsl:value-of select="Parameter[2]/@Value"/>
          </td>
          <td>
            <xsl:value-of select="Parameter[1]/@Value"/>
          </td>
        </tr>
  </xsl:template>
</xsl:stylesheet>

转换结果如下所示:

<text>
    <h1>Table of <em>ae<sup>bx</sup></em></h1>
        <table border="1">
            <tr><td><em>x</em></td><td><em>ae<sup>bx</sup></em></td></tr>
            <tr><td>0</td><td>0.7</td></tr>
            <tr><td>1000</td><td>1.35969847666886E+130</td></tr>
            <tr><td>2000</td><td>2.64111421065087E+260</td></tr>
        </table>
</text>
 

服务器还生成错误报告。结果响应包含计算结果:
以及异常报告。

<Root>
    <Text><![CDATA[<h1>Table of <em>ae<sup>bx</sup></em></h1>
        <table border="1">
            <tr><td><em>x</em></td><td><em>ae<sup>bx</sup></em></td></tr>
            <tr><td>0</td><td>0.7</td></tr>
            <tr><td>1000</td><td>1.35969847666886E+130</td></tr>
            <tr><td>2000</td><td>2.64111421065087E+260</td></tr>
        </table>]]>
    </Text>
    <Exceptions>
        <Exception>
            <Object>10</Object>
            <StackTrace>   at EngineeringInitializer.BasicEngineeringInitializer.<InitializeApplication>b__0(Object o) 
                at c:\AUsers\1MySoft\CSharp\src\src\EngineeringInitializer\BasicEngineeringInitializer.cs:line 68
                at Calculation.Calculate.Update() at c:\Users\3D_MONSTR\AppData\Local\Temp\ewmveptb.0.cs:line 33
                at DataPerformer.VectorFormulaConsumer.UpdateMeasurements() 
                at c:\AUsers\1MySoft\CSharp\src\src\DataPerformer\VectorFormulaConsumer.cs:line 401
            </StackTrace>
            <ExceptionMessage>Infinity</ExceptionMessage>
            <ExceptionType>System.Exception</ExceptionType>
        </Exception>
        <Exception>
            <Object>10</Object>
            <StackTrace>   
                at OrbitalService.ServiceOrbital.<AppllicactionInitialize>b__0(Object o) 
                at c:\AUsers\1MySoft\CSharp\src\WCF\OrbitalService\ServiceOrbital.svc.cs:line 43
                at DataPerformer.StaticExtensionDataPerformerAdvanced.<>c__DisplayClass5.<CreateXmlDocument>b__3() 
                at c:\AUsers\1MySoft\CSharp\src\src\DataPerformer\StaticExtensionDataPerformerAdvanced.cs:line 301
                at DataPerformer.StaticExtensionDataPerformerBase.PerformFixed(IDataConsumer consumer,
                 Double start, Double step, Int32 count, ITimeMeasureProvider provider, IDifferentialEquationProcessor processor, 
                 IComponentCollection collection, Int32 priority, Action action) 
                at c:\AUsers\1MySoft\CSharp\src\src\DataPerformer
                 \DataPerformer.Base\DataPerformer.Base\StaticExtensionDataPerformerBase.cs:line 1723
            </StackTrace>
            <ExceptionMessage>Infinity</ExceptionMessage>
            <ExceptionType>System.Exception</ExceptionType>
        </Exception>
    </Exceptions>
</Root>

Text标签的内容包含输出HTML。属性ScriptFunction = "showOutput"表示
调用showOutput脚本。

<script type="text/javascript">
 
             // Shows output
             // text is output text
             function showOutput(text) {
                 var div_preview = document.getElementById("Output");
                 div_preview.innerHTML = text;
             }
</script>
<!--...-->
<div id="Output"></div>

调用此脚本会显示以下内容:

+++++++++++++++++++++++++++++++

aebx的表格

<>
xaebx
00.7
10001.35969847666886E+130
20002.64111421065087E+260

+++++++++++++++++++++++++++++++

2.4 用户控件组合

显然,上述任何组件本身都是无用的。例如,在没有实质性任务的情况下,异常处理程序永远不会处理异常。以下XAML代码包含用户控件的有用组合。

<UserControl xmlns:SilverlightDiagramUI="clr-namespace:Diagram.UI.SilverlightDiagramUI;
    assembly=Diagram.UI.SilverlightDiagramUI" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:DataPerformer.UI.SilverlightDataPerformer" 
    x:Class="DataPerformer.UI.SilverlightDataPerformer.SilverlightControlChartInputOutput"
    mc:Ignorable="d"
    d:DesignHeight="300" d:DesignWidth="400"><StackPanel>
         <Grid x:Name="LayoutRoot" ShowGridLines="True">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
            </Grid.ColumnDefinitions>
             <SilverlightDiagramUI:SilverlightControlAlias x:Name="Aliases" Grid.Column="0" 
             Grid.Row="0"  HorizontalAlignment="Stretch" Margin="0,0,0,0" />
            <local:SilverlightControlHtmlOutput x:Name="Chart"  Grid.Column="0" Grid.Row="1" />
            <Button click="Button_Click" content="Start" grid.row="2" 
            horizontalalignment="Stretch" margin="0,0,0,0" width="75" x:name="Start"/>
            <local:SilverlightControlHtmlErrorHandler x:Name="Errors" 
            HorizontalAlignment="Left" Height="0" Grid.Row="2" Width="0" Visibility="Collapsed"/>
        </Grid>
    </StackPanel>
</UserControl>
以下序列图解释了此用户控件的逻辑。

 

Full sequence diagram

此组件将别名附加到图表请求。

<ChartDocument Chart="ExponentChart.xml" Desktop="Exponent.cfa">
  <Aliases Desktop="Exponent.cfa">
    <Alias>
      <Name>Exp.a</Name>
      <Value>0.7</Value>
      <Type>System.Double</Type>
    </Alias>
    <Alias>
      <Name>Exp.b</Name>
      <Value>0.3</Value>
      <Type>System.Double</Type>
    </Alias>
  </Aliases>
  <Parameters>
    <Start>0</Start>
    <Step>1000</Step>
    <Finish>20000</Finish>
    <Xslt>Exponent.xslt</Xslt>
  </Parameters>
</ChartDocument>

如果指数值变得太大,程序会抛出异常。

Error message

UI的完整图如下所示:

Exp UI

它包含别名、区间参数、输出HTML、错误报告。


3. 实际示例。人造地球卫星运动预测

3.1 业务逻辑


此示例的业务逻辑已在此描述。但我已复制代码以便于阅读。

 

3.1.1 运动模型

人造卫星的运动模型是一个描述其在格林尼治参考系中运动的常微分方程组:

Motion equations

其中
运动参数 分别是坐标和速度分量,
Omega 是地球的角速度,以及
Omega 是总外部加速度的分量。当前模型包含引力和一个空气动力。这些加速度可以用以下公式定义:

Acceleration formulae

下表包含这些参数的含义:

Parameters

常微分方程组件用于上述方程。该组件没有丰富的符号系统,只使用小写变量。但是,该组件支持丰富的注释系统,如下所示:

Parameters

这些注释可以包含变量和物理参数之间的映射。方程的自变量(xyzuvw)在右侧部分被勾选。这些变量与

Parameters

该组件以下列方式表示微分方程:

Parameters

除了自变量,方程还包含其他参数。下表包含了这些参数的映射关系。

Parameters

其中一些参数是常量。其他参数需要导出。常量被勾选,而导出的参数未被勾选,如下所示:

Parameters

导出的参数与引力和大气模型有关。这些模型包含在外部库中。下图包含了运动模型的组件。

Motion model

运动方程组件被视为普通微分方程的上述组件。这些方程有反馈。方程的右侧取决于引力。否则,引力取决于卫星的坐标。Vector组件用作Motion equations的反馈。

FeedBack

这张图有以下含义。Vector组件的变量xyzuvw被赋值给Motion equationsxyzuvw变量。Vector的输入和输出参数如下所示。

Pareameters of Vector object

GravityAtmosphere组件是从外部库获得的,这些库分别计算地球的引力和地球大气。这两个对象都实现IObjectTransformer接口。G-transformationA-transformation对象是用于应用GravityAtmosphere对象的辅助对象。G-transformation的属性如下所示。

Pareameters of Vector object

上图的含义是:G-transformation连接到Gravity对象。此对象对应于导出的Gravity类(Gravity类的代码在此文本之上)。Gravity类实现IObjectTransformer接口。Gravity的输入参数是标记为“x”、“y”、“z”的坐标。因此,上面的组合框被标记为x”、“y”、“z”标签。上图表示参数x”、“y”、“z”被赋值给Vector组件的Formula_1Formula_2Formula_3。否则,VectorFormula_1Formula_2Formula_3被赋值给Motion equationsxyz。然而,Motion equations组件使用G-transformation的参数。

Parameters of Vector object

这张图的含义是:Motion equationsabc参数被赋值给G-transformationGxGyGz参数。因此,地球的Gravity函数被导出到G-transformation。地球大气层也以类似的方式导出。

3.1.2 文档生成

在此示例中,使用了实际函数f。然而,该函数还有另一个参数。该参数是卫星的Z-坐标。

除了计算,任何软件都需要生成文档。文件生成功能在预测问题上得到体现。预测文档包含赤道交点处卫星运动的参数。下图解释了预测问题:

Forecast picture

卫星沿着其轨道运动。运动参数对应于它从南向北运动并与赤道相交的时间。当其Z坐标为0时,卫星与赤道相交。如果我们得到作为Z函数的坐标和时间,那么我们就可以看到必要的参数是在Z=0处这些函数的取值。下图代表了该任务的解决方案。

Forecast algorithm

Delay组件具有FunctionAccumulator类型。此类型非常类似于Simulink的Transport delay。然而,FunctionAccumulator更高级。Delay的属性如下所示。

Delay

Delay组件连接到Data的数据。它存储Data的最新输出值。Step count = 4表示Delay存储了每个输出参数的最后4个值。数据的Formula_4是卫星的Z坐标。因此,Delay的所有参数都被视为Z的函数。让我们考虑预测的所有组件。Satellite组件是一个容器,包含卫星的Motion equationsData组件连接到Motion equations,并将时间参数添加到运动参数。Data的属性如下所示。

Data.

Recursive组件类似于Simulink的Memory组件。此组件计算Z坐标的时间延迟。Recursive元素属性如下所示。

Recursve

Condition 元素用于确定赤道穿越的时间。Condition 的属性如下所示:

Condition

此组件的参数含义包含在注释中。

Condition

最后,Output 组件包含预测参数的计算。它有以下公式:

Output Formulae

数字86400是一天中的秒数。time函数将double变量转换为DateTimeb是赤道交变条件。输入参数是作为Z函数的运动参数。参数的完整列表包含在以下注释中:

Output

最后,Chart 组件具有以下属性:

Chart

这些属性的含义如下。当条件变量为true时执行输出。在这种情况下,条件是Condition对象的Formula_1。这是赤道交变条件。输出参数是Output对象的Formula_1 - Formula_7。标签XYZVxVyVz是XML文档中反映的标签。XML文档如下所示:

<Root>
  <Parameters>
    <Parameter Name="T" Value="Monday, June 28, 1999 11:17:05 PM.457" />
    <Parameter Name="X" Value="-6595.47050815637" />
    <Parameter Name="Y" Value="-2472.05799683873" />
    <Parameter Name="Z" Value="0" />
    <Parameter Name="Vx" Value="-0.541295154046453" />
    <Parameter Name="Vy" Value="1.46945520971716" />
    <Parameter Name="Vz" Value="7.45051367374592" />
  </Parameters>
  <Parameters>
    <Parameter Name="T" Value="Tuesday, June 29, 1999 12:55:08 AM.068" />
    <Parameter Name="X" Value="-7026.76544757067" />
    <Parameter Name="Y" Value="487.053173194772" />
    <Parameter Name="Z" Value="0" />
    <Parameter Name="Vx" Value="0.117122882887633" />
    <Parameter Name="Vy" Value="1.56173832654579" />
    <Parameter Name="Vz" Value="7.45055871464937" />
  </Parameters>
 <Root>

第一个参数的类型为DateTime,其他参数的类型为double。以下代码包含XSLT文件。

<xsl:stylesheet version="1.0" xmlns:xsl=http://www.w3.org/1999/XSL/Transform 
    xmlns:msxsl="urn:schemas-microsoft-com:xslt"
    xmlns:user="urn:my-scripts">
  <xsl:template match="/">
    <html>
      <body>
        <p>
          <h1>Forecast</h1>
        </p>
        <xsl:apply-templates />
      </body>
    </html>
  </xsl:template>
  <xsl:template match="Parameters">
    <p></p>
    <p>
      <table border="1">
        <xsl:apply-templates />
      </table>
    </p>
  </xsl:template>
  <xsl:template match="Parameter">
    <xsl:apply-templates />
    <tr>
      <td>
        <xsl:value-of select="@Name" />
      </td>
      <td>
        <xsl:value-of select="@Value" />
      </td>
    </tr>
  </xsl:template>
</xsl:stylesheet>

此 XSLT 代码提供了以下 HTML 文档。

<>
T 1999年6月28日星期一 晚上11:17:05.457
X -6595.47050815637
Y -2472.05799683873
Z 0
Vx -0.541295154046453
Vy 1.46945520971716
Vz 7.45051367374592
T 1999年6月29日星期二 凌晨12:55:08.068
X -7026.76544757067
Y 487.053173194772
Z 0
Vx 0.117122882887633
Vy 1.56173832654579
Vz 7.45055871464937

3.2 Web版本

作为一个Web应用程序,这个项目与第1节中描述的基本项目没有原则上的区别。该项目有输出条件,这一点在以下XML中得到体现。

<WriteText>
  <Interval>
    <Start>1770457394</Start>
    <Step>10</Step>
    <Finish>1770460394</Finish>
  </Interval>
  <ChartName>Forecast</ChartName>
  <Condition>Condition.Formula_1</Condition>
  <Argument>Time</Argument>
  <Parameters>
    <Parameter>
      <Name>Output.Formula_1</Name>
      <Value>T</Value>
    </Parameter>
    <Parameter>
      <Name>Output.Formula_2</Name>
      <Value>X</Value>
    </Parameter>
    <Parameter>
      <Name>Output.Formula_3</Name>
      <Value>Y</Value>
    </Parameter>
    <Parameter>
      <Name>Output.Formula_4</Name>
      <Value>Vx</Value>
    </Parameter>
    <Parameter>
      <Name>Output.Formula_5</Name>
      <Value>Vy</Value>
    </Parameter>
    <Parameter>
      <Name>Output.Formula_6</Name>
      <Value>Vz</Value>
    </Parameter>
  </Parameters>
</WriteText>

表达式<Condition>Condition.Formula_1</Condition>表示Condition对象的输出条件是Formula_1。以下XML显示了别名:

<Root>
  <Item>
    <Number>1</Number>    <Name> X - coordinate of the satellite (x) , km </Name>
    <Alias>Motion equations/Motion equations.x</Alias>
    <Type>System.Double</Type>
  </Item>
  <Item>
    <Number>2</Number>
    <Name> Y - coordinate of the satellite (y), km </Name>
    <Alias>Motion equations/Motion equations.y</Alias>
    <Type>System.Double</Type>
  </Item>
  <Item>
    <Number>3</Number>
    <Name> Z - coordinate of the satellite (z), km </Name>
    <Alias>Motion equations/Motion equations.z</Alias>
    <Type>System.Double</Type>
  </Item>
  <Item>
    <Number>4</Number>
    <Name> X - component of velicity of the satellite (Vx), km/s </Name>
    <Alias>Motion equations/Motion equations.u</Alias>
    <Type>System.Double</Type>
  </Item>
  <Item>
    <Number>5</Number>
    <Name> Y - component of velicity of the satellite (Vy), km/s </Name>
    <Alias>Motion equations/Motion equations.v</Alias>
    <Type>System.Double</Type>
  </Item>
  <Item>
    <Number>6</Number>
    <Name> Z - component of velicity of the satellite (Vz), km/s </Name>
    <Alias>Motion equations/Motion equations.w</Alias>
    <Type>System.Double</Type>
  </Item>
</Root>gt;

这些别名是微分方程组的初始条件。

Initial conditions


演示的Web客户端位于本文开头。

关注点


我蓝色的大梦想是发明新的时间机器。我想开发一个类似于Code Project的在线设计器。

Time machine Code Project

但是,我的设计器应该成为科学和工程门户开发人员的工具包。

© . All rights reserved.