2using System.Collections.Generic;
4using System.Text.RegularExpressions;
5using System.Threading.Tasks;
19 private readonly
string threadId;
20 private readonly SortedDictionary<string, SourceState> sources =
new SortedDictionary<string, SourceState>();
22 private readonly
int maxPaletteSize;
23 private Dictionary<string, KeyValuePair<SKColor, int>> legend =
null;
25 private SKColor[] palette =
null;
26 private object tag =
null;
27 private int nrTop = 0;
28 private int nrBottom = 0;
29 private bool filterSources =
false;
40 this.maxPaletteSize = MaxPaletteSize;
53 await this.synchObject.BeginRead();
56 string[] Result =
new string[this.sources.Count];
57 this.sources.Keys.CopyTo(Result, 0);
62 await this.synchObject.EndRead();
71 await this.synchObject.BeginRead();
86 await this.synchObject.EndRead();
95 get => this.filterSources;
96 set => this.filterSources = value;
105 set => this.tag = value;
116 return this.
Add(Source, Markdown,
string.Empty);
128 return this.
Add(Source, Markdown, Id,
false,
false);
137 public Task<bool>
Add(
string Source,
string Text)
139 return this.
Add(Source, Text,
string.Empty);
149 public Task<bool>
Add(
string Source,
string Text,
string Id)
151 return this.
Add(Source, Text, Id,
false,
false);
163 return this.
Add(Source, Markdown, Id,
true,
false);
173 public Task<bool>
Update(
string Source,
string Text,
string Id)
175 return this.
Add(Source, Text, Id,
true,
false);
187 private async Task<bool>
Add(
string Source,
string Text,
string Id,
bool Update,
bool IsDefault)
190 return await this.
Add(Source, Doc, Id,
Update, IsDefault);
201 return this.
Add(Source, Text,
string.Empty,
false,
true);
212 return this.
Add(Source, Markdown,
string.Empty,
false,
true);
229 await this.synchObject.BeginWrite();
232 if ((Result = !this.sources.TryGetValue(Source, out
SourceState State)) || State.IsDefault)
234 if (Result && this.filterSources)
238 this.sources[Source] = State;
241 if (Markdown is
null)
245 Type = await State.Update(Markdown, Id);
247 Type = await State.Add(Markdown, Id);
249 if ((
int)(this.type & Type) != 0)
250 this.type = (
DocumentType)Math.Max((
int)this.type, (int)Type);
262 string[] Rows0 =
null;
265 foreach (SourceState Info
in this.sources.Values)
267 Rows = Info.FirstDocument.Rows;
273 this.nrTop = this.nrBottom = d;
279 c = Math.Min(this.nrTop, d);
281 for (i = 0; i < c; i++)
283 if (Rows[i] != Rows0[i])
289 c = Math.Min(this.nrBottom, d);
291 for (i = 0; i < c; i++)
293 if (Rows[d - i - 1] != Rows0[d0 - i - 1])
301 if (this.nrTop < 1 || this.nrBottom <= 1)
306 if (this.sources.Count >= 2)
313 await this.synchObject.EndWrite();
317 await this.Raise(this.
Updated, Source);
319 await this.Raise(this.
Added, Source);
324 private Task Raise(EventHandlerAsync<SourceEventArgs> Handler,
string Source)
326 return Handler.Raise(
this,
new SourceEventArgs(Source));
332 public event EventHandlerAsync<SourceEventArgs>
Added =
null;
337 public event EventHandlerAsync<SourceEventArgs>
Updated =
null;
343 [Obsolete(
"Use GenerateMarkdownAsync() instead.")]
355 StringBuilder Markdown =
new StringBuilder();
357 await this.synchObject.BeginRead();
368 Markdown.AppendLine(
"| Nr | Source | Response |");
371 Markdown.AppendLine(
"|---:|:-------|-------:|");
373 Markdown.AppendLine(
"|---:|:-------|:-------|");
377 foreach (KeyValuePair<string, SourceState> P
in this.sources)
379 Markdown.Append(
"| ");
381 Markdown.Append(
" | `");
382 Markdown.Append(P.Key);
383 Markdown.Append(
"` | ");
384 Markdown.Append((await P.Value.GetFirstText()).Trim());
385 Markdown.AppendLine(
" |");
391 Markdown.AppendLine(
"| Nr | Source | Response |");
392 Markdown.AppendLine(
"|---:|:-------|:-------|");
396 foreach (KeyValuePair<string, SourceState> P
in this.sources)
398 Markdown.Append(
"| ");
400 Markdown.Append(
" | `");
401 Markdown.Append(P.Key);
402 Markdown.Append(
"` | ");
404 foreach (
string Row
in P.Value.FirstDocument.Rows)
406 Markdown.Append(Row);
407 Markdown.Append(
"<br/>");
410 Markdown.AppendLine(
"|");
417 List<string> ConsolidatedRows =
new List<string>();
419 int d = this.sources.Count;
421 foreach (KeyValuePair<string, SourceState> P
in this.sources)
423 string[] Rows = P.Value.FirstDocument.Rows;
435 ConsolidatedRows.Add(Rows[i]);
438 if (ConsolidatedRows[0].StartsWith(
"```dot", StringComparison.OrdinalIgnoreCase))
439 OptimizeDotEdges(ConsolidatedRows);
441 foreach (
string Row
in ConsolidatedRows)
442 Markdown.AppendLine(Row);
451 foreach (KeyValuePair<string, SourceState> P
in this.sources)
455 if (!(Doc?.Table is
null))
470 this.GenerateComplexLocked(Markdown);
481 if (this.legend is
null)
482 this.legend =
new Dictionary<string, KeyValuePair<SKColor, int>>();
484 if (this.palette is
null)
487 foreach (KeyValuePair<string, SourceState> P
in this.sources)
491 if (!(Doc?.
Graph is
null))
493 i = this.legend.Count % this.maxPaletteSize;
495 this.legend[P.Key] =
new KeyValuePair<SKColor, int>(this.palette[i], i);
505 Markdown.AppendLine(
"```Graph");
507 Markdown.AppendLine();
508 Markdown.AppendLine(
"```");
510 if (this.legend.Count > 0)
512 string[] Labels =
new string[this.legend.Count];
513 SKColor[] Colors =
new SKColor[this.legend.Count];
516 Markdown.AppendLine();
517 Markdown.Append(
"{{Legend([");
519 foreach (KeyValuePair<
string, KeyValuePair<SKColor, int>> P
in this.legend)
524 Markdown.Append(
',');
529 Markdown.Append(
"],[");
532 foreach (KeyValuePair<
string, KeyValuePair<SKColor, int>> P
in this.legend)
537 Markdown.Append(
',');
542 Markdown.AppendLine(
"],4)}}");
545 Markdown.AppendLine();
550 this.GenerateComplexLocked(Markdown);
556 this.GenerateComplexLocked(Markdown);
562 await this.synchObject.EndRead();
565 return Markdown.ToString();
568 private static readonly Regex dotEdge =
new Regex(
"^(?'A'(\"[^\"]*\"|'[^']*'|[^\\s]*))\\s*->\\s*(?'B'(\"[^\"]*\"|'[^']*'|[^\\s]*))\\s*([\\[](?'Note'[^\\]]*)[\\]]\\s*)?;\\s*$", RegexOptions.Singleline | RegexOptions.Compiled);
570 private static void OptimizeDotEdges(List<string> Rows)
574 string Row, Row2, A, B, Note;
575 int i, j, c = Rows.Count;
576 bool Changed =
false;
578 Matches =
new Match[c];
580 for (i = 0; i < c; i++)
586 M = dotEdge.Match(Row);
590 if (!M.Success || M.Index > 0 || M.Length < Row.Length)
593 A = M.Groups[
"A"].Value;
594 B = M.Groups[
"B"].Value;
595 Note = M.Groups[
"Note"].Value;
597 for (j = i + 1; j < c; j++)
603 M = dotEdge.Match(Row2);
609 M.Length == Row.Length &&
610 M.Groups[
"A"].Value == B &&
611 M.Groups[
"B"].Value == A &&
612 M.Groups[
"Note"].Value == Note)
614 StringBuilder sb =
new StringBuilder();
622 if (!
string.IsNullOrEmpty(Note))
625 sb.Append(
"dir=both];");
627 Rows[i] = sb.ToString();
629 Rows[j] =
string.Empty;
641 if (
string.IsNullOrEmpty(Rows[i]))
659 SKColor[] Result =
new SKColor[N];
660 double d = 360.0 / Math.Max(N, 12);
663 for (i = 0; i < N; i++)
664 Result[i] = SKColor.FromHsl((
float)(d * i), 100, 75);
669 private void GenerateComplexLocked(StringBuilder Markdown)
671 foreach (KeyValuePair<string, SourceState> P
in this.sources)
673 Markdown.Append(
'`');
674 Markdown.Append(P.Key);
675 Markdown.AppendLine(
"`");
676 Markdown.AppendLine();
680 if (Info.Length == 0)
681 Markdown.AppendLine(
":\t");
691 Markdown.AppendLine(
":\t");
693 if (!(Doc?.Rows is
null))
695 foreach (
string Row
in Doc.
Rows)
697 Markdown.Append(
":\t");
698 Markdown.AppendLine(Row);
704 Markdown.AppendLine();
711 [Obsolete(
"Use DisposeAsync() instead.")]
729 return this.
Disposed.Raise(
this, EventArgs.Empty,
false);
Represents a consolidated table.
async Task Export(StringBuilder Markdown)
Exports the consolidated table to Markdown.
async Task Add(string Source, Table MarkdownTable)
Adds data from a Markdown table to the consolidated table.
static async Task< ConsolidatedTable > CreateAsync(string Source, Table MarkdownTable)
Creates a consolidated table.
Consolidates Markdown from multiple sources, sharing the same thread.
Task< bool > Add(string Source, MarkdownDocument Markdown, string Id)
Adds incoming markdown information.
void Dispose()
IDisposable.Dispose
Task< bool > Add(string Source, string Text, string Id)
Adds incoming markdown information.
Task< bool > AddDefault(string Source, string Text)
Adds default markdown to present, until a proper response is returned.
Task< bool > Add(string Source, string Text)
Adds incoming markdown information.
Task< bool > Update(string Source, string Text, string Id)
Updates incoming markdown information.
object Tag
External tag object that can be tagged to the object by its owner.
async Task< int > GetNrReportedSources()
Number of sources that have reported content.
async Task< string[]> GetSources()
Consolidated sources.
string GenerateMarkdown()
Generates consolidated markdown from all sources.
Consolidator(string ThreadId, int MaxPaletteSize)
Consolidates Markdown from multiple sources, sharing the same thread.
EventHandlerAsync< SourceEventArgs > Added
Event raised when content from a source has been added.
Task DisposeAsync()
IDisposable.Dispose
Task< bool > AddDefault(string Source, MarkdownDocument Markdown)
Adds default markdown to present, until a proper response is returned.
async Task< string > GenerateMarkdownAsync()
Generates consolidated markdown from all sources.
Task< bool > Update(string Source, MarkdownDocument Markdown, string Id)
Updates incoming markdown information.
bool FilterSources
If input should be restricted to a defined set of sources.
EventHandlerAsync Disposed
Event raised when consolidator has been disposed.
static SKColor[] CreatePalette(int N)
Creates a palette for graphs.
EventHandlerAsync< SourceEventArgs > Updated
Event raised when content from a source has been updated.
Task< bool > Add(string Source, MarkdownDocument Markdown)
Adds incoming markdown information.
Information about a document.
Graph Graph
Graph object, if DocumentType.SingleGraph
Table Table
Table object, if DocumentType.SingleTable
Maintains the state of one source.
bool IsDefault
If the content is default content (true), or reported content (false).
Contains a markdown document. This markdown document class supports original markdown,...
static Task< MarkdownDocument > CreateAsync(string MarkdownText, params Type[] TransparentExceptionTypes)
Contains a markdown document. This markdown document class supports original markdown,...
Static class managing the application event log. Applications and services log events on this static ...
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.
Represents an object that allows single concurrent writers but multiple concurrent readers....
Class managing a script expression.
static string ToString(double Value)
Converts a value to a string, that can be parsed as part of an expression.
string ToXml()
Exports the graph to XML.
abstract bool TrySetDefaultColor(SKColor Color)
Tries to set the default color.
static string ToRGBAStyle(SKColor Color)
Converts a color to an RGB(A) style string.
virtual ISemiGroupElementWise AddRightElementWise(ISemiGroupElementWise Element)
Tries to add an element to the current element, from the right, element-wise.
Interface for consolidators.
DocumentType
Type of markdown document.
delegate string ToString(IElement Element)
Delegate for callback methods that convert an element value to a string.
delegate Task EventHandlerAsync(object Sender, EventArgs e)
Asynchronous version of EventArgs.