Changed pipe names to end with ".pipe".
Added timeouts to closing.
This commit is contained in:
parent
526e657f59
commit
78e9e185c2
@ -18,27 +18,28 @@ namespace GameServiceWarden.Core.Games
|
||||
{
|
||||
public class ServiceDescriptor //entity
|
||||
{
|
||||
private const string DISTRIBUTOR_SUFFIX = "_dist";
|
||||
private const int INIT_TIMEOUT = 1000;
|
||||
private const string LOG_DISTRIBUTOR_PREFIX = "log_dist_";
|
||||
private const int TIMEOUT = 1000;
|
||||
/// <summary>
|
||||
/// The name of the service itself, independent of the name of the module this service is using.
|
||||
/// </summary>
|
||||
public string ServiceName { get { return serviceName; } }
|
||||
private readonly string serviceName;
|
||||
private bool running;
|
||||
private readonly Guid runningUID;
|
||||
private volatile bool running;
|
||||
private readonly IService service;
|
||||
/// <summary>
|
||||
/// The services log output pipe name.
|
||||
/// </summary>
|
||||
public string ServiceLogPipeName { get { return (serviceName); } }
|
||||
public string ServiceLogPipeName { get { return (runningUID.ToString() + ".pipe"); } }
|
||||
private string moduleName;
|
||||
private readonly string assemblyName;
|
||||
private NamedPipeServerStream logReceiver;
|
||||
private NamedPipeClientStream logSender;
|
||||
private volatile NamedPipeServerStream logReceiver;
|
||||
private volatile NamedPipeClientStream logSender;
|
||||
private ConcurrentStack<NamedPipeServerStream> logStreamListeners;
|
||||
private Task logUpdateTask;
|
||||
private Task acceptingTask;
|
||||
private CancellationTokenSource stopToken;
|
||||
private volatile CancellationTokenSource stopToken;
|
||||
|
||||
/// <summary>
|
||||
/// Name of module this service uses.
|
||||
@ -52,6 +53,8 @@ namespace GameServiceWarden.Core.Games
|
||||
this.assemblyName = assemblyName ?? throw new ArgumentNullException("assemblyName");
|
||||
this.serviceName = serviceName ?? throw new ArgumentNullException("serviceName");
|
||||
this.service.StateChangeEvent += OnServiceStateChange;
|
||||
runningUID = Guid.NewGuid();
|
||||
|
||||
|
||||
Dictionary<string, IServiceConfigurable> tempConfigurables = new Dictionary<string, IServiceConfigurable>();
|
||||
foreach (IServiceConfigurable configurable in service.Configurables)
|
||||
@ -71,23 +74,25 @@ namespace GameServiceWarden.Core.Games
|
||||
{
|
||||
Logger.Log($"\"{ServiceName}\" is starting.");
|
||||
if (running) throw new InvalidOperationException("Service instance already running.");
|
||||
logReceiver = new NamedPipeServerStream(ServiceLogPipeName + DISTRIBUTOR_SUFFIX, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
logReceiver = new NamedPipeServerStream(LOG_DISTRIBUTOR_PREFIX + ServiceLogPipeName, PipeDirection.In, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
Task waitForConnection = logReceiver.WaitForConnectionAsync();
|
||||
logSender = new NamedPipeClientStream(".", ServiceLogPipeName + DISTRIBUTOR_SUFFIX, PipeDirection.Out);
|
||||
logSender = new NamedPipeClientStream(".", LOG_DISTRIBUTOR_PREFIX + ServiceLogPipeName, PipeDirection.Out);
|
||||
logSender.Connect();
|
||||
waitForConnection.Wait();
|
||||
byte[] idToken = Guid.NewGuid().ToByteArray();
|
||||
ValueTask sendTokenTask = logSender.WriteAsync(idToken);
|
||||
byte[] receivedToken = new byte[idToken.Length];
|
||||
logReceiver.Read(receivedToken);
|
||||
|
||||
if (!sendTokenTask.AsTask().Wait(500) || !sendTokenTask.IsCompletedSuccessfully) {
|
||||
|
||||
if (!sendTokenTask.AsTask().Wait(500) || !sendTokenTask.IsCompletedSuccessfully)
|
||||
{
|
||||
throw new ServiceInitializationException("Error while sending identification token.");
|
||||
}
|
||||
if (!idToken.SequenceEqual(receivedToken)) {
|
||||
if (!idToken.SequenceEqual(receivedToken))
|
||||
{
|
||||
throw new ServiceInitializationException("Wrong distributor identification token.");
|
||||
}
|
||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(INIT_TIMEOUT);
|
||||
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TIMEOUT);
|
||||
Task initializationTask = Task.Run(() => service.InitializeService(logSender), cancellationTokenSource.Token);
|
||||
initializationTask.Wait();
|
||||
cancellationTokenSource.Dispose();
|
||||
@ -102,18 +107,27 @@ namespace GameServiceWarden.Core.Games
|
||||
/// <exception cref="InvalidOperationException">Is thrown when the is not running.</exception>
|
||||
public void Stop()
|
||||
{
|
||||
Logger.Log("\"{ServiceName}\" is stopping.");
|
||||
if (!running) throw new InvalidOperationException("Service instance not running.");
|
||||
Logger.Log($"\"{ServiceName}\" is stopping.");
|
||||
service.ElegantShutdown();
|
||||
logSender.Close();
|
||||
logReceiver.Close();
|
||||
stopToken.Cancel();
|
||||
try {
|
||||
acceptingTask.Wait();
|
||||
} catch (AggregateException e) {
|
||||
try
|
||||
{
|
||||
if (!acceptingTask.Wait(TIMEOUT)) {
|
||||
throw new TimeoutException($"Could not stop \"{ServiceName}\" accepting task within {TIMEOUT}ms.");
|
||||
}
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
e.Handle((exception) => exception is TaskCanceledException);
|
||||
}
|
||||
try
|
||||
{
|
||||
logUpdateTask.Wait();
|
||||
if (!logUpdateTask.Wait(TIMEOUT)) {
|
||||
throw new TimeoutException($"Could not stop \"{ServiceName}\" broadcast within{TIMEOUT}ms.");
|
||||
}
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
@ -163,11 +177,13 @@ namespace GameServiceWarden.Core.Games
|
||||
return running;
|
||||
}
|
||||
|
||||
public string GetServiceName() {
|
||||
public string GetServiceName()
|
||||
{
|
||||
return serviceName;
|
||||
}
|
||||
|
||||
public string GetModuleName() {
|
||||
public string GetModuleName()
|
||||
{
|
||||
return moduleName;
|
||||
}
|
||||
|
||||
@ -180,7 +196,7 @@ namespace GameServiceWarden.Core.Games
|
||||
private void OnServiceStateChange(object sender, bool running)
|
||||
{
|
||||
this.running = running;
|
||||
Logger.Log($"{ServiceName}'s state changed to {(running ? "running" : "stopped")}.", LogLevel.DEBUG);
|
||||
Logger.Log($"The service \"{ServiceName}\" is changing states to {(running ? "running" : "stopped")}.", LogLevel.DEBUG);
|
||||
ServiceStateChangeEvent?.Invoke(this, running);
|
||||
}
|
||||
|
||||
@ -197,22 +213,26 @@ namespace GameServiceWarden.Core.Games
|
||||
Logger.Log($"\"{ServiceName}\" stopped accepting log listeners.");
|
||||
}
|
||||
|
||||
private async Task BroadcastLog() {
|
||||
private async Task BroadcastLog()
|
||||
{
|
||||
Stack<NamedPipeServerStream> completeStack = new Stack<NamedPipeServerStream>();
|
||||
Stack<(Task, CancellationTokenSource)> writeTasks = new Stack<(Task, CancellationTokenSource)>();
|
||||
byte[] buffer = new byte[1024 * 8];
|
||||
int fill;
|
||||
Logger.Log($"\"{ServiceName}\" is now listening to the service log and broadcasting.");
|
||||
while ((fill = await logReceiver.ReadAsync(buffer, 0, buffer.Length, stopToken.Token)) > 0)
|
||||
while (running && (fill = await logReceiver.ReadAsync(buffer, 0, buffer.Length, stopToken.Token)) > 0)
|
||||
{
|
||||
Logger.Log($"Broadcasting {fill} bytes.", LogLevel.DEBUG);
|
||||
NamedPipeServerStream pipe;
|
||||
while (logStreamListeners.TryPop(out pipe))
|
||||
{
|
||||
if (!pipe.IsConnected) {
|
||||
if (!pipe.IsConnected)
|
||||
{
|
||||
pipe.Dispose();
|
||||
Logger.Log($"\"{ServiceName}\" detected a disconnected log listener. Removing from list of listener(s).", LogLevel.DEBUG);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
CancellationTokenSource cancelToken = new CancellationTokenSource(1000);
|
||||
writeTasks.Push((pipe.WriteAsync(buffer, 0, fill, cancelToken.Token), cancelToken));
|
||||
completeStack.Push(pipe);
|
||||
@ -224,7 +244,8 @@ namespace GameServiceWarden.Core.Games
|
||||
logStreamListeners.Push(completePipe);
|
||||
}
|
||||
(Task, CancellationTokenSource) taskAndCancel;
|
||||
while (writeTasks.TryPop(out taskAndCancel)) {
|
||||
while (writeTasks.TryPop(out taskAndCancel))
|
||||
{
|
||||
await taskAndCancel.Item1;
|
||||
taskAndCancel.Item2.Dispose();
|
||||
}
|
||||
|
@ -204,7 +204,6 @@ namespace GameServiceWarden.Core.Games
|
||||
{
|
||||
switch (action.action)
|
||||
{
|
||||
//TODO FINISH MOVING THIS!!!!
|
||||
case ServiceManagerAction.Type.View:
|
||||
GetServiceNames();
|
||||
GetRunningServiceNames();
|
||||
|
@ -15,7 +15,8 @@ namespace GameServiceWarden.Core.UI
|
||||
{
|
||||
public class IPCMediator
|
||||
{
|
||||
private const int CONNECT_TIMEOUT = 1000;
|
||||
private const int TIMEOUT = 1000;
|
||||
public string PipeName {get { return name + ".pipe"; } }
|
||||
private readonly string name;
|
||||
private readonly ConcurrentDictionary<string, (NamedPipeServerStream, Task)> pipes;
|
||||
public BlockingCollection<(string, CommunicableType, byte[])> RequestQueue { get; private set; }
|
||||
@ -48,7 +49,7 @@ namespace GameServiceWarden.Core.UI
|
||||
stopAcceptingToken.Cancel();
|
||||
try
|
||||
{
|
||||
acceptTask.Wait();
|
||||
if (!acceptTask.Wait(TIMEOUT)) throw new TimeoutException($"IPCMediator \"{name}\" was unable to stop accepting task within {TIMEOUT}ms.");
|
||||
}
|
||||
catch (AggregateException e)
|
||||
{
|
||||
@ -109,7 +110,7 @@ namespace GameServiceWarden.Core.UI
|
||||
Logger.Log("Accepting Interface connections.");
|
||||
while (active)
|
||||
{
|
||||
NamedPipeServerStream pipe = new NamedPipeServerStream(name, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
NamedPipeServerStream pipe = new NamedPipeServerStream(PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
|
||||
Logger.Log("Waiting for connection.", LogLevel.DEBUG);
|
||||
await pipe.WaitForConnectionAsync(stopAcceptingToken.Token);
|
||||
connectionTasks.Add(OnConnection(pipe));
|
||||
@ -135,7 +136,7 @@ namespace GameServiceWarden.Core.UI
|
||||
Logger.Log("Interface attempting to connect.", LogLevel.DEBUG);
|
||||
byte[] headerBuffer = new byte[sizeof(uint) * 2];
|
||||
int headerFill = 0;
|
||||
CancellationTokenSource headerCancel = new CancellationTokenSource(CONNECT_TIMEOUT);
|
||||
CancellationTokenSource headerCancel = new CancellationTokenSource(TIMEOUT);
|
||||
try
|
||||
{
|
||||
int readLength;
|
||||
@ -148,7 +149,7 @@ namespace GameServiceWarden.Core.UI
|
||||
catch (AggregateException e)
|
||||
{
|
||||
e.Handle((exception) => exception is TaskCanceledException);
|
||||
Logger.Log($"Interface did not send header data within {CONNECT_TIMEOUT}ms.", LogLevel.DEBUG);
|
||||
Logger.Log($"Interface did not send header data within {TIMEOUT}ms.", LogLevel.DEBUG);
|
||||
} finally {
|
||||
await pipe.DisposeAsync();
|
||||
headerCancel.Dispose();
|
||||
@ -164,7 +165,7 @@ namespace GameServiceWarden.Core.UI
|
||||
|
||||
byte[] bodyBuffer = new byte[bodyLength];
|
||||
int bodyFill = 0;
|
||||
CancellationTokenSource bodyCancel = new CancellationTokenSource(CONNECT_TIMEOUT);
|
||||
CancellationTokenSource bodyCancel = new CancellationTokenSource(TIMEOUT);
|
||||
try
|
||||
{
|
||||
int readLength = 0;
|
||||
@ -177,7 +178,7 @@ namespace GameServiceWarden.Core.UI
|
||||
catch (AggregateException e)
|
||||
{
|
||||
e.Handle((exception) => exception is TaskCanceledException);
|
||||
Logger.Log($"Interface failed to send body data within {CONNECT_TIMEOUT}.", LogLevel.DEBUG);
|
||||
Logger.Log($"Interface failed to send body data within {TIMEOUT}.", LogLevel.DEBUG);
|
||||
} finally {
|
||||
await pipe.DisposeAsync();
|
||||
bodyCancel.Dispose();
|
||||
@ -204,7 +205,7 @@ namespace GameServiceWarden.Core.UI
|
||||
requestAccepted = true;
|
||||
response.identifier = request.requestedIdentifier;
|
||||
}
|
||||
CancellationTokenSource cancelResponse = new CancellationTokenSource(CONNECT_TIMEOUT);
|
||||
CancellationTokenSource cancelResponse = new CancellationTokenSource(TIMEOUT);
|
||||
try
|
||||
{
|
||||
await pipe.WriteAsync(JsonSerializer.SerializeToUtf8Bytes(response), cancelResponse.Token);
|
||||
@ -212,7 +213,7 @@ namespace GameServiceWarden.Core.UI
|
||||
catch (AggregateException e)
|
||||
{
|
||||
e.Handle((exception) => exception is TaskCanceledException);
|
||||
Logger.Log($"Interface did not receive response within {CONNECT_TIMEOUT}ms.", LogLevel.DEBUG);
|
||||
Logger.Log($"Interface did not receive response within {TIMEOUT}ms.", LogLevel.DEBUG);
|
||||
}
|
||||
if (!requestAccepted) {
|
||||
cancelResponse.Dispose();
|
||||
|
@ -5,18 +5,18 @@
|
||||
<mxCell id="0"/>
|
||||
<mxCell id="1" parent="0"/>
|
||||
<mxCell id="dmd0HlDYcxYugIlahWj0-5" value="ServiceDescriptor" style="swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;" parent="1" vertex="1">
|
||||
<mxGeometry x="695" y="1100" width="510" height="410" as="geometry">
|
||||
<mxGeometry x="695" y="1100" width="510" height="420" as="geometry">
|
||||
<mxRectangle x="762" y="1030" width="130" height="26" as="alternateBounds"/>
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="dmd0HlDYcxYugIlahWj0-6" value="+ ServiceName: string property - serviceName: string - running: bool - service: IService - ServiceLogPipeName: string property - moduleName: string - assemblyName: string - logStreamListeners: ConcurrentStack<NamedPipeServerStream> - logUpdateTask: Task - acceptingTask: Task - stopToken: CancellationTokenSource - configurables: IReadOnlyDictionary<string, IServiceConfigurable> + ServiceStateChangeEvent: event EventHandler<bool>" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="dmd0HlDYcxYugIlahWj0-5" vertex="1">
|
||||
<mxGeometry y="26" width="510" height="194" as="geometry"/>
|
||||
<mxCell id="dmd0HlDYcxYugIlahWj0-6" value="+ ServiceName: string property - serviceName: string - runningUID: Guid - running: bool - service: IService - ServiceLogPipeName: string property - moduleName: string - assemblyName: string - logStreamListeners: ConcurrentStack<NamedPipeServerStream> - logUpdateTask: Task - acceptingTask: Task - stopToken: CancellationTokenSource - configurables: IReadOnlyDictionary<string, IServiceConfigurable> + ServiceStateChangeEvent: event EventHandler<bool>" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="dmd0HlDYcxYugIlahWj0-5" vertex="1">
|
||||
<mxGeometry y="26" width="510" height="204" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dmd0HlDYcxYugIlahWj0-7" value="" style="line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;" parent="dmd0HlDYcxYugIlahWj0-5" vertex="1">
|
||||
<mxGeometry y="220" width="510" height="8" as="geometry"/>
|
||||
<mxGeometry y="230" width="510" height="8" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dmd0HlDYcxYugIlahWj0-8" value="+ Start(): void + Stop(): void + ExecuteCommand(command: string): void + GetConfigurableOptions(): ISet<string> + SetConfigurableValue(configurationName: string, value: string): bool + GetConfigurableValue(configurationName: string): string + GetServiceState(): bool + GetModuleName(): string + GetassemblyName(): string - OnServiceStateChange(sender: object, running: bool): void - AcceptLogConnections(): Task - BroadcastLog(): Task" style="text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;" parent="dmd0HlDYcxYugIlahWj0-5" vertex="1">
|
||||
<mxGeometry y="228" width="510" height="182" as="geometry"/>
|
||||
<mxGeometry y="238" width="510" height="182" as="geometry"/>
|
||||
</mxCell>
|
||||
<mxCell id="dmd0HlDYcxYugIlahWj0-15" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="1" source="dmd0HlDYcxYugIlahWj0-11" target="dmd0HlDYcxYugIlahWj0-5" edge="1">
|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||
@ -193,348 +193,348 @@
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
<diagram id="gj0qHRc3eh050ABAey3g" name="Data-Flow">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGraphModel dx="1024" dy="592" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<root>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-0"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-1" parent="jVG6p58vlRYGO9X4wXeX-0"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-21" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.3333333333333333;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;endArrow=open;endFill=0;fillColor=#1ba1e2;strokeColor=#006EAF;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-2" target="jVG6p58vlRYGO9X4wXeX-3" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-2" value="Actor" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="10" y="300" width="30" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-12" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fillColor=#1ba1e2;strokeColor=#006EAF;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-3" target="jVG6p58vlRYGO9X4wXeX-4" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-22" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.25;exitDx=0;exitDy=0;entryX=0.75;entryY=0.1;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=open;endFill=0;fillColor=#1ba1e2;strokeColor=#006EAF;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-3" target="jVG6p58vlRYGO9X4wXeX-2" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="345FJoVc2gbAayMsQlD7-0" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=1;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-3" target="jVG6p58vlRYGO9X4wXeX-4" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="345FJoVc2gbAayMsQlD7-6" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.25;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-3" target="28FAlPysTx9DMYvLwa-2-7" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-3" value="Console View" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="80" y="300" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-13" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;fillColor=#1ba1e2;strokeColor=#006EAF;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-4" target="jVG6p58vlRYGO9X4wXeX-5" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="345FJoVc2gbAayMsQlD7-2" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-4" target="jVG6p58vlRYGO9X4wXeX-5" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-4" value="string command (request)" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="260" y="482.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-8" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-5" target="jVG6p58vlRYGO9X4wXeX-7" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-3" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;endArrow=open;endFill=0;dashed=1;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-5" target="jVG6p58vlRYGO9X4wXeX-7" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-18" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#006EAF;fillColor=#1ba1e2;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-5" target="jVG6p58vlRYGO9X4wXeX-9" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-5" value="MainController" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="420" y="482.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-6" value="http://www.plainionist.net/Implementing-Clean-Architecture-Controller-Presenter/" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry y="840" width="480" height="20" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-7" value="&lt;&lt;Interface&gt;&gt;<br>ICommand" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="420" y="372.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-10" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-9" target="jVG6p58vlRYGO9X4wXeX-7" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-8" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#006EAF;fillColor=#1ba1e2;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-9" target="UY-EM7-1ECCvWtENr50b-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxPoint x="809.9999999999998" y="512.5000000000002" as="sourcePoint"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxGeometry>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="345FJoVc2gbAayMsQlD7-3" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-9" target="UY-EM7-1ECCvWtENr50b-2" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="345FJoVc2gbAayMsQlD7-4" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="jVG6p58vlRYGO9X4wXeX-9" target="UY-EM7-1ECCvWtENr50b-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="jVG6p58vlRYGO9X4wXeX-9" value="ServiceController" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="575" y="482.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-4" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;endArrow=block;endFill=1;strokeColor=none;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-1" target="UY-EM7-1ECCvWtENr50b-2" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-5" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=0;strokeColor=#f0f0f0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-1" target="UY-EM7-1ECCvWtENr50b-2" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-7" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1.008;entryY=0.625;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=open;endFill=0;strokeColor=#f0f0f0;dashed=1;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-1" target="UY-EM7-1ECCvWtENr50b-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<Array as="points">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxPoint x="960" y="338"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxPoint x="960" y="580"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</Array>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxGeometry>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-16" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=open;endFill=0;dashed=1;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-1" target="UY-EM7-1ECCvWtENr50b-11" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="345FJoVc2gbAayMsQlD7-7" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-1" target="UY-EM7-1ECCvWtENr50b-10" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<Array as="points">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxPoint x="960" y="338"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxPoint x="960" y="83"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</Array>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxGeometry>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-1" value="ServiceManager" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="800" y="307.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-9" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;endArrow=open;endFill=0;fillColor=#1ba1e2;strokeColor=#006EAF;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-5" target="tM_Gde3HH8YiZ2frBV5J-0" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-12" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endArrow=block;endFill=0;strokeColor=#f0f0f0;dashed=1;exitX=1;exitY=0.75;exitDx=0;exitDy=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-5" target="UY-EM7-1ECCvWtENr50b-11" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-13" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.75;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#f0f0f0;dashed=1;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-5" target="UY-EM7-1ECCvWtENr50b-10" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="tM_Gde3HH8YiZ2frBV5J-3" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-5" target="tM_Gde3HH8YiZ2frBV5J-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-5" value="ServicePresenter" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="575" y="122.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-8" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;endArrow=open;endFill=0;fillColor=#1ba1e2;strokeColor=#006EAF;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-7" target="jVG6p58vlRYGO9X4wXeX-3" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="tM_Gde3HH8YiZ2frBV5J-5" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-7" target="tM_Gde3HH8YiZ2frBV5J-0" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="28FAlPysTx9DMYvLwa-2-7" value="String Output" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="260" y="122.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-1" value="ServiceAction &lt;DS&gt;" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="800" y="542.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-6" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#f0f0f0;dashed=1;" parent="jVG6p58vlRYGO9X4wXeX-1" source="UY-EM7-1ECCvWtENr50b-2" target="UY-EM7-1ECCvWtENr50b-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-20" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#006EAF;fillColor=#1ba1e2;" parent="jVG6p58vlRYGO9X4wXeX-1" source="UY-EM7-1ECCvWtENr50b-1" target="28FAlPysTx9DMYvLwa-2-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-2" value="&lt;&lt;Interface&gt;&gt;<br>IServiceManipulator" style="whiteSpace=wrap;html=1;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="800" y="432.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-22" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.25;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#006EAF;fillColor=#1ba1e2;" parent="jVG6p58vlRYGO9X4wXeX-1" source="UY-EM7-1ECCvWtENr50b-10" target="28FAlPysTx9DMYvLwa-2-5" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-10" value="ServicesResult &lt;DS&gt;" style="whiteSpace=wrap;html=1;fillColor=none;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="800" y="67.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-14" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=1;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#f0f0f0;dashed=1;" parent="jVG6p58vlRYGO9X4wXeX-1" source="UY-EM7-1ECCvWtENr50b-11" target="UY-EM7-1ECCvWtENr50b-10" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-21" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.25;exitDx=0;exitDy=0;entryX=1;entryY=0.75;entryDx=0;entryDy=0;endArrow=open;endFill=0;strokeColor=#006EAF;fillColor=#1ba1e2;" parent="jVG6p58vlRYGO9X4wXeX-1" source="28FAlPysTx9DMYvLwa-2-1" target="UY-EM7-1ECCvWtENr50b-10" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-11" value="&lt;&lt;Interface&gt;&gt;<br>IServicesMonitor" style="whiteSpace=wrap;html=1;fillColor=none;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="800" y="167.5" width="120" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-70" value="" style="line;strokeWidth=2;direction=south;html=1;fillColor=none;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="220" y="50" width="10" height="570" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-71" value="" style="line;strokeWidth=2;direction=south;html=1;fillColor=none;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="760" y="50" width="10" height="570" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="UY-EM7-1ECCvWtENr50b-73" value="Page 191 (Chapter 22) of Clean Architecture" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry y="870" width="480" height="20" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="tM_Gde3HH8YiZ2frBV5J-2" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;dashed=1;endArrow=block;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="tM_Gde3HH8YiZ2frBV5J-0" target="tM_Gde3HH8YiZ2frBV5J-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="tM_Gde3HH8YiZ2frBV5J-4" value="Use" style="edgeStyle=orthogonalEdgeStyle;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;dashed=1;endArrow=open;endFill=0;" parent="jVG6p58vlRYGO9X4wXeX-1" source="tM_Gde3HH8YiZ2frBV5J-0" target="tM_Gde3HH8YiZ2frBV5J-1" edge="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry relative="1" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="tM_Gde3HH8YiZ2frBV5J-0" value="MainPresenter" style="html=1;dashed=0;whitespace=wrap;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="420" y="122.5" width="110" height="60" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxCell id="tM_Gde3HH8YiZ2frBV5J-1" value="&lt;&lt;Interface&gt;&gt;<br>IConsoleOutput" style="html=1;dashed=0;whitespace=wrap;" parent="jVG6p58vlRYGO9X4wXeX-1" vertex="1">
|
||||

 
 



|
||||

 
 
 
 





|
||||
<mxGeometry x="420" y="20" width="110" height="50" as="geometry"/>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxCell>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</root>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</mxGraphModel>
|
||||

 
 



|
||||

 
 
 
 





|
||||
</diagram>
|
||||
</mxfile>
|
@ -302,15 +302,14 @@ namespace GameServiceWarden.Core.Tests.Modules.Games
|
||||
{
|
||||
using (StreamReader reader = new StreamReader(clientStreams[i]))
|
||||
{
|
||||
CancellationTokenSource cancelToken = new CancellationTokenSource(15000);
|
||||
string message = null;
|
||||
Task task = Task.Run(() => message = reader.ReadLine(), cancelToken.Token);
|
||||
Assert.True(task.Wait(10000));
|
||||
Task clientTask = Task.Run(() => message = reader.ReadLine());
|
||||
Assert.True(clientTask.Wait(1000));
|
||||
Assert.True(COMMAND.Equals(message), $"Received message \"{message}\" when expecting \"{COMMAND}\"");
|
||||
cancelToken.Dispose();
|
||||
}
|
||||
}
|
||||
serviceManager.StopService(FAKE_SERVICE_NAME);
|
||||
Task task = Task.Run(() => serviceManager.StopService(FAKE_SERVICE_NAME));
|
||||
Assert.True(task.Wait(5000)); //TODO FIX WHY THIS IS HAPPENING!!!!!
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
Reference in New Issue
Block a user