工程网站的快速开发






4.95/5 (10投票s)
从桌面到 Web。

有用链接
- 原文
- Visual Studio Express 2012 for Windows Desktop
- Visual Studio Express 2012 for Web
- 首页:Silverlight.NET
- Windows Azure。一款坚如磐石的云平台,助您畅想未来
- 首页:Mathframe
- 首页:AstroFrame
- 首页:Weifen Luo
1. 引言
2002年,我曾预言我的软件项目应该有Web版本。最近(2012年10月),我开始实现这个想法。然而,十年来我开发的软件已经使其易于适应Web。所以我又一次
重新发明
时间机器。我关于快速开发工程Web应用的想法与LabVIEW的想法相似。LabVIEW同时支持业务逻辑和用户界面的图形化编程。
我的方法也是如此。下图说明了这一点。
桌面窗口应用程序使我们能够开发业务逻辑。然后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的错误处理:
上述错误处理的Web版本看起来是这样的:
<>N | 信号强度 | Message | 堆栈跟踪 |
1 | 10 | NaN | 在EngineeringInitializer.BasicEngineeringInitializer中。 |
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
对象(SilverlightControlAlias
和SilverlightControlHtmlOutput
。这两个消费者都自动与错误处理程序关联,因为这三个元素有共同的根。下图展示了IErrorHandler
和IErrorHandlerConsumer
之间的互操作性。以下序列图解释了异常处理程序的用法。
2.2 客户端接口
2.2.1 初始化接口
一些UI元素在出现后应进行初始化。以下模式代表此类初始化:
IPostLoadService
接口。2.2.2 附加信息接口
许多用户控件包含子控件。通常,对服务器的请求包含所有子控件中的信息。以下控件:
有两个子控件。“绿色”子控件生成以下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
对象都会将信息添加到请求中。下图说明了请求的逻辑:
请注意,不应显式调用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有两个参数a和b,可以被视为常量。软件应支持常量输入。
2.3.1 业务逻辑
下图代表上述任务的业务逻辑。
其中,Exp组件计算f(t)=aebt。整个上图称为Desktop。任何桌面都对应一个实现IDesktop
接口的对象。Exp的属性如下所示。
上图右下角包含a和b常量的编辑器。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>
然后,用户控件接收初始化响应,其外观会发生以下变化:
此组件实现IAddRequest
接口。因此,它提供有关别名值的信息以供主要任务使用。附加信息在2.2.2中讨论。
2.3 图表输出
2.3.1 业务逻辑
我们的示例
包含Chart组件。除了图形模式,它还有文本模式。
此模式生成以下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中的名称 |
1 | Exp.Formula_1 | 值 |
1 | Exp.Formula_2 | 参数 |
类SilverlightControlHtmlOutput
作为IPostLoadService
向服务器发送以下请求:
<Chart>ExponentChart.xml</Chart>
服务器响应是ExponentChart.xml
的内容。初始化后,用户控件获得以下视觉外观:
该类还实现了以下接口:
/// <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的表格
<>x | aebx |
0 | 0.7 |
1000 | 1.35969847666886E+130 |
2000 | 2.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>以下序列图解释了此用户控件的逻辑。
此组件将别名附加到图表请求。
<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>
如果指数值变得太大,程序会抛出异常。
UI的完整图如下所示:
它包含别名、区间参数、输出HTML、错误报告。
3. 实际示例。人造地球卫星运动预测
3.1 业务逻辑
此示例的业务逻辑已在此处描述。但我已复制代码以便于阅读。
3.1.1 运动模型
人造卫星的运动模型是一个描述其在格林尼治参考系中运动的常微分方程组:
其中
分别是坐标和速度分量,
是地球的角速度,以及
是总外部加速度的分量。当前模型包含引力和一个空气动力。这些加速度可以用以下公式定义:

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

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

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

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

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

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

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

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

这张图有以下含义。Vector组件的变量x、y、z、u、v、w被赋值给Motion equations的x、y、z、u、v、w变量。Vector的输入和输出参数如下所示。

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

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

这张图的含义是:Motion equations的a、b、c参数被赋值给G-transformation的Gx、Gy、Gz参数。因此,地球的Gravity
函数被导出到G-transformation。地球大气层也以类似的方式导出。
3.1.2 文档生成
在此示例中,使用了实际函数f。然而,该函数还有另一个参数。该参数是卫星的Z-坐标。
除了计算,任何软件都需要生成文档。文件生成功能在预测问题上得到体现。预测文档包含赤道交点处卫星运动的参数。下图解释了预测问题:

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

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

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

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

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

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

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

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

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

这些属性的含义如下。当条件变量为true
时执行输出。在这种情况下,条件是Condition
对象的Formula_1。这是赤道交变条件。输出参数是Output
对象的Formula_1 - Formula_7。标签X、Y、Z、Vx、Vy、Vz是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;
这些别名是微分方程组的初始条件。
演示的Web客户端位于本文开头。
关注点
我蓝色的大梦想是发明新的时间机器。我想开发一个类似于Code Project的在线设计器。
但是,我的设计器应该成为科学和工程门户开发人员的工具包。