SampleSmart 1.0.0.0
SampleSmart 사용 방법을 보여 주는 예제 프로젝트입니다.
로딩중...
검색중...
일치하는것 없음
PlcSampleRuntime.cs
이 파일의 문서화 페이지로 가기
1using Dreamine.PLC.Abstractions.Clients;
2using Dreamine.PLC.Abstractions.Connections;
3using Dreamine.PLC.Core.Clients;
4using Dreamine.PLC.Core.Devices;
5using Dreamine.PLC.Core.Simulation;
6using Dreamine.PLC.Mitsubishi.MC.Clients;
7using Dreamine.PLC.Mitsubishi.MC.Options;
8using Dreamine.PLC.Mitsubishi.MC.Simulation;
9using Dreamine.PLC.Mitsubishi.MxComponent.Clients;
10using Dreamine.PLC.Mitsubishi.MxComponent.Options;
11using Dreamine.PLC.Omron.CxComponent.Clients;
12using Dreamine.PLC.Omron.CxComponent.Options;
13using Dreamine.PLC.Omron.Fins.Clients;
14using Dreamine.PLC.Omron.Fins.Options;
15using Dreamine.PLC.Omron.Fins.Simulation;
16using Dreamine.PLC.Wpf.ViewModels;
17
19
28public sealed class PlcSampleRuntime
29{
38 private readonly InMemoryPlcClient _inMemoryClient = new();
47 private readonly DefaultPlcAddressParser _addressParser = new();
56 private IPlcClient _activeClient;
65 private PlcSimulatorServer? _simulatorServer;
74 private MitsubishiMcTcpSimulatorServer? _mcTcpSimulatorServer;
83 private MitsubishiMcUdpSimulatorServer? _mcUdpSimulatorServer;
92 private OmronFinsTcpSimulatorServer? _finsTcpSimulatorServer;
101 private OmronFinsUdpSimulatorServer? _finsUdpSimulatorServer;
110 private PlcSimulatorTcpClient? _simulatorClient;
119 private MitsubishiMcPlcClient? _mitsubishiMcClient;
128 private MitsubishiMxComponentPlcClient? _mitsubishiMxComponentClient;
137 private OmronFinsPlcClient? _omronFinsClient;
146 private OmronCxComponentPlcClient? _omronCxComponentClient;
155 private string _activeServerMode = string.Empty;
156
166 {
168 Monitor = new PlcMonitorViewModel(_inMemoryClient, "InMemory PLC");
169 }
170
179 public PlcMonitorViewModel Monitor { get; }
180
189 public string ServerStatus { get; private set; } = "Server stopped.";
190
199 public void UseInMemoryClient()
200 {
202 Monitor.SetClient(_inMemoryClient, "InMemory PLC");
203 Monitor.StatusMessage = "InMemory PLC client selected.";
204 }
205
230 public void UseSimulatorTcpClient(string host, int port)
231 {
232 _simulatorClient = new PlcSimulatorTcpClient(new PlcSimulatorClientOptions
233 {
234 Host = host,
235 Port = port
236 });
237
239 Monitor.SetClient(_simulatorClient, $"PLC Simulator TCP Client ({host}:{port})");
240 Monitor.StatusMessage = "PLC Simulator TCP client selected.";
241 }
242
243
284 public void UseMitsubishiMcClient(string host, int port, string transportText, int retryCount)
285 {
286 if (!Enum.TryParse<MitsubishiMcTransportType>(transportText, ignoreCase: true, out var transportType))
287 {
288 Monitor.StatusMessage = "MC transport must be Tcp or Udp.";
289 return;
290 }
291
292 _mitsubishiMcClient = new MitsubishiMcPlcClient(new MitsubishiMcConnectionOptions
293 {
294 Host = host,
295 Port = port,
296 TransportType = transportType,
297 RetryCount = Math.Max(1, retryCount),
298 ConnectTimeoutMs = 3000,
299 ReceiveTimeoutMs = 3000,
300 SendTimeoutMs = 3000
301 });
302
304 Monitor.SetClient(_mitsubishiMcClient, $"Mitsubishi MC {transportType} ({host}:{port})");
305 Monitor.StatusMessage = $"Mitsubishi MC {transportType} client selected.";
306 }
307
308
349 public void UseOmronFinsClient(string host, int port, string transportText, int retryCount)
350 {
351 if (!Enum.TryParse<OmronFinsTransportType>(transportText, ignoreCase: true, out var transportType))
352 {
353 Monitor.StatusMessage = "FINS transport must be Tcp or Udp.";
354 return;
355 }
356
357 _omronFinsClient = new OmronFinsPlcClient(new OmronFinsConnectionOptions
358 {
359 Host = host,
360 Port = port,
361 TransportType = transportType,
362 RetryCount = Math.Max(1, retryCount),
363 ConnectTimeoutMs = 3000,
364 ReceiveTimeoutMs = 3000,
365 DestinationNode = 0x00,
366 SourceNode = 0x01
367 });
368
370 Monitor.SetClient(_omronFinsClient, $"Omron FINS {transportType} ({host}:{port})");
371 Monitor.StatusMessage = $"Omron FINS {transportType} client selected.";
372 }
373
398 public void UseMitsubishiMxComponentClient(string progId, int logicalStationNumber)
399 {
400 _mitsubishiMxComponentClient = new MitsubishiMxComponentPlcClient(new MitsubishiMxComponentOptions
401 {
402 ProgId = string.IsNullOrWhiteSpace(progId) ? MitsubishiMxComponentOptions.DefaultProgId : progId.Trim(),
403 LogicalStationNumber = logicalStationNumber
404 });
405
407 Monitor.SetClient(_mitsubishiMxComponentClient, $"Mitsubishi MX Component LS={logicalStationNumber}");
408 Monitor.StatusMessage = "Mitsubishi MX Component client selected. Vendor runtime must be installed before Connect.";
409 }
410
435 public void UseOmronCxComponentClient(string progId, string peerAddress)
436 {
437 _omronCxComponentClient = new OmronCxComponentPlcClient(new OmronCxComponentOptions
438 {
439 ProgId = string.IsNullOrWhiteSpace(progId) ? "OMRON.Compolet.CJ2Compolet" : progId.Trim(),
440 PeerAddress = string.IsNullOrWhiteSpace(peerAddress) ? "127.0.0.1" : peerAddress.Trim()
441 });
442
444 Monitor.SetClient(_omronCxComponentClient, $"Omron CX-Compolet ({peerAddress})");
445 Monitor.StatusMessage = "Omron CX-Compolet client selected. Vendor runtime must be installed before Connect.";
446 }
447
488 public async Task StartProtocolServerAsync(string modeText, string host, int port)
489 {
490 var mode = NormalizeMode(modeText);
491 if (IsAnyServerRunning())
492 {
493 ServerStatus = $"Server already running. mode={_activeServerMode}. Stop the current server first.";
494 Monitor.StatusMessage = ServerStatus;
495 return;
496 }
497
498 switch (mode)
499 {
500 case "SIMULATORTCP":
501 _simulatorServer = new PlcSimulatorServer(new PlcSimulatorServerOptions
502 {
503 Host = host,
504 Port = port,
505 EnableAutoWordResponse = true,
506 AutoResponseTriggerAddress = "D100",
507 AutoResponseAddress = "D101",
508 AutoResponseIncrement = 1
509 });
510
511 _simulatorServer.StatusChanged += OnSimulatorServerStatusChanged;
512 await _simulatorServer.StartAsync();
513 _activeServerMode = "SimulatorTcp";
514 break;
515
516 case "MCTCP":
517 _mcTcpSimulatorServer = new MitsubishiMcTcpSimulatorServer(CreateMcSimulatorOptions(host, port));
518 _mcTcpSimulatorServer.StatusChanged += OnSimulatorServerStatusChanged;
519 await _mcTcpSimulatorServer.StartAsync();
520 _activeServerMode = "McTcp";
521 break;
522
523 case "MCUDP":
524 _mcUdpSimulatorServer = new MitsubishiMcUdpSimulatorServer(CreateMcSimulatorOptions(host, port));
525 _mcUdpSimulatorServer.StatusChanged += OnSimulatorServerStatusChanged;
526 await _mcUdpSimulatorServer.StartAsync();
527 _activeServerMode = "McUdp";
528 break;
529
530 case "FINSTCP":
531 _finsTcpSimulatorServer = new OmronFinsTcpSimulatorServer(CreateFinsSimulatorOptions(host, port));
532 _finsTcpSimulatorServer.StatusChanged += OnSimulatorServerStatusChanged;
533 await _finsTcpSimulatorServer.StartAsync();
534 _activeServerMode = "FinsTcp";
535 break;
536
537 case "FINSUDP":
538 _finsUdpSimulatorServer = new OmronFinsUdpSimulatorServer(CreateFinsSimulatorOptions(host, port));
539 _finsUdpSimulatorServer.StatusChanged += OnSimulatorServerStatusChanged;
540 await _finsUdpSimulatorServer.StartAsync();
541 _activeServerMode = "FinsUdp";
542 break;
543
544 default:
545 Monitor.StatusMessage = "Server mode must be SimulatorTcp, McTcp, McUdp, FinsTcp, or FinsUdp.";
546 return;
547 }
548
549 ServerStatus = $"{_activeServerMode} server running. {host}:{port}";
550 Monitor.StatusMessage = ServerStatus;
551 }
552
569 public async Task StopProtocolServerAsync()
570 {
571 if (!IsAnyServerRunning())
572 {
573 ServerStatus = "Server stopped.";
574 Monitor.StatusMessage = ServerStatus;
575 return;
576 }
577
578 if (_simulatorServer is not null)
579 {
580 _simulatorServer.StatusChanged -= OnSimulatorServerStatusChanged;
581 await _simulatorServer.StopAsync();
582 _simulatorServer = null;
583 }
584
585 if (_mcTcpSimulatorServer is not null)
586 {
587 _mcTcpSimulatorServer.StatusChanged -= OnSimulatorServerStatusChanged;
588 await _mcTcpSimulatorServer.StopAsync();
590 }
591
592 if (_mcUdpSimulatorServer is not null)
593 {
594 _mcUdpSimulatorServer.StatusChanged -= OnSimulatorServerStatusChanged;
595 await _mcUdpSimulatorServer.StopAsync();
597 }
598
599 if (_finsTcpSimulatorServer is not null)
600 {
601 _finsTcpSimulatorServer.StatusChanged -= OnSimulatorServerStatusChanged;
602 await _finsTcpSimulatorServer.StopAsync();
604 }
605
606 if (_finsUdpSimulatorServer is not null)
607 {
608 _finsUdpSimulatorServer.StatusChanged -= OnSimulatorServerStatusChanged;
609 await _finsUdpSimulatorServer.StopAsync();
611 }
612
613 _activeServerMode = string.Empty;
614 ServerStatus = "Server stopped.";
615 Monitor.StatusMessage = ServerStatus;
616 }
617
650 public Task StartSimulatorServerAsync(string host, int port)
651 {
652 return StartProtocolServerAsync("SimulatorTcp", host, port);
653 }
654
672 {
674 }
675
724 public async Task RunHandshakeTestAsync(
725 short startValue,
726 int iterations,
727 int delayMs,
728 CancellationToken cancellationToken = default)
729 {
730 if (iterations <= 0)
731 {
732 Monitor.StatusMessage = "Handshake iterations must be greater than zero.";
733 Monitor.AppendLog("Handshake", "D100/D101", iterations.ToString(), false, Monitor.StatusMessage);
734 return;
735 }
736
737 if (_activeClient.State != PlcConnectionState.Connected)
738 {
739 Monitor.StatusMessage = "Handshake test requires a connected PLC client.";
740 Monitor.AppendLog("Handshake", "D100/D101", string.Empty, false, Monitor.StatusMessage);
741 return;
742 }
743
744 var clientWriteAddress = _addressParser.Parse("D100").Value;
745 var serverResponseAddress = _addressParser.Parse("D101").Value;
746 var value = startValue;
747
748 for (var index = 0; index < iterations; index++)
749 {
750 cancellationToken.ThrowIfCancellationRequested();
751
752 var writeResult = await _activeClient.WriteWordsAsync(clientWriteAddress, [value], cancellationToken).ConfigureAwait(true);
753 if (!writeResult.IsSuccess)
754 {
755 Monitor.StatusMessage = writeResult.Message ?? "Handshake write failed.";
756 Monitor.AppendLog("HandshakeWrite", "D100", value.ToString(), false, Monitor.StatusMessage);
757 return;
758 }
759
760 var rawExpected = value + 1;
761 if (rawExpected > short.MaxValue)
762 {
763 Monitor.StatusMessage = $"Handshake stopped before 16-bit overflow. last={value}, next={rawExpected}.";
764 Monitor.AppendLog("Handshake", "D100/D101", $"last={value}, next={rawExpected}", false, Monitor.StatusMessage);
765 return;
766 }
767
768 var expected = (short)rawExpected;
769 var readResult = await _activeClient.ReadWordsAsync(serverResponseAddress, 1, cancellationToken).ConfigureAwait(true);
770 var actual = readResult.Value is { Length: > 0 } ? readResult.Value[0] : short.MinValue;
771 if (!readResult.IsSuccess || actual != expected)
772 {
773 Monitor.StatusMessage = readResult.IsSuccess
774 ? $"Handshake mismatch. expected={expected}, actual={actual}."
775 : readResult.Message ?? "Handshake read failed.";
776 Monitor.AppendLog("HandshakeRead", "D101", $"expected={expected}, actual={actual}", false, Monitor.StatusMessage);
777 return;
778 }
779
780 Monitor.StatusMessage = $"Handshake OK {index + 1}/{iterations}. D100={value}, D101={actual}";
781 Monitor.AppendLog("Handshake", "D100/D101", $"{value}->{actual}", true, Monitor.StatusMessage);
782 value = actual;
783
784 if (delayMs > 0)
785 {
786 await Task.Delay(delayMs, cancellationToken).ConfigureAwait(true);
787 }
788 }
789 }
790
791
824 private static MitsubishiMcSimulatorServerOptions CreateMcSimulatorOptions(string host, int port)
825 {
826 return new MitsubishiMcSimulatorServerOptions
827 {
828 Host = host,
829 Port = port,
830 EnableAutoWordResponse = true,
831 AutoResponseTriggerDeviceCode = 0xA8,
832 AutoResponseTriggerOffset = 100,
833 AutoResponseDeviceCode = 0xA8,
834 AutoResponseOffset = 101,
835 AutoResponseIncrement = 1
836 };
837 }
838
871 private static OmronFinsSimulatorServerOptions CreateFinsSimulatorOptions(string host, int port)
872 {
873 return new OmronFinsSimulatorServerOptions
874 {
875 Host = host,
876 Port = port,
877 EnableAutoWordResponse = true,
878 AutoResponseTriggerOffset = 100,
879 AutoResponseOffset = 101,
880 AutoResponseIncrement = 1
881 };
882 }
883
900 private bool IsAnyServerRunning()
901 {
902 return _simulatorServer is not null
903 || _mcTcpSimulatorServer is not null
904 || _mcUdpSimulatorServer is not null
905 || _finsTcpSimulatorServer is not null
906 || _finsUdpSimulatorServer is not null;
907 }
908
933 private static string NormalizeMode(string? modeText)
934 {
935 return string.IsNullOrWhiteSpace(modeText)
936 ? "SIMULATORTCP"
937 : modeText.Trim().ToUpperInvariant();
938 }
939
964 private void OnSimulatorServerStatusChanged(object? sender, string e)
965 {
966 ServerStatus = e;
967 Monitor.StatusMessage = e;
968 }
969}
MitsubishiMcUdpSimulatorServer? _mcUdpSimulatorServer
static MitsubishiMcSimulatorServerOptions CreateMcSimulatorOptions(string host, int port)
async Task StartProtocolServerAsync(string modeText, string host, int port)
MitsubishiMcTcpSimulatorServer? _mcTcpSimulatorServer
void UseMitsubishiMcClient(string host, int port, string transportText, int retryCount)
void UseOmronCxComponentClient(string progId, string peerAddress)
void UseMitsubishiMxComponentClient(string progId, int logicalStationNumber)
MitsubishiMxComponentPlcClient? _mitsubishiMxComponentClient
void UseOmronFinsClient(string host, int port, string transportText, int retryCount)
static OmronFinsSimulatorServerOptions CreateFinsSimulatorOptions(string host, int port)
void OnSimulatorServerStatusChanged(object? sender, string e)
readonly DefaultPlcAddressParser _addressParser
async Task RunHandshakeTestAsync(short startValue, int iterations, int delayMs, CancellationToken cancellationToken=default)