Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ScriptModel.cs
1using SkiaSharp;
2using System;
3using System.IO;
4using System.Text;
5using System.Threading.Tasks;
6using System.Windows;
7using System.Windows.Controls;
8using System.Windows.Input;
9using System.Windows.Markup;
10using System.Windows.Media;
11using System.Windows.Media.Imaging;
15using Waher.Events;
16using Waher.Script;
22
24{
29 public class ScriptModel : Model, IDisposable
30 {
31 private readonly Property<string> referenceUri;
32 private readonly Property<string> input;
33
34 private readonly Variables variables;
35 private readonly StackPanel historyPanel;
36
42 public ScriptModel(StackPanel HistoryPanel)
43 {
44 this.referenceUri = new Property<string>(nameof(this.ReferenceUri), "https://lab.tagroot.io/Script.md", this);
45 this.input = new Property<string>(nameof(this.Input), string.Empty, this);
46
47 this.historyPanel = HistoryPanel;
48 this.variables = new Variables()
49 {
50 ConsoleOut = new PrintOutput(this)
51 };
52 }
53
57 public string ReferenceUri
58 {
59 get => this.referenceUri.Value;
60 set => this.referenceUri.Value = value;
61 }
62
66 public string Input
67 {
68 get => this.input.Value;
69 set => this.input.Value = value;
70 }
71
75 public void InputPreviewKeyDown(KeyEventArgs e)
76 {
77 try
78 {
79 if (e.Key == Key.Enter)
80 {
81 if (!Keyboard.Modifiers.HasFlag(ModifierKeys.Control) && !Keyboard.Modifiers.HasFlag(ModifierKeys.Shift))
82 {
83 this.ExecuteButton_Click(this, e);
84 e.Handled = true;
85 }
86 }
87 }
88 catch (Exception ex)
89 {
90 Log.Exception(ex);
91 MainWindow.ErrorBox(ex.Message);
92 }
93 }
94
95 private void ExecuteButton_Click(object sender, RoutedEventArgs e)
96 {
98 TextBlock ScriptBlock;
99 UIElement ResultBlock = null;
100
101 try
102 {
103 Exp = new Waher.Script.Expression(this.Input);
104
105 ScriptBlock = new TextBlock()
106 {
107 Text = this.Input,
108 FontFamily = new FontFamily("Courier New"),
109 TextWrapping = TextWrapping.Wrap,
110 Tag = Exp
111 };
112
113 ScriptBlock.PreviewMouseDown += this.TextBlock_PreviewMouseDown;
114
115 this.historyPanel.Children.Add(ScriptBlock);
116
117 if (this.historyPanel.Parent is ScrollViewer ScrollViewer)
118 ScrollViewer.ScrollToBottom();
119
120 this.Input = string.Empty;
121 }
122 catch (Exception ex)
123 {
124 ex = Log.UnnestException(ex);
125 MainWindow.ErrorBox(ex.Message);
126 return;
127 }
128
129 Task.Run(async () =>
130 {
131 try
132 {
133 IElement Ans;
134
135 Task Preview(object sender2, PreviewEventArgs e2)
136 {
137 MainWindow.UpdateGui(async () =>
138 {
139 ResultBlock = await this.ShowResult(ResultBlock, e2.Preview, ScriptBlock);
140 });
141
142 return Task.CompletedTask;
143 }
144
145 this.variables.OnPreview += Preview;
146 try
147 {
148 Ans = await Exp.Root.EvaluateAsync(this.variables);
149 }
151 {
152 Ans = ex.ReturnValue;
153 }
154 catch (Exception ex)
155 {
156 Ans = new ObjectValue(ex);
157 }
158 finally
159 {
160 this.variables.OnPreview -= Preview;
161 }
162
163 this.variables["Ans"] = Ans;
164
165 MainWindow.UpdateGui(async () =>
166 {
167 ResultBlock = await this.ShowResult(ResultBlock, Ans, ScriptBlock);
168 });
169 }
170 catch (Exception ex)
171 {
172 ex = Log.UnnestException(ex);
173 await MainWindow.MessageBox(ex.Message, "Unable to parse script.", MessageBoxButton.OK, MessageBoxImage.Error);
174 }
175 });
176 }
177
178 private async Task<UIElement> ShowResult(UIElement ResultBlock, IElement Ans, TextBlock ScriptBlock)
179 {
180 try
181 {
182 if (Ans is Graph G)
183 {
184 PixelInformation Pixels = G.CreatePixels(this.variables, out object[] States);
185 return this.AddImageBlock(ScriptBlock, Pixels, G, States, ResultBlock);
186 }
187 else if (Ans.AssociatedObjectValue is SKImage Img)
188 return this.AddImageBlock(ScriptBlock, PixelInformation.FromImage(Img), null, null, ResultBlock);
189 else if (Ans.AssociatedObjectValue is Exception ex)
190 {
191 UIElement Last = ResultBlock ?? ScriptBlock;
192
193 ex = Log.UnnestException(ex);
194
195 if (ex is AggregateException ex2)
196 {
197 foreach (Exception ex3 in ex2.InnerExceptions)
198 Last = this.AddTextBlock(Last, ex3.Message, Colors.Red, FontWeights.Bold, null, ex3);
199 }
200 else
201 Last = this.AddTextBlock(ScriptBlock, ex.Message, Colors.Red, FontWeights.Bold, ResultBlock, ex);
202
203 return Last;
204 }
205 else if (Ans.AssociatedObjectValue is ObjectMatrix M && M.ColumnNames != null)
206 {
207 StringBuilder Markdown = new();
208
209 foreach (string s2 in M.ColumnNames)
210 {
211 Markdown.Append("| ");
212 Markdown.Append(MarkdownDocument.Encode(s2));
213 }
214
215 Markdown.AppendLine(" |");
216
217 foreach (string s2 in M.ColumnNames)
218 Markdown.Append("|---");
219
220 Markdown.AppendLine("|");
221
222 int x, y;
223
224 for (y = 0; y < M.Rows; y++)
225 {
226 for (x = 0; x < M.Columns; x++)
227 {
228 Markdown.Append("| ");
229
230 object Item = M.GetElement(x, y).AssociatedObjectValue;
231 if (Item != null)
232 {
233 if (Item is not string s2)
234 s2 = Waher.Script.Expression.ToString(Item);
235
236 s2 = s2.Replace("\r\n", "\n").Replace('\r', '\n').Replace("\n", "<br/>");
237 Markdown.Append(MarkdownDocument.Encode(s2));
238 }
239 }
240
241 Markdown.AppendLine(" |");
242 }
243
244 MarkdownDocument Doc = await MarkdownDocument.CreateAsync(Markdown.ToString(), GetMarkdownSettings());
245 string XAML = await Doc.GenerateXAML(GetXamlSettings());
246
247 if (XamlReader.Parse(XAML) is UIElement Parsed)
248 return this.AddBlock(ScriptBlock, Parsed);
249
250 return null;
251 }
252 else
253 return this.AddTextBlock(ScriptBlock, Ans.ToString(), Colors.Red, FontWeights.Normal, ResultBlock, Ans);
254 }
255 catch (Exception ex)
256 {
257 ex = Log.UnnestException(ex);
258 Ans = new ObjectValue(ex);
259 this.variables["Ans"] = Ans;
260
261 if (ex is AggregateException ex2)
262 {
263 foreach (Exception ex3 in ex2.InnerExceptions)
264 ScriptBlock = this.AddTextBlock(ScriptBlock, ex3.Message, Colors.Red, FontWeights.Bold, null, ex3);
265 }
266 else
267 this.AddTextBlock(ScriptBlock, ex.Message, Colors.Red, FontWeights.Bold, ResultBlock, ex);
268
269 return null;
270 }
271 }
272
273 public static MarkdownSettings GetMarkdownSettings()
274 {
275 return new MarkdownSettings(null, false);
276 }
277
278 public static HtmlSettings GetHtmlSettings()
279 {
280 return new HtmlSettings()
281 {
282 XmlEntitiesOnly = true
283 };
284 }
285
286 public static XamlSettings GetXamlSettings()
287 {
288 return new XamlSettings()
289 {
290 TableCellRowBackgroundColor1 = "#20404040",
291 TableCellRowBackgroundColor2 = "#10808080"
292 };
293 }
294
295 private TextBlock AddTextBlock(UIElement ScriptBlock, string s, Color cl, FontWeight Weight, UIElement ResultBlock, object Tag)
296 {
297 if (ResultBlock is TextBlock TextBlock)
298 {
299 TextBlock.Text = s;
300 TextBlock.Tag = Tag;
301 }
302 else
303 {
304 TextBlock = new TextBlock()
305 {
306 Text = s,
307 FontFamily = new FontFamily("Courier New"),
308 Foreground = new SolidColorBrush(cl),
309 TextWrapping = TextWrapping.Wrap,
310 FontWeight = Weight,
311 Tag = Tag
312 };
313
314 TextBlock.PreviewMouseDown += this.TextBlock_PreviewMouseDown;
315
316 this.AddBlock(ScriptBlock, TextBlock);
317 }
318
319 return TextBlock;
320 }
321
322 private UIElement AddBlock(UIElement ScriptBlock, UIElement ResultBlock)
323 {
324 if (ScriptBlock is null)
325 this.historyPanel.Children.Add(ResultBlock);
326 else
327 this.historyPanel.Children.Insert(this.historyPanel.Children.IndexOf(ScriptBlock) + 1, ResultBlock);
328
329 return ResultBlock;
330 }
331
332 private void TextBlock_PreviewMouseDown(object sender, MouseButtonEventArgs e)
333 {
334 this.Input = ((TextBlock)sender).Text;
335 e.Handled = true;
336 }
337
338 private UIElement AddImageBlock(UIElement ScriptBlock, PixelInformation Pixels, Graph Graph, object[] States, UIElement ResultBlock)
339 {
340 BitmapImage BitmapImage;
341 byte[] Bin = Pixels.EncodeAsPng();
342
343 using (MemoryStream ms = new(Bin))
344 {
345 BitmapImage = new BitmapImage();
346 BitmapImage.BeginInit();
347 BitmapImage.CacheOption = BitmapCacheOption.OnLoad;
348 BitmapImage.StreamSource = ms;
349 BitmapImage.EndInit();
350 }
351
352 if (ResultBlock is Image ImageBlock)
353 {
354 ImageBlock.Source = BitmapImage;
355 ImageBlock.Width = Pixels.Width;
356 ImageBlock.Height = Pixels.Height;
357 ImageBlock.Tag = new Tuple<byte[], int, int, Graph, object[]>(Bin, Pixels.Width, Pixels.Height, Graph, States);
358 }
359 else
360 {
361 ImageBlock = new Image()
362 {
363 Source = BitmapImage,
364 Width = Pixels.Width,
365 Height = Pixels.Height,
366 Tag = new Tuple<byte[], int, int, Graph, object[]>(Bin, Pixels.Width, Pixels.Height, Graph, States)
367 };
368
369 ImageBlock.PreviewMouseDown += this.ImageBlock_PreviewMouseDown;
370
371 this.historyPanel.Children.Insert(this.historyPanel.Children.IndexOf(ScriptBlock) + 1, ImageBlock);
372 }
373
374 return ImageBlock;
375 }
376
377 private void ImageBlock_PreviewMouseDown(object sender, MouseButtonEventArgs e)
378 {
379 Image ImageBlock = (Image)sender;
380
381 if (e.ChangedButton == MouseButton.Left)
382 {
383 Point P = e.GetPosition(ImageBlock);
384 string Script;
385
386 if (ImageBlock.Tag is Tuple<byte[], int, int, Graph, object[]> Image && Image.Item4 != null && Image.Item5 != null)
387 {
388 double X = ((double)P.X) * Image.Item2 / ImageBlock.ActualWidth;
389 double Y = ((double)P.Y) * Image.Item3 / ImageBlock.ActualHeight;
390
391 Script = Image.Item4.GetBitmapClickScript(X, Y, Image.Item5);
392 }
393 else
394 Script = "[" + P.X.ToString() + "," + P.Y.ToString() + "]";
395
396 this.Input = Script;
397 this.ExecuteButton_Click(this, e);
398 }
399 }
400
401 internal void Print(string Output)
402 {
403 MainWindow.UpdateGui(() =>
404 {
405 this.AddTextBlock(null, Output, Colors.Blue, FontWeights.Normal, null, false);
406 return Task.CompletedTask;
407 });
408 }
409
411 public void Dispose()
412 {
413 this.historyPanel.Children.Clear();
414 }
415
419 public override async Task Start()
420 {
422 {
423 MainWindow.currentInstance.ScriptTab.DataContext = this;
424 return Task.CompletedTask;
425 });
426
427 await base.Start();
428 }
429 }
430}
Contains a markdown document. This markdown document class supports original markdown,...
static string Encode(string s)
Encodes all special characters in a string so that it can be included in a markdown document without ...
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Contains settings that the Markdown parser uses to customize its behavior.
Contains settings that the HTML export uses to customize HTML output.
Definition: HtmlSettings.cs:7
Contains settings that the XAML export uses to customize XAML output.
Definition: XamlSettings.cs:10
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:13
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1647
static Exception UnnestException(Exception Exception)
Unnests an exception, to extract the relevant inner exception.
Definition: Log.cs:818
Class managing a script expression.
Definition: Expression.cs:39
ScriptNode Root
Root script node.
Definition: Expression.cs:4299
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
Definition: Expression.cs:4496
Base class for graphs.
Definition: Graph.cs:79
Contains pixel information
virtual byte[] EncodeAsPng()
Encodes the pixels into a binary PNG image.
static PixelInformation FromImage(SKImage Image)
Gets the pixel information from an SKImage.
string[] ColumnNames
Contains optional column names.
Event arguments for preview events.
IElement Preview
Preview of result.
Collection of variables.
Definition: Variables.cs:25
Basic interface for all types of elements.
Definition: IElement.cs:20
object AssociatedObjectValue
Associated object value.
Definition: IElement.cs:33
MouseButton
Enumeration identifying mouse button being used.
Definition: App.xaml.cs:4