Dreamine.PLC.Core
1.0.1
Dreamine.PLC.Core 산업 자동화 및 I/O 기능을 제공합니다.
Toggle main menu visibility
로딩중...
검색중...
일치하는것 없음
PlcSimulatorServer.cs
이 파일의 문서화 페이지로 가기
1
using
System.Net;
2
using
System.Net.Sockets;
3
using
Dreamine.PLC.Core.Devices
;
4
using
Dreamine.PLC.Core.Memory
;
5
6
namespace
Dreamine.PLC.Core.Simulation
;
7
16
public
sealed
class
PlcSimulatorServer
: IAsyncDisposable
17
{
26
private
readonly
PlcSimulatorServerOptions
_options
;
35
private
readonly
InMemoryPlcMemory
_memory
;
44
private
readonly
DefaultPlcAddressParser
_addressParser
=
new
();
53
private
readonly List<Task>
_clientTasks
= [];
62
private
readonly
object
_syncRoot
=
new
();
71
private
CancellationTokenSource?
_cts
;
80
private
TcpListener?
_listener
;
89
private
Task?
_acceptTask
;
90
115
public
PlcSimulatorServer
(
PlcSimulatorServerOptions
options)
116
: this(options, new
InMemoryPlcMemory
())
117
{
118
}
119
151
public
PlcSimulatorServer
(
PlcSimulatorServerOptions
options,
InMemoryPlcMemory
memory)
152
{
153
_options
= options ??
throw
new
ArgumentNullException(nameof(options));
154
_memory
= memory ??
throw
new
ArgumentNullException(nameof(memory));
155
}
156
165
public
event
EventHandler<string>?
StatusChanged
;
166
175
public
bool
IsRunning
=>
_listener
is not
null
;
176
200
public
Task
StartAsync
(CancellationToken cancellationToken =
default
)
201
{
202
if
(
_listener
is not
null
)
203
{
204
return
Task.CompletedTask;
205
}
206
207
var address =
ParseAddress
(
_options
.Host);
208
_listener
=
new
TcpListener(address,
_options
.Port);
209
_listener
.Start();
210
_cts
= CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
211
_acceptTask
= Task.Run(() =>
AcceptLoopAsync
(
_cts
.Token), CancellationToken.None);
212
StatusChanged
?.Invoke(
this
, $
"PLC simulator server started. {_options.Host}:{_options.Port}"
);
213
return
Task.CompletedTask;
214
}
215
232
public
async Task
StopAsync
()
233
{
234
if
(
_listener
is
null
)
235
{
236
return
;
237
}
238
239
_cts
?.Cancel();
240
_listener
.Stop();
241
_listener
=
null
;
242
243
if
(
_acceptTask
is not
null
)
244
{
245
try
246
{
247
await
_acceptTask
.ConfigureAwait(
false
);
248
}
249
catch
(OperationCanceledException)
250
{
251
}
252
catch
(ObjectDisposedException)
253
{
254
}
255
}
256
257
Task[] clientTasks;
258
lock (
_syncRoot
)
259
{
260
clientTasks =
_clientTasks
.ToArray();
261
_clientTasks
.Clear();
262
}
263
264
try
265
{
266
await Task.WhenAll(clientTasks).ConfigureAwait(
false
);
267
}
268
catch
269
{
270
}
271
272
_cts
?.Dispose();
273
_cts
=
null
;
274
StatusChanged
?.Invoke(
this
,
"PLC simulator server stopped."
);
275
}
276
292
public
async ValueTask
DisposeAsync
()
293
{
294
await
StopAsync
().ConfigureAwait(
false
);
295
}
296
319
private
async Task
AcceptLoopAsync
(CancellationToken cancellationToken)
320
{
321
while
(!cancellationToken.IsCancellationRequested &&
_listener
is not
null
)
322
{
323
TcpClient client;
324
325
try
326
{
327
client = await
_listener
.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(
false
);
328
}
329
catch
(OperationCanceledException)
330
{
331
break
;
332
}
333
catch
(ObjectDisposedException)
334
{
335
break
;
336
}
337
338
var task = Task.Run(() =>
HandleClientAsync
(client, cancellationToken), CancellationToken.None);
339
lock (
_syncRoot
)
340
{
341
_clientTasks
.RemoveAll(
static
item => item.IsCompleted);
342
_clientTasks
.Add(task);
343
}
344
}
345
}
346
376
private
async Task
HandleClientAsync
(TcpClient client, CancellationToken cancellationToken)
377
{
378
using
(client)
379
using
(var stream = client.GetStream())
380
using
(var reader =
new
StreamReader(stream))
381
using
(var writer =
new
StreamWriter(stream) { AutoFlush =
true
})
382
{
383
StatusChanged
?.Invoke(
this
,
"PLC simulator client connected."
);
384
385
while
(!cancellationToken.IsCancellationRequested)
386
{
387
var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(
false
);
388
if
(line is
null
)
389
{
390
break
;
391
}
392
393
var response =
ExecuteLine
(line);
394
await writer.WriteLineAsync(response.AsMemory(), cancellationToken).ConfigureAwait(
false
);
395
}
396
}
397
398
StatusChanged
?.Invoke(
this
,
"PLC simulator client disconnected."
);
399
}
400
423
private
string
ExecuteLine
(
string
line)
424
{
425
try
426
{
427
var parts = line.Split(
' '
, 3, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
428
if
(parts.Length < 3)
429
{
430
return
PlcSimulatorProtocol
.
Error
(
"Invalid command format."
);
431
}
432
433
var command = parts[0].ToUpperInvariant();
434
var addressResult =
_addressParser
.Parse(parts[1]);
435
if
(!addressResult.IsSuccess)
436
{
437
return
PlcSimulatorProtocol
.
Error
(addressResult.Message ??
"Invalid PLC address."
);
438
}
439
440
var address = addressResult.Value;
441
var argument = parts[2];
442
443
return
command
switch
444
{
445
PlcSimulatorProtocol.ReadBits
=>
ExecuteReadBits
(address, argument),
446
PlcSimulatorProtocol.ReadWords
=>
ExecuteReadWords
(address, argument),
447
PlcSimulatorProtocol.WriteBits
=>
ExecuteWriteBits
(address, argument),
448
PlcSimulatorProtocol.WriteWords
=>
ExecuteWriteWords
(address, argument),
449
_ =>
PlcSimulatorProtocol
.
Error
($
"Unsupported command: {command}"
)
450
};
451
}
452
catch
(Exception ex)
453
{
454
return
PlcSimulatorProtocol
.
Error
(ex.Message);
455
}
456
}
457
487
private
string
ExecuteReadBits
(Abstractions.Devices.PlcAddress address,
string
argument)
488
{
489
if
(!
TryParseCount
(argument, out var count, out var error))
490
{
491
return
PlcSimulatorProtocol
.
Error
(error);
492
}
493
494
var result =
_memory
.ReadBits(address, count);
495
return
result.IsSuccess && result.Value is not
null
496
?
PlcSimulatorProtocol
.
Ok
(
PlcSimulatorProtocol
.
FormatBits
(result.Value))
497
:
PlcSimulatorProtocol
.
Error
(result.Message ??
"Read bits failed."
);
498
}
499
529
private
string
ExecuteReadWords
(Abstractions.Devices.PlcAddress address,
string
argument)
530
{
531
if
(!
TryParseCount
(argument, out var count, out var error))
532
{
533
return
PlcSimulatorProtocol
.
Error
(error);
534
}
535
536
var result =
_memory
.ReadWords(address, count);
537
return
result.IsSuccess && result.Value is not
null
538
?
PlcSimulatorProtocol
.
Ok
(
PlcSimulatorProtocol
.
FormatWords
(result.Value))
539
:
PlcSimulatorProtocol
.
Error
(result.Message ??
"Read words failed."
);
540
}
541
578
private
static
bool
TryParseCount
(
string
text, out
int
count, out
string
error)
579
{
580
if
(!
int
.TryParse(text, out count))
581
{
582
error = $
"Invalid read count: {text}"
;
583
return
false
;
584
}
585
586
if
(count <= 0)
587
{
588
error =
"Read count must be greater than zero."
;
589
return
false
;
590
}
591
592
error =
string
.Empty;
593
return
true
;
594
}
595
625
private
string
ExecuteWriteBits
(Abstractions.Devices.PlcAddress address,
string
argument)
626
{
627
var values =
PlcSimulatorProtocol
.
ParseBits
(argument);
628
var result =
_memory
.WriteBits(address, values);
629
return
result.IsSuccess ?
PlcSimulatorProtocol
.
Ok
() :
PlcSimulatorProtocol
.
Error
(result.Message ??
"Write bits failed."
);
630
}
631
661
private
string
ExecuteWriteWords
(Abstractions.Devices.PlcAddress address,
string
argument)
662
{
663
var values =
PlcSimulatorProtocol
.
ParseWords
(argument);
664
var result =
_memory
.WriteWords(address, values);
665
if
(!result.IsSuccess)
666
{
667
return
PlcSimulatorProtocol
.
Error
(result.Message ??
"Write words failed."
);
668
}
669
670
ApplyAutoWordResponse
(address, values);
671
return
PlcSimulatorProtocol
.
Ok
();
672
}
673
696
private
void
ApplyAutoWordResponse
(Abstractions.Devices.PlcAddress writtenAddress, IReadOnlyList<short> values)
697
{
698
if
(!
_options
.EnableAutoWordResponse || values.Count != 1)
699
{
700
return
;
701
}
702
703
var triggerResult =
_addressParser
.Parse(
_options
.AutoResponseTriggerAddress);
704
var responseResult =
_addressParser
.Parse(
_options
.AutoResponseAddress);
705
if
(!triggerResult.IsSuccess || !responseResult.IsSuccess)
706
{
707
return
;
708
}
709
710
var triggerAddress = triggerResult.Value;
711
if
(writtenAddress.DeviceType != triggerAddress.DeviceType || writtenAddress.Offset != triggerAddress.Offset)
712
{
713
return
;
714
}
715
716
var rawResponseValue = values[0] +
_options
.AutoResponseIncrement;
717
if
(rawResponseValue is < short.MinValue or >
short
.MaxValue)
718
{
719
StatusChanged
?.Invoke(
this
, $
"Auto response skipped: value overflow. {_options.AutoResponseAddress}={rawResponseValue}"
);
720
return
;
721
}
722
723
var responseValue = (short)rawResponseValue;
724
_memory
.WriteWords(responseResult.Value, [responseValue]);
725
StatusChanged
?.Invoke(
this
, $
"Auto response: {_options.AutoResponseAddress}={responseValue}"
);
726
}
727
750
private
static
IPAddress
ParseAddress
(
string
host)
751
{
752
if
(
string
.IsNullOrWhiteSpace(host) || host ==
"*"
|| host ==
"+"
)
753
{
754
return
IPAddress.Any;
755
}
756
757
return
IPAddress.TryParse(host, out var address) ? address : IPAddress.Any;
758
}
759
}
Dreamine.PLC.Core.Devices
Definition
DefaultPlcAddressParser.cs:5
Dreamine.PLC.Core.Memory
Definition
InMemoryPlcMemory.cs:4
Dreamine.PLC.Core.Simulation
Definition
PlcSimulatorClientOptions.cs:1
Dreamine.PLC.Core.Devices.DefaultPlcAddressParser
Definition
DefaultPlcAddressParser.cs:16
Dreamine.PLC.Core.Memory.InMemoryPlcMemory
Definition
InMemoryPlcMemory.cs:15
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol
Definition
PlcSimulatorProtocol.cs:14
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.FormatWords
static string FormatWords(IEnumerable< short > values)
Definition
PlcSimulatorProtocol.cs:166
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.ReadBits
const string ReadBits
Definition
PlcSimulatorProtocol.cs:23
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.ParseBits
static bool[] ParseBits(string payload)
Definition
PlcSimulatorProtocol.cs:203
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.FormatBits
static string FormatBits(IEnumerable< bool > values)
Definition
PlcSimulatorProtocol.cs:137
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.WriteWords
const string WriteWords
Definition
PlcSimulatorProtocol.cs:53
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.Ok
static string Ok(string? payload=null)
Definition
PlcSimulatorProtocol.cs:79
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.WriteBits
const string WriteBits
Definition
PlcSimulatorProtocol.cs:43
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.ParseWords
static short[] ParseWords(string payload)
Definition
PlcSimulatorProtocol.cs:250
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.ReadWords
const string ReadWords
Definition
PlcSimulatorProtocol.cs:33
Dreamine.PLC.Core.Simulation.PlcSimulatorProtocol.Error
static string Error(string message)
Definition
PlcSimulatorProtocol.cs:108
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._syncRoot
readonly object _syncRoot
Definition
PlcSimulatorServer.cs:62
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.StartAsync
Task StartAsync(CancellationToken cancellationToken=default)
Definition
PlcSimulatorServer.cs:200
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ApplyAutoWordResponse
void ApplyAutoWordResponse(Abstractions.Devices.PlcAddress writtenAddress, IReadOnlyList< short > values)
Definition
PlcSimulatorServer.cs:696
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.PlcSimulatorServer
PlcSimulatorServer(PlcSimulatorServerOptions options, InMemoryPlcMemory memory)
Definition
PlcSimulatorServer.cs:151
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.StopAsync
async Task StopAsync()
Definition
PlcSimulatorServer.cs:232
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ExecuteWriteWords
string ExecuteWriteWords(Abstractions.Devices.PlcAddress address, string argument)
Definition
PlcSimulatorServer.cs:661
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.IsRunning
bool IsRunning
Definition
PlcSimulatorServer.cs:175
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._acceptTask
Task? _acceptTask
Definition
PlcSimulatorServer.cs:89
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.AcceptLoopAsync
async Task AcceptLoopAsync(CancellationToken cancellationToken)
Definition
PlcSimulatorServer.cs:319
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._clientTasks
readonly List< Task > _clientTasks
Definition
PlcSimulatorServer.cs:53
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.TryParseCount
static bool TryParseCount(string text, out int count, out string error)
Definition
PlcSimulatorServer.cs:578
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._memory
readonly InMemoryPlcMemory _memory
Definition
PlcSimulatorServer.cs:35
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._listener
TcpListener? _listener
Definition
PlcSimulatorServer.cs:80
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._cts
CancellationTokenSource? _cts
Definition
PlcSimulatorServer.cs:71
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.StatusChanged
EventHandler< string >? StatusChanged
Definition
PlcSimulatorServer.cs:165
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ExecuteReadBits
string ExecuteReadBits(Abstractions.Devices.PlcAddress address, string argument)
Definition
PlcSimulatorServer.cs:487
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.PlcSimulatorServer
PlcSimulatorServer(PlcSimulatorServerOptions options)
Definition
PlcSimulatorServer.cs:115
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.HandleClientAsync
async Task HandleClientAsync(TcpClient client, CancellationToken cancellationToken)
Definition
PlcSimulatorServer.cs:376
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ExecuteWriteBits
string ExecuteWriteBits(Abstractions.Devices.PlcAddress address, string argument)
Definition
PlcSimulatorServer.cs:625
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ParseAddress
static IPAddress ParseAddress(string host)
Definition
PlcSimulatorServer.cs:750
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._addressParser
readonly DefaultPlcAddressParser _addressParser
Definition
PlcSimulatorServer.cs:44
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ExecuteReadWords
string ExecuteReadWords(Abstractions.Devices.PlcAddress address, string argument)
Definition
PlcSimulatorServer.cs:529
Dreamine.PLC.Core.Simulation.PlcSimulatorServer._options
readonly PlcSimulatorServerOptions _options
Definition
PlcSimulatorServer.cs:26
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.DisposeAsync
async ValueTask DisposeAsync()
Definition
PlcSimulatorServer.cs:292
Dreamine.PLC.Core.Simulation.PlcSimulatorServer.ExecuteLine
string ExecuteLine(string line)
Definition
PlcSimulatorServer.cs:423
Dreamine.PLC.Core.Simulation.PlcSimulatorServerOptions
Definition
PlcSimulatorServerOptions.cs:12
Simulation
PlcSimulatorServer.cs
다음에 의해 생성됨 :
1.17.0