4using System.Threading.Tasks;
59 private static Dictionary<string, IStateMachineNode> stateMachineNodes =
null;
63 internal class CacheRecord
94 Result.XmlDefinition = Xml;
106 public static Task<StateMachine>
Parse(
string NormalizedXml, XmlDocument Xml,
Token Token)
108 return Parse(NormalizedXml, Xml.DocumentElement,
Token);
118 public static async Task<StateMachine>
Parse(
string NormalizedXml, XmlElement Xml,
Token Token)
126 XmlDefinition = NormalizedXml,
130 CreatorTokenId = Token.TokenId
144 out
string LocalName, out
string Namespace)
147 out LocalName, out Namespace) &&
157 internal static async Task<IStateMachineNode> Create(XmlElement Xml)
161 if (stateMachineNodes is
null)
163 Dictionary<string, IStateMachineNode> ByFqn =
new Dictionary<string, IStateMachineNode>();
167 if (T.IsAbstract || T.IsInterface || T.IsGenericTypeDefinition)
173 ByFqn[Node.Namespace +
"#" + Node.
LocalName] = Node;
181 stateMachineNodes = ByFqn;
184 string Fqn = Xml.NamespaceURI +
"#" + Xml.LocalName;
185 if (!stateMachineNodes.TryGetValue(Fqn, out Node))
186 throw new Exception(
"A State-Machine node named " + Fqn +
" not known.");
188 Node = Node.Create();
189 await Node.Parse(Xml);
204 CacheRecord CacheRecord = await GetStateMachine(
Token,
false);
205 if (CacheRecord is
null)
208 await EventGenerated(
Token,
Event, CacheRecord);
228 await TokenDestroyed(
Token, Machine);
232 if (!(CacheRecord.Profiler is
null))
234 int NoteNr = CacheRecord.Profiler.AddNote(ex);
235 CacheRecord.Profiler?.Exception(ex,
"Note" + NoteNr);
248 internal static async Task EventGenerated(
string StateMachineId,
TokenEvent Event)
250 CacheRecord CacheRecord = await GetStateMachine(StateMachineId,
false);
251 if (CacheRecord is
null)
256 await EventGenerated(
Token,
Event, CacheRecord);
265 internal static Task<CacheRecord> GetStateMachine(
Token Token,
bool Locked)
277 internal static async Task<CurrentState> GetOrCreateCurrentState(
StateMachine Machine,
bool Locked)
296 State =
string.Empty,
320 internal static async Task<CacheRecord> GetStateMachine(
string MachineId,
bool Locked)
322 if (
string.IsNullOrEmpty(MachineId))
325 if (stateMachines.TryGetValue(MachineId, out CacheRecord Result))
336 Result =
new CacheRecord()
345 stateMachines[MachineId] = Result;
347 Result.Profiler.Start();
348 Result.Profiler.Event(
"Reloaded");
358 internal static async Task<string> GetCurrentState(
string MachineId)
360 CacheRecord
State = await GetStateMachine(MachineId,
false);
374 Ledger.EntryAdded += Ledger_EntryAdded;
375 Ledger.EntryUpdated += Ledger_EntryUpdated;
376 Ledger.EntryDeleted += Ledger_EntryDeleted;
378 LinkedList<TimepointEventHandler> ToDelete =
null;
385 if (await ReregisterOnStart(Handler))
387 ToDelete ??=
new LinkedList<TimepointEventHandler>();
388 ToDelete.AddLast(Handler);
394 if (!(ToDelete is
null))
410 CacheRecord CacheRecord = await GetStateMachine(Handler.
StateMachineId,
false);
411 if (CacheRecord is
null)
442 internal static Task ModuleStopped()
447 Ledger.EntryAdded -= Ledger_EntryAdded;
448 Ledger.EntryUpdated -= Ledger_EntryUpdated;
449 Ledger.EntryDeleted -= Ledger_EntryDeleted;
453 return Task.CompletedTask;
481 await Machine.
Start(Arguments);
490 internal static void EventRaised(EventHandlers.EventHandler Handler,
Token Token,
491 params KeyValuePair<string, object>[] ToSet)
497 await ProcessEvent(Handler,
Token, ToSet);
506 internal static async Task<EventRecord> ProcessEvent(EventHandlers.EventHandler Handler,
507 Token Token, params KeyValuePair<string, object>[] ToSet)
510 return await EventRaisedLocked(Handler,
Token, ToSet);
513 internal class EventRecord
515 public CacheRecord CacheRecord;
516 public bool SuppressSamples;
519 internal static async Task<EventRecord> EventRaisedLocked(EventHandlers.EventHandler Handler,
520 Token Token, params KeyValuePair<string, object>[] ToSet)
522 EventRecord Result =
new EventRecord()
524 SuppressSamples =
false,
528 if (Result.CacheRecord is
null)
546 if (!(ToSet is
null))
548 foreach (KeyValuePair<string, object> P
in ToSet)
557 Result.CacheRecord.EDaler ?? eDaler, Result.CacheRecord.Profiler);
564 if (await
Event.Applies(Arguments))
568 Result.SuppressSamples = await
Event.GetSuppressSample(Arguments);
569 await
Event.ExecuteLog(Arguments, Result.SuppressSamples);
570 StateId = await
Event.GetNewState(Arguments);
574 StateId = await
Event.GetFailureState(Arguments);
576 if (
string.IsNullOrEmpty(StateId))
580 if (
string.IsNullOrEmpty(StateId))
593 if (!(Result.CacheRecord.Profiler is
null))
595 int NoteNr = Result.CacheRecord.Profiler.AddNote(ex);
596 Result.CacheRecord.Profiler?.Exception(ex,
"Note" + NoteNr);
619 if ((Handlers?.Length ?? 0) == 0)
626 KeyValuePair<string, object>[]
Variables = GetVariablesToSet(
648 if ((Handlers?.Length ?? 0) == 0)
651 XmlDocument Doc =
null;
678 if ((Handlers?.Length ?? 0) == 0)
708 if ((Handlers?.Length ?? 0) == 0)
711 XmlDocument Doc =
null;
727 private static async Task<bool> IsAuthorized(
string Source,
string Privilege)
729 bool CheckPrivilege = !
string.IsNullOrEmpty(
Privilege);
731 if (!CheckPrivilege && Source.IndexOf(
'@') > 0)
741 internal static KeyValuePair<string, object>[] GetVariablesToSet(
TokenNoteEventHandler Handler,
object Note,
bool Personal,
string Source)
743 List<KeyValuePair<string, object>> Result =
new List<KeyValuePair<string, object>>();
746 Result.Add(
new KeyValuePair<string, object>(Handler.
SourceVariable, Source));
749 Result.Add(
new KeyValuePair<string, object>(Handler.
PersonalVariable, Personal));
752 Result.Add(
new KeyValuePair<string, object>(Handler.
NoteVariable, Note));
754 return Result.ToArray();
771 if ((Handlers?.Length ?? 0) == 0)
781 internal static KeyValuePair<string, object>[] GetVariablesToSet(
784 List<KeyValuePair<string, object>> Result =
new List<KeyValuePair<string, object>>();
804 return Result.ToArray();
817 if ((Handlers?.Length ?? 0) == 0)
823 EventRaised(Handler,
null, GetVariablesToSet(PaymentHandler, Uri, Uri.
To.
BareJid));
838 if ((Handlers?.Length ?? 0) == 0)
842 Sender = SenderAddress.
BareJid;
847 EventRaised(Handler,
null, GetVariablesToSet(PaymentHandler, Uri, Sender));
853 List<KeyValuePair<string, object>> Result =
new List<KeyValuePair<string, object>>();
856 Result.Add(
new KeyValuePair<string, object>(Handler.
RemoteVariable, RemoteAddress));
881 return Result.ToArray();
893 StringBuilder sb =
new StringBuilder();
913 sb.Append(
Contract.ForMachinesLocalName);
915 sb.Append(
Contract.ForMachinesNamespace);
919 if ((Handlers?.Length ?? 0) > 0)
940 sb.Append(LocalName);
942 sb.Append(Namespace);
944 sb.Append(NameType.ToString());
948 if ((Handlers?.Length ?? 0) > 0)
974 StringBuilder sb =
new StringBuilder();
980 sb.Append(
Contract.ForMachinesLocalName);
982 sb.Append(
Contract.ForMachinesNamespace);
986 if ((Handlers?.Length ?? 0) > 0)
1007 sb.Append(LocalName);
1009 sb.Append(Namespace);
1011 sb.Append(NameType.ToString());
1015 if ((Handlers?.Length ?? 0) > 0)
1035 StringBuilder sb =
new StringBuilder();
1041 sb.Append(
Contract.ForMachinesLocalName);
1043 sb.Append(
Contract.ForMachinesNamespace);
1047 if ((Handlers?.Length ?? 0) > 0)
1068 sb.Append(LocalName);
1070 sb.Append(Namespace);
1072 sb.Append(NameType.ToString());
1076 if ((Handlers?.Length ?? 0) > 0)
1090 internal static async Task<KeyValuePair<string, object>[]> GetVariablesToSet(
ContractEventHandler Handler,
1093 List<KeyValuePair<string, object>> Result =
new List<KeyValuePair<string, object>>();
1100 StringBuilder sb =
new StringBuilder();
1101 await
Contract.
Serialize(sb,
true,
true,
true,
true,
true,
true,
true,
null, legal);
1103 XmlDocument Doc =
XML.
ParseXml(sb.ToString(),
true);
1110 Dictionary<string, object> Parameters =
new Dictionary<string, object>()
1112 {
"Duration", Contract.Duration }
1124 Result.Add(
new KeyValuePair<string, object>(Handler.
ParametersVariable, Parameters));
1129 Dictionary<string, ChunkedList<string>>
Roles =
new Dictionary<string, ChunkedList<string>>();
1142 Dictionary<string, object> Roles2 =
new Dictionary<string, object>();
1145 Roles2[P.Key] = P.Value.ToArray();
1147 Result.Add(
new KeyValuePair<string, object>(Handler.
RolesVariable, Roles2));
1157 Result.Add(
new KeyValuePair<string, object>(Handler.
LegalIdVariable, LegalId?.Value));
1162 return Result.ToArray();
1180 Machine,
Token, Record.CurrentState, Record.Legal, Record.EDaler,
1187 if ((Handlers?.Length ?? 0) > 0)
1192 await EventRaisedLocked(Handler,
Token);
1221 EventHandlerAsync<QuickLoginResponseEventArgs> Callback,
object State)
1226 CacheRecord Record = await GetStateMachine(
Token.
MachineId,
false);
1231 StringBuilder sb =
new StringBuilder();
1232 DateTime TP = DateTime.UtcNow;
1234 sb.Append(
"To kill a state-machine. By signing this request, you ");
1235 sb.Append(
"confirm you have reviewed the token (");
1237 sb.Append(
") and understand the consequences of killing the state-machine. ");
1238 sb.Append(
"You also agree to store your approval and digital signature in ");
1239 sb.Append(
"the event log, and take full responsibility of the killing of the ");
1240 sb.Append(
"state-machine. This petition was sent ");
1241 sb.Append(TP.ToShortDateString());
1243 sb.Append(TP.ToLongTimeString());
1246 string Purpose = sb.ToString();
1249 await
QuickLogin.StartQuickLogin(LegalId, Purpose, Content,
null,
null, RemoteEndPoint,
1250 async (Sender, e) =>
1254 Variables Variables = Record.CurrentState.GetVariables(Record.Machine);
1255 EvaluationArguments Arguments = new EvaluationArguments(Variables,
1256 Record.Machine, Token, Record.CurrentState, Record.Legal, Record.EDaler,
1259 using Semaphore Semaphore = await Semaphores.BeginWrite(
"machine:" + Token.MachineId.Value);
1261 await StateMachine.GoToState(string.Empty, Arguments);
1263 StringBuilder sb = new StringBuilder();
1264 e.RequestedIdentity.Serialize(sb, true, true, true, true, true, true, true);
1266 NoteText Comment = new NoteText()
1268 ArchiveOptional = Token.ArchiveOptional,
1269 ArchiveRequired = Token.ArchiveRequired,
1270 Expires = Token.Expires,
1272 Timestamp = DateTime.UtcNow,
1274 Note =
"State-machine killed by " + LegalId
1288 Currency =
string.
Empty,
1289 Timestamp = DateTime.UtcNow,
1293 ClientEndPoint = e.ClientEndpoint,
1294 PetitionId = e.PetitionId,
1296 Content = e.SignatureContent,
1298 ClientIdentityXml = sb.ToString()
1305 e.SignatureContent,
State);
1307 await Callback.Raise(Sender, e2);
1313 private static void Ledger_EntryAdded(
object Sender,
ObjectEventArgs e)
1318 private static void Ledger_EntryUpdated(
object Sender,
ObjectEventArgs e)
1323 private static void Ledger_EntryDeleted(
object Sender,
ObjectEventArgs e)
1328 internal static async Task<(int, int, int, int)> DeleteExpiredMachines()
1331 int NrEventHandlers = 0;
1332 int NrCurrentStates = 0;
1342 if (stateMachines.TryGetValue(Machine.
StateMachineId, out CacheRecord Record))
1346 if (!
string.IsNullOrEmpty(Record.CurrentState?.State) &&
1348 !(State.OnEvent is
null))
1354 Machine,
null, Record.CurrentState, Record.Legal, Record.EDaler,
1359 catch (Exception ex)
1366 IEnumerable<EventHandlers.EventHandler> EventHandlers = await
Database.
FindDelete<EventHandlers.EventHandler>(
1369 foreach (EventHandlers.EventHandler
EventHandler in EventHandlers)
1384 return (NrMachines, NrEventHandlers, NrCurrentStates, NrSamples);
1387 #region XMPP Interface
1393 EDaler.RegisterIqGetHandler(
"currentState", StateMachineNamespace, GetCurrentStateHandler,
true);
1394 EDaler.RegisterIqGetHandler(
"profilingReport", StateMachineNamespace, GetProfilingReport,
false);
1395 EDaler.RegisterIqGetHandler(
"presentReport", StateMachineNamespace, GetPresentReport,
false);
1396 EDaler.RegisterIqGetHandler(
"historyReport", StateMachineNamespace, GetHistoryReport,
false);
1397 EDaler.RegisterIqGetHandler(
"stateDiagram", StateMachineNamespace, GetStateDiagram,
false);
1404 EDaler.UnregisterIqGetHandler(
"currentState", StateMachineNamespace, GetCurrentStateHandler,
true);
1405 EDaler.UnregisterIqGetHandler(
"profilingReport", StateMachineNamespace, GetProfilingReport,
false);
1406 EDaler.UnregisterIqGetHandler(
"presentReport", StateMachineNamespace, GetPresentReport,
false);
1407 EDaler.UnregisterIqGetHandler(
"historyReport", StateMachineNamespace, GetHistoryReport,
false);
1408 EDaler.UnregisterIqGetHandler(
"stateDiagram", StateMachineNamespace, GetStateDiagram,
false);
1411 #region GetCurrentStateHandler
1413 private static async Task GetCurrentStateHandler(
object Sender,
IqEventArgs e)
1416 if (
string.IsNullOrEmpty(TokenId))
1426 StringBuilder Xml =
new StringBuilder();
1436 Xml.Append(
"<currentState xmlns='");
1437 Xml.Append(StateMachineNamespace);
1438 Xml.Append(
"' state='");
1440 Xml.Append(
"' ended='");
1442 Xml.Append(
"' running='");
1444 Xml.Append(
"' expires='");
1454 Xml.Append(
"</currentState>");
1457 internal static void AppendVariable(StringBuilder Xml,
string Name,
object Value)
1459 Xml.Append(
"<variable name='");
1464 Xml.Append(
"<null/>");
1465 else if (Value is
double dbl)
1467 Xml.Append(
"<dbl>");
1469 Xml.Append(
"</dbl>");
1471 else if (Value is
float fl)
1475 Xml.Append(
"</fl>");
1477 else if (Value is decimal dec)
1479 Xml.Append(
"<dec>");
1481 Xml.Append(
"</dec>");
1483 else if (Value is
int i32)
1485 Xml.Append(
"<i32>");
1486 Xml.Append(i32.ToString());
1487 Xml.Append(
"</i32>");
1489 else if (Value is
long i64)
1491 Xml.Append(
"<i64>");
1492 Xml.Append(i64.ToString());
1493 Xml.Append(
"</i64>");
1495 else if (Value is
short i16)
1497 Xml.Append(
"<i16>");
1498 Xml.Append(i16.ToString());
1499 Xml.Append(
"</i16>");
1501 else if (Value is sbyte i8)
1504 Xml.Append(i8.ToString());
1505 Xml.Append(
"</i8>");
1507 else if (Value is uint ui32)
1509 Xml.Append(
"<ui32>");
1510 Xml.Append(ui32.ToString());
1511 Xml.Append(
"</ui32>");
1513 else if (Value is ulong ui64)
1515 Xml.Append(
"<ui64>");
1516 Xml.Append(ui64.ToString());
1517 Xml.Append(
"</ui64>");
1519 else if (Value is ushort ui16)
1521 Xml.Append(
"<ui16>");
1522 Xml.Append(ui16.ToString());
1523 Xml.Append(
"</ui16>");
1525 else if (Value is
byte ui8)
1527 Xml.Append(
"<ui8>");
1528 Xml.Append(ui8.ToString());
1529 Xml.Append(
"</ui8>");
1531 else if (Value is
bool b)
1537 else if (Value is DateTime TP)
1541 Xml.Append(
"</dt>");
1543 else if (Value is DateTimeOffset TPO)
1545 Xml.Append(
"<dto>");
1547 Xml.Append(
"</dto>");
1549 else if (Value is TimeSpan TS)
1553 Xml.Append(
"</ts>");
1561 else if (Value is
string s)
1569 Xml.Append(
"<exp>");
1571 Xml.Append(
"</exp>");
1574 Xml.Append(
"</variable>");
1577 private static async Task<CacheRecord> GetCacheRecord(
string TokenId,
IqEventArgs e)
1602 CacheRecord Record = await GetStateMachine(
Token.
MachineId,
false);
1612 private static async Task<CurrentState> GetCurrentState(
string TokenId,
IqEventArgs e)
1614 CacheRecord Record = await GetCacheRecord(TokenId, e);
1618 return Record.CurrentState;
1623 #region GetProfilingReport
1625 internal static async Task<string> GetProfilingReportAsync(
string TokenId,
ReportFormat Format)
1627 CacheRecord Record = await GetCacheRecord(TokenId,
null);
1629 return string.Empty;
1631 return await GetProfilingReport(Record.Machine.StateMachineId.Value,
1632 Record.Profiler, Format);
1635 private static async Task GetProfilingReport(
object Sender,
IqEventArgs e)
1638 if (
string.IsNullOrEmpty(TokenId))
1644 CacheRecord Record = await GetCacheRecord(TokenId, e);
1655 StringBuilder Xml =
new StringBuilder();
1657 Xml.Append(
"<report xmlns='");
1658 Xml.Append(StateMachineNamespace);
1661 Xml.Append(
XML.
Encode(await GetProfilingReport(
1662 Record.Machine.StateMachineId.Value, Record.Profiler,
ReportFormat)));
1664 Xml.Append(
"</report>");
1669 private static async Task<string> GetProfilingReport(
string StateMachineId,
Profiler Profiler,
1673 StringBuilder Markdown =
new StringBuilder();
1675 Markdown.AppendLine(
"Timing Diagram");
1676 Markdown.AppendLine(
"==================");
1677 Markdown.AppendLine();
1678 Markdown.AppendLine(
"```uml");
1680 Markdown.AppendLine(
"```");
1684 for (i = 1; i <= c; i++)
1688 Markdown.AppendLine();
1689 Markdown.Append(
"Note ");
1690 Markdown.AppendLine(i.ToString());
1691 Markdown.AppendLine(
"-----------------");
1692 Markdown.AppendLine();
1694 if (Note is
string s)
1696 if (s.StartsWith(
"@startjson") && s.EndsWith(
"@endjson"))
1698 Markdown.AppendLine(
"```uml");
1699 Markdown.AppendLine(s);
1700 Markdown.AppendLine(
"```");
1708 Markdown.AppendLine();
1709 Markdown.AppendLine(
"```");
1710 Markdown.AppendLine(ScriptError.Node?.SubExpression ?? ScriptError.StackTrace);
1711 Markdown.AppendLine(
"```");
1717 else if (Note is Exception ex)
1720 Markdown.AppendLine();
1721 Markdown.AppendLine(
"```");
1722 Markdown.AppendLine(ex.StackTrace);
1723 Markdown.AppendLine(
"```");
1727 Markdown.AppendLine(
"```uml");
1728 Markdown.AppendLine(
"@startjson");
1729 Markdown.AppendLine(
JSON.
Encode(Note,
true));
1730 Markdown.AppendLine(
"@endjson");
1731 Markdown.AppendLine(
"```");
1741 #region GetPresentReport
1745 CacheRecord Record = await GetStateMachine(
Token,
false);
1747 return string.Empty;
1749 return await GetPresentReport(Record, Format);
1752 private static async Task GetPresentReport(
object Sender,
IqEventArgs e)
1755 if (
string.IsNullOrEmpty(TokenId))
1761 CacheRecord Record = await GetCacheRecord(TokenId, e);
1766 StringBuilder Xml =
new StringBuilder();
1768 Xml.Append(
"<report xmlns='");
1769 Xml.Append(StateMachineNamespace);
1772 Xml.Append(
"</report>");
1777 private static async Task<string> GetPresentReport(CacheRecord Record,
ReportFormat Format)
1780 string Markdown =
null;
1786 Markdown = Report.Markdown;
1791 if (
string.IsNullOrEmpty(Markdown))
1792 return string.Empty;
1801 #region GetHistoryReport
1805 CacheRecord Record = await GetStateMachine(
Token,
false);
1807 return string.Empty;
1809 return await GetHistoryReport(Record, Format);
1812 private static async Task GetHistoryReport(
object Sender,
IqEventArgs e)
1815 if (
string.IsNullOrEmpty(TokenId))
1821 CacheRecord Record = await GetCacheRecord(TokenId, e);
1826 StringBuilder Xml =
new StringBuilder();
1828 Xml.Append(
"<report xmlns='");
1829 Xml.Append(StateMachineNamespace);
1832 Xml.Append(
"</report>");
1837 private static async Task<string> GetHistoryReport(CacheRecord Record,
ReportFormat Format)
1840 string Markdown =
null;
1846 Markdown = Report.Markdown;
1851 if (
string.IsNullOrEmpty(Markdown))
1852 return string.Empty;
1867 CacheRecord Record = await GetStateMachine(MachineId,
false);
1868 return await GetHistoryVariables(Record);
1871 private static async Task<Variables> GetHistoryVariables(CacheRecord Record)
1873 Dictionary<string, SortedDictionary<DateTime, StateMachineSample>> ByVariableAndTime =
new Dictionary<string, SortedDictionary<DateTime, StateMachineSample>>();
1874 SortedDictionary<DateTime, StateMachineSample> ByTime =
null;
1877 "Variable",
"Timestamp");
1878 string LastVariable =
null;
1880 DateTime Now = DateTime.UtcNow;
1884 if (ByTime is
null || Sample.
Variable != LastVariable)
1888 if (!ByVariableAndTime.TryGetValue(LastVariable, out ByTime))
1890 ByTime =
new SortedDictionary<DateTime, StateMachineSample>(timestampDescending);
1891 ByVariableAndTime[LastVariable] = ByTime;
1898 foreach (KeyValuePair<
string, SortedDictionary<DateTime, StateMachineSample>> P
in ByVariableAndTime)
1906 Value = v.ValueObject,
1909 ArchiveOptional = Record.Machine.ArchiveOptional,
1910 ArchiveRequired = Record.Machine.ArchiveRequired
1915 P.Value.Values.CopyTo(History, 0);
1922 private class TimestampDescending : IComparer<DateTime>
1924 public int Compare(DateTime x, DateTime y)
1926 return y.CompareTo(x);
1930 private static readonly TimestampDescending timestampDescending =
new TimestampDescending();
1934 #region GetStateDiagram
1938 CacheRecord Record = await GetStateMachine(
Token,
false);
1940 return string.Empty;
1942 return await GetStateDiagram(Record, Format);
1945 private static async Task GetStateDiagram(
object Sender,
IqEventArgs e)
1948 if (
string.IsNullOrEmpty(TokenId))
1954 CacheRecord Record = await GetCacheRecord(TokenId, e);
1959 StringBuilder Xml =
new StringBuilder();
1961 Xml.Append(
"<report xmlns='");
1962 Xml.Append(StateMachineNamespace);
1965 Xml.Append(
"</report>");
1970 private static async Task<string> GetStateDiagram(CacheRecord Record,
ReportFormat Format)
1973 StringBuilder Markdown =
new StringBuilder();
1975 Dictionary<string, string> States =
new Dictionary<string, string>();
1976 LinkedList<State> StateNodes =
new LinkedList<State>();
1980 Markdown.AppendLine(
"```uml");
1981 Markdown.AppendLine(
"@startuml");
1982 Markdown.AppendLine();
1983 Markdown.AppendLine(
"skinparam state {");
1984 Markdown.AppendLine(
"\tFontSize 12");
1985 Markdown.AppendLine(
"\tFontStyle Italic");
1986 Markdown.AppendLine(
"\tAttributeFontSize 18");
1987 Markdown.AppendLine(
"\tBackgroundColor<<Action>> LightSalmon");
1988 Markdown.AppendLine(
"\tBackgroundColor<<Calc>> LightBlue");
1989 Markdown.AppendLine(
"\tBackgroundColor<<Current>> LightGreen");
1990 Markdown.AppendLine(
"\tFontStyle<<Current>> Bold, Italic");
1991 Markdown.AppendLine(
"\tAttributeFontStyle<<Current>> Bold");
1992 Markdown.AppendLine(
"\tBorderColor<<Current>> Black");
1993 Markdown.AppendLine(
"}");
1994 Markdown.AppendLine();
1995 Markdown.AppendLine(
"skinparam note {");
1996 Markdown.AppendLine(
"\tBackgroundColor<<Warning>> Salmon");
1997 Markdown.AppendLine(
"}");
1998 Markdown.AppendLine();
2000 Markdown.AppendLine(
"state \"Initialize\" as Init");
2001 Markdown.AppendLine(
"[*] --> Init");
2003 Markdown.Append(
"Init --> ");
2004 Markdown.AppendLine(GetStateLabel(States, Machine.
Root.
StartState));
2008 if (Node is Model.Variable
Variable)
2010 Markdown.Append(
"Init : ");
2014 !
string.IsNullOrEmpty(StateVariable.InitExpression))
2016 Markdown.Append(
":=");
2017 Markdown.Append(EncodeLabel(StateVariable.InitExpression));
2020 Markdown.AppendLine();
2024 StateNodes.AddLast(
State);
2026 Markdown.AppendLine();
2027 Markdown.Append(
"state \"State\" as ");
2028 Markdown.Append(GetStateLabel(States,
State.
Id));
2030 if (
State.
Id == Record.CurrentState.State)
2031 Markdown.Append(
" <<Current>>");
2033 Markdown.Append(
" : ");
2034 Markdown.AppendLine(EncodeLabel(
State.
Id));
2040 s = GetStateLabel(States,
State.
Id);
2044 Markdown.Append(
"note right of ");
2046 Markdown.AppendLine(
" <<Warning>> : No events defined.");
2051 GenerateEventStateDiagram(
Event, Markdown,
State, ref AIndex, s, States, Machine);
2053 Markdown.AppendLine();
2056 Markdown.AppendLine(
"@enduml");
2057 Markdown.AppendLine(
"```");
2062 private static void GenerateEventStateDiagram(
OnEvent Event, StringBuilder Markdown,
2063 State State, ref
int AIndex,
string s, Dictionary<string, string> States,
2074 s2 =
"A" + AIndex.ToString();
2076 Markdown.Append(
"state \"OnLeave Action\" as ");
2077 Markdown.Append(s2);
2078 Markdown.Append(
" <<Action>> : ");
2082 Markdown.Append(
" --> ");
2083 Markdown.Append(s2);
2085 if (!
string.IsNullOrEmpty(s3))
2087 Markdown.Append(
" : ");
2088 Markdown.Append(EncodeLabel(s3));
2092 Markdown.AppendLine();
2097 if (
Event.HasActionReference)
2100 s2 =
"A" + AIndex.ToString();
2102 Markdown.Append(
"state \"Event Action\" as ");
2103 Markdown.Append(s2);
2104 Markdown.Append(
" <<Action>> : ");
2105 Markdown.AppendLine(EncodeLabel(
Event.ActionReferenceDefinition));
2108 Markdown.Append(
" --> ");
2109 Markdown.Append(s2);
2111 if (!
string.IsNullOrEmpty(s3))
2113 Markdown.Append(
" : ");
2114 Markdown.Append(EncodeLabel(s3));
2118 Markdown.AppendLine();
2121 if (
Event.HasFailureState)
2124 else if (
Event.HasFailureState)
2126 if (
string.IsNullOrEmpty(s3))
2137 Actions = NewState.OnEnter;
2138 Termination = NewState.IsDone(Machine);
2143 Termination =
false;
2146 GenerateOnEnterBranch(
Event, Markdown,
State, ref AIndex, s, States,
2147 Actions, s3,
Event.NewStateDefinition, Termination);
2149 if (
Event.HasFailureState)
2153 Actions = FailureState.OnEnter;
2154 Termination = FailureState.IsDone(Machine);
2159 Termination =
false;
2162 GenerateOnEnterBranch(
Event, Markdown,
State, ref AIndex, s, States,
2163 Actions,
"Error",
Event.FailureStateDefinition, Termination);
2167 private static void GenerateOnEnterBranch(
OnEvent Event, StringBuilder Markdown,
2168 State State, ref
int AIndex,
string s, Dictionary<string, string> States,
2169 ActionReference[] Actions,
string s3,
string NewState,
bool Termination)
2173 if (!(Actions is
null) && !Termination)
2178 s2 =
"A" + AIndex.ToString();
2180 Markdown.Append(
"state \"OnEnter Action\" as ");
2181 Markdown.Append(s2);
2182 Markdown.Append(
" <<Action>> : ");
2183 Markdown.AppendLine(EncodeLabel(
Action.ActionReferenceDefinition));
2186 Markdown.Append(
" --> ");
2187 Markdown.Append(s2);
2189 if (!
string.IsNullOrEmpty(s3))
2191 Markdown.Append(
" : ");
2192 Markdown.Append(EncodeLabel(s3));
2196 Markdown.AppendLine();
2205 if (
string.IsNullOrEmpty(NewState))
2208 bool VarState = !States.ContainsKey(NewState);
2210 s2 = GetStateLabel(States, NewState);
2214 Markdown.Append(
"state \"Calc\" as ");
2215 Markdown.Append(s2);
2216 Markdown.Append(
" <<Calc>> : ");
2217 Markdown.AppendLine(EncodeLabel(
Event.NewStateDefinition));
2219 Markdown.Append(
"note right of ");
2220 Markdown.Append(s2);
2221 Markdown.AppendLine(
" <<Warning>> : Variable state, through script.");
2226 Markdown.Append(
" --> ");
2227 Markdown.Append(s2);
2229 if (!
string.IsNullOrEmpty(s3))
2231 Markdown.Append(
" : ");
2232 Markdown.Append(EncodeLabel(s3));
2235 Markdown.AppendLine();
2239 private static string GetStateLabel(Dictionary<string, string> States,
string StateId)
2241 if (!States.TryGetValue(StateId, out
string Id))
2243 int Index = States.Count + 1;
2244 States[StateId] = Id =
"S" + Index.ToString();
2250 private static string EncodeLabel(
string s)
2253 Replace(
"\\",
"\\\\").
2255 Replace(
"\a",
"\\a").
2256 Replace(
"\b",
"\\b").
2257 Replace(
"\f",
"\\f").
2258 Replace(
"\n",
"\\n").
2259 Replace(
"\r",
"\\r").
2260 Replace(
"\t",
"\\t").
2261 Replace(
"\v",
"\\v");
Helps with parsing of commong data types.
static string Encode(bool x)
Encodes a Boolean for use in XML and other formats.
Helps with common JSON-related tasks.
static string Encode(string s)
Encodes a string for inclusion in JSON.
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 ...
Helps with common XML-related tasks.
static string Attribute(XmlElement E, string Name)
Gets the value of an XML attribute.
static string Encode(string s)
Encodes a string for use in XML.
static XmlDocument ParseXml(string Xml)
Parses an XML Document from its string representation.
Class representing an event.
Event(DateTime Timestamp, EventType Type, string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Class representing an event.
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.
static void Error(string Message, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, string StackTrace, params KeyValuePair< string, object >[] Tags)
Logs an error event.
Implements an HTTP server.
static SessionVariables CreateSessionVariables()
Creates a new collection of variables, that contains access to the global set of variables.
bool IsComponentDomain(CaseInsensitiveString Domain, bool IncludeAlternativeDomains)
Checks if a domain is the component domain, or optionally, an alternative component domain.
Event arguments for IQ queries.
Task IqResult(string Xml, string From)
Returns a response to the current request.
Task IqErrorItemNotFound(XmppAddress From, string ErrorText, string Language)
Returns a item-not-found error.
XmlElement Query
Query element, if found, null otherwise.
XmppAddress To
To address attribute
Task IqErrorServiceUnavailable(XmppAddress From, string ErrorText, string Language)
Returns a service-unavailable error.
Task IqErrorBadRequest(XmppAddress From, string ErrorText, string Language)
Returns a bad-request error.
Contains information about one XMPP address.
bool IsBareJID
If the address is a Bare JID.
CaseInsensitiveString Domain
Domain
CaseInsensitiveString BareJid
Bare JID
CaseInsensitiveString Account
Account
Represents a case-insensitive string.
string Value
String-representation of the case-insensitive string. (Representation is case sensitive....
static readonly CaseInsensitiveString Empty
Empty case-insensitive string
string LowerCase
Lower-case representation of the case-insensitive string.
static bool IsNullOrEmpty(CaseInsensitiveString value)
Indicates whether the specified string is null or an CaseInsensitiveString.Empty string.
Static interface for database persistence. In order to work, a database provider has to be assigned t...
static Task< IEnumerable< object > > FindDelete(string Collection, params string[] SortOrder)
Finds objects in a given collection and deletes them in the same atomic operation.
static async Task Delete(object Object)
Deletes an object in the database.
static Task< IEnumerable< object > > Find(string Collection, params string[] SortOrder)
Finds objects in a given collection.
static async Task Insert(object Object)
Inserts an object into the default collection of the database.
This filter selects objects that have a named field equal to a given value.
This filter selects objects that have a named field lesser than a given value.
Event arguments for database object events.
Implements an in-memory cache.
A chunked list is a linked list of chunks of objects of type T .
Static class managing persistent counters.
static Task< bool > AddHash(byte[] Hash)
Persists a hash, using the default (empty) realm.
Static class that dynamically manages types and interfaces available in the runtime environment.
static object Instantiate(Type Type, params object[] Arguments)
Returns an instance of the type Type . If one needs to be created, it is. If the constructor requires...
static Type[] GetTypesImplementingInterface(string InterfaceFullName)
Gets all types implementing a given interface.
Class that keeps track of events and timing.
bool TryGetNote(int Index, out object Note)
Tries to get a note from the profile.
Profiler()
Class that keeps track of events and timing.
string ExportPlantUml(TimeUnit TimeUnit)
Exports events to PlantUML.
int NoteCount
Number of notes added.
void Start()
Starts measuring time.
Represents a named semaphore, i.e. an object, identified by a name, that allows single concurrent wri...
async Task DisposeAsync()
Disposes of the object, asynchronously.
Static class of application-wide semaphores that can be used to order access to editable objects.
static async Task< Semaphore > BeginRead(string Key)
Waits until the semaphore identified by Key is ready for reading. Each call to BeginRead must be fol...
static async Task< Semaphore > BeginWrite(string Key)
Waits until the semaphore identified by Key is ready for writing. Each call to BeginWrite must be fo...
Script runtime exception.
Class managing a script expression.
static string ToExpressionString(object Value)
Converts an object to a string, that can be parsed as part of an expression.
Contains information about a variable.
string Name
Name of variable.
virtual bool TryGetVariable(string Name, out Variable Variable)
Tries to get a variable object, given its name.
Contains methods for simple hash calculations.
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
byte[] ComputeVariable(byte[] N)
Computes the SPONGE function, as defined in section 4 of NIST FIPS 202.
Implements the SHA3-256 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Corresponds to a privilege in the system.
Corresponds to a role in the system.
Maintains the collection of all roles in the system.
Corresponds to a user in the system.
bool HasPrivilege(string Privilege)
If the user has a given privilege.
Maintains the collection of all users in the system.
static async Task< User > GetUser(string UserName, bool CreateIfNew)
Gets the User object corresponding to a User Name.
Manages eDaler on accounts connected to the broker.
Abstract base class for eDaler URIs
CaseInsensitiveString ContractCondition
Optional Contract defining conditions that must be met before payment can be realized....
decimal TotalAmount
Total amount: Amount+AmountExtra
byte[] EncryptedMessage
Encrypted message for recipient. If EncryptionPublicKey is null, the message is just UTF-8 encoded.
decimal? AmountExtra
Any extra amount
byte[] EncryptionPublicKey
Sender public key used to generate the shared secret to encrypt the message for the recipient....
Represents a digital signature on a contract.
Contains the definition of a contract
async Task Serialize(StringBuilder Xml, bool IncludeNamespace, bool IncludeIdAttribute, bool IncludeClientSignatures, bool IncludeAttachments, bool IncludeStatus, bool IncludeServerSignature, bool IncludeAttachmentReferences, Dictionary< string, string > AttachmentsUrls, LegalComponent LegalComponent)
Serializes the Contract, in normalized form.
DateTime? FirstSignatureAt
Timestamp of first client signature, if one exists.
DateTime Expires
When contract expires.
CaseInsensitiveString ContractId
Contract Identity
Contains information about a contract signature.
Abstract base class for contractual parameters
abstract object ObjectValue
Parameter value.
CaseInsensitiveString Name
Parameter name
Abstract base class of signatures
Legal (digital identities, smart contracts) service component.
Web resource that allows calling applications to enable Quick-Login using legal identities.
Event arguments for Quick-Login responses
Event raised when a token has been destroyed.
A text note logged on the token from an external source.
An xml note logged on the token from an external source.
Event raised when a state-machine has been killed by an operator.
A text note logged on the token.
An xml note logged on the token.
Abstract base class for token events.
Event raised when a token has been transferred.
Marketplace processor, brokering sales of items via tenders and offers defined in smart contracts.
bool HasStateMachine
If the token has an associated state-machine.
Duration? ArchiveOptional
Duration after which token expires, and the required archiving time, the token can optionally be arch...
string Definition
M2M definition of token, in XML, from the original creation contract.
CaseInsensitiveString OwnershipContract
ID of contract that details the claims of the current owner
string DefinitionNamespace
Namespace of M2M definition of token.
CaseInsensitiveString Owner
Current owner of token
CaseInsensitiveString TrustProviderJid
JID of Trust Provider, asserting claims in the token.
CaseInsensitiveString CreationContract
ID of contract that details the creation of the token.
XmlDocument DefinitionParsed
Parsed definition
Duration? ArchiveRequired
Duration after which token expires, the token is required to be archived.
CaseInsensitiveString MachineId
State Machine ID, if any
CaseInsensitiveString TrustProvider
Trust Provider, asserting claims in the token.
DateTime Expires
Expiry date of token.
CaseInsensitiveString TokenId
Token ID
Class representing the current state of a state machine.
bool IsRunning
If state-machine is running.
CurrentStateVariable[] VariableValues
Current variable values.
bool HasEnded
If state-machine has ended.
DateTime Expires
When state-machine expires
Variables GetVariables(StateMachine Machine)
Gets a new variable collection containing the current state variables.
string State
ID of current state in state-machine. Empty State = State-machine has ended.
CurrentState()
Class representing the current state of a state machine.
CaseInsensitiveString StateMachineId
ID of State-Machine.
Class representing a persisted state-machine variable value.
Abstract base class for cached event handlers.
static async Task< CachedEventHandler[]> GetEventHandlers(string EventType)
Gets registered event handlers of a given event type.
async Task RemoveFromCache()
Removes the event handler from the cache.
Event handler for contract events.
string LegalIdVariable
Legal ID variable name.
string MachineReadableVariable
Machine-readable variable name.
string RolesVariable
Roles variable name.
string ContractIdVariable
Contract ID variable name.
string ContractXmlVariable
Contract XML variable name.
string ParametersVariable
Parameters variable name.
string RoleVariable
Role variable name.
Abstract base class for persisted state-machine event handlers.
string State
ID of state in state-machine to which the event handler belongs.
string StateMachineId
ID of State-Machine.
int EventIndex
Zero-based index of event handler in state.
Event handler for owner events.
Abstract base class for payment event handlerss.
string RemoteVariable
Optional repote partty variable.
string AmountExtraVariable
Optional amount extra variable.
string ReferenceVariable
Optional reference variable.
string AmountVariable
Optional amount variable.
string ConditionVariable
Optional condition variable.
string AmountTotalVariable
Optional amount total variable.
string CurrencyVariable
Optional currency variable.
Event handler for timepoint events.
Event handler for token events.
Event handler for token note events.
string PersonalVariable
Optional personal variable.
string NoteVariable
Optional note variable.
string SourceVariable
Optional source variable.
string Privilege
Privilege required from external party.
Event handler for token transfer events.
string ValueVariable
Optional value variable.
string SellerVariable
Optional seller variable.
string BuyerVariable
Optional buyer variable.
string ContractVariable
Optional contract variable.
string AmountVariable
Optional amount variable.
string CurrencyVariable
Optional currency variable.
Exception thrown when the Error action is executed.
Represents an action definition.
Abstract base class for nodes referencing an action.
string ActionReferenceDefinition
Action Reference
Defines an expiry timestamp.
Expires()
Defines an expiry timestamp.
Defines the name of a variable.
Contains information required for evaluating script in a state-machine.
StateMachine Machine
Reference to state-machine definition.
Event raised when someone signs a contract where the owner of the associated token is part.
Event raised when a contract where the owner of the associated token is part, has become signed.
Event raised when a contract template has been approved.
Event raised when an associated token gets destroyed.
Event raised when an external text note has been logged on the token corresponding to the state-machi...
Event raised when an external XML note has been logged on the token corresponding to the state-machin...
Event raised when a payment has been received.
Event raised when a payment has been sent.
Event raised when a text note has been logged on the token corresponding to the state-machine.
Event raised when an associated token is transferred to a new owner.
Event raised when an XML note has been logged on the token corresponding to the state-machine.
Abstract base class for timed State-Machine event nodes.
async Task ReregisterOnStart(TimepointEventHandler Handler)
Re-registers the event on module start.
Contains a report over the history of the state-machine.
Contains a report over the present state of the state-machine.
IStateMachineNode[] ChildNodes
Child nodes, if available. Null if no children.
Root of the State-Machine definition
string StartState
Start State of state-machine.
Action executed when entering a state.
Action executed when entering a state.
Represents an action definition.
State()
Represents an action definition.
OnLeave[] OnLeave
Events raised when leaving the state.
OnEvent[] OnEvent
Events that can be raised when in the state.
Class representing a state machine.
StateMachineRoot Root
Root of State-Machine model.
bool TryGetState(string Id, out State State)
Tries to get a state.
Duration? ArchiveRequired
Duration after which token expires, the token is required to be archived.
void IndexElements()
Indexes all elements in the state-machine.
async Task<(CurrentState, EvaluationArguments)> CreateCurrentState(Token Token, LegalComponent Legal, EDalerComponent EDaler, Profiler Profiler)
Starts processing of the state-machine.
static async Task GoToState(string StateId, EvaluationArguments Arguments)
Goes to a new state.
CaseInsensitiveString CreatorTokenId
ID of token that created the state-machine.
async Task Start(EvaluationArguments Arguments)
Starts the processing of the state-machine.
string ObjectId
Object ID of state machine.
CaseInsensitiveString StateMachineId
ID of State Machine.
void CheckReferences(Token Token)
Indexes all elements in the state-machine.
static async Task EvaluationComplete(EvaluationArguments Arguments)
Method called when current evaluation has been completed, and new states need to be persisted.
Duration? ArchiveOptional
Duration after which token expires, and the required archiving time, the token can optionally be arch...
DateTime Expires
When state-machine expires
static async Task CheckConditionalEvents(EvaluationArguments Arguments)
Checks conditional events.
static async Task UnregisterEventHandlers(OnEvent[] Events, EvaluationArguments Arguments)
Unregisters event handlers for the current state.
const string StateMachineDefinition
Local name of state-machine definition
static Task< StateMachine > Parse(Token Token)
Parses the XML representation of a State Machine.
static Task< StateMachine > Parse(string NormalizedXml, XmlDocument Xml, Token Token)
Parses the XML representation of a State Machine.
static async Task< Variables > GetHistoryVariables(string MachineId)
Gets history variables for a state-machine.
static async Task< StateMachine > Parse(string Xml, Token Token)
Parses the XML representation of a State Machine.
const string StateMachineNamespace
https://paiwise.tagroot.io/Schema/StateMachines.xsd
static async Task< StateMachine > Parse(string NormalizedXml, XmlElement Xml, Token Token)
Parses the XML representation of a State Machine.
Class representing a sample of a state machine variable over time.
DateTime Timestamp
Timestamp of sample
string StateMachineId
ID of State-Machine.
string Variable
ID of State-Machine.
Basic interface for a user.
Interface for State-Machine nodes
string LocalName
Local name
IStateMachineNode[] ChildNodes
Child nodes, if available. Null if no children.
TimeUnit
Options for presenting time in reports.
ProfilerThreadType
Type of profiler thread.
SecondaryNameType
What type of secondary name is begin used.
ReportFormat
Desired report format
Represents a duration value, as defined by the xsd:duration data type: http://www....