Skip to content

Commit 4ace4a0

Browse files
committed
Improved Synchronization of Channels
1 parent 45e4e2f commit 4ace4a0

File tree

4 files changed

+242
-95
lines changed

4 files changed

+242
-95
lines changed

RICADO.Omron/Channels/EthernetChannel.cs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,14 @@ internal abstract class EthernetChannel : IDisposable
1515

1616
private byte _requestId = 0;
1717

18-
private SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
18+
private readonly SemaphoreSlim _semaphore;
19+
20+
#endregion
21+
22+
23+
#region Protected Properties
24+
25+
protected SemaphoreSlim Semaphore => _semaphore;
1926

2027
#endregion
2128

@@ -47,14 +54,19 @@ internal EthernetChannel(string remoteHost, int port)
4754
{
4855
_remoteHost = remoteHost;
4956
_port = port;
57+
58+
_semaphore = new SemaphoreSlim(1, 1);
5059
}
5160

5261
#endregion
5362

5463

5564
#region Public Methods
5665

57-
public abstract void Dispose();
66+
public virtual void Dispose()
67+
{
68+
_semaphore?.Dispose();
69+
}
5870

5971
#endregion
6072

@@ -77,7 +89,10 @@ internal async Task<ProcessRequestResult> ProcessRequestAsync(FINSRequest reques
7789
{
7890
try
7991
{
80-
await _semaphore.WaitAsync(cancellationToken);
92+
if (!_semaphore.Wait(0))
93+
{
94+
await _semaphore.WaitAsync(cancellationToken);
95+
}
8196

8297
if (attempts > 0)
8398
{
@@ -102,7 +117,7 @@ internal async Task<ProcessRequestResult> ProcessRequestAsync(FINSRequest reques
102117

103118
break;
104119
}
105-
catch (OmronException)
120+
catch (Exception)
106121
{
107122
if(attempts >= retries)
108123
{

RICADO.Omron/Channels/EthernetTCPChannel.cs

Lines changed: 74 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,12 @@ internal EthernetTCPChannel(string remoteHost, int port) : base(remoteHost, port
6161

6262
public override void Dispose()
6363
{
64-
if (_client == null)
64+
try
6565
{
66-
return;
66+
_client?.Dispose();
6767
}
68-
69-
try
68+
catch
7069
{
71-
_client.Dispose();
7270
}
7371
finally
7472
{
@@ -81,9 +79,23 @@ public override void Dispose()
8179

8280
#region Internal Methods
8381

84-
internal override Task InitializeAsync(int timeout, CancellationToken cancellationToken)
82+
internal override async Task InitializeAsync(int timeout, CancellationToken cancellationToken)
8583
{
86-
return initializeClient(timeout, cancellationToken);
84+
try
85+
{
86+
if(!Semaphore.Wait(0))
87+
{
88+
await Semaphore.WaitAsync(cancellationToken);
89+
}
90+
91+
destroyClient();
92+
93+
await initializeClient(timeout, cancellationToken);
94+
}
95+
finally
96+
{
97+
Semaphore.Release();
98+
}
8799
}
88100

89101
#endregion
@@ -93,26 +105,23 @@ internal override Task InitializeAsync(int timeout, CancellationToken cancellati
93105

94106
protected override async Task DestroyAndInitializeClient(int timeout, CancellationToken cancellationToken)
95107
{
96-
try
97-
{
98-
_client?.Dispose();
99-
}
100-
finally
101-
{
102-
_client = null;
103-
}
108+
destroyClient();
104109

105110
try
106111
{
107112
await initializeClient(timeout, cancellationToken);
108113
}
114+
catch (ObjectDisposedException)
115+
{
116+
throw new OmronException("Failed to Re-Connect to Omron PLC '" + RemoteHost + ":" + Port + "' - The underlying Socket Connection was Closed");
117+
}
109118
catch (TimeoutException)
110119
{
111-
throw new OmronException("Failed to Re-Connect within the Timeout Period to Omron PLC '" + base.RemoteHost + ":" + base.Port + "'");
120+
throw new OmronException("Failed to Re-Connect within the Timeout Period to Omron PLC '" + RemoteHost + ":" + Port + "'");
112121
}
113122
catch (System.Net.Sockets.SocketException e)
114123
{
115-
throw new OmronException("Failed to Re-Connect to Omron PLC '" + base.RemoteHost + ":" + base.Port + "'", e);
124+
throw new OmronException("Failed to Re-Connect to Omron PLC '" + RemoteHost + ":" + Port + "'", e);
116125
}
117126
}
118127

@@ -133,12 +142,7 @@ protected override Task<ReceiveMessageResult> ReceiveMessageAsync(int timeout, C
133142

134143
private async Task initializeClient(int timeout, CancellationToken cancellationToken)
135144
{
136-
if (_client != null)
137-
{
138-
return;
139-
}
140-
141-
_client = new TcpClient(base.RemoteHost, base.Port);
145+
_client = new TcpClient(RemoteHost, Port);
142146

143147
await _client.ConnectAsync(timeout, cancellationToken);
144148

@@ -152,28 +156,40 @@ private async Task initializeClient(int timeout, CancellationToken cancellationT
152156

153157
if(receiveResult.Message.Length < 8)
154158
{
155-
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - TCP Negotiation Message Length was too Short");
159+
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + RemoteHost + ":" + Port + "' - TCP Negotiation Message Length was too Short");
156160
}
157161

158162
byte[] tcpNegotiationMessage = receiveResult.Message.Slice(0, 8).ToArray();
159163

160164
if(tcpNegotiationMessage[3] == 0 || tcpNegotiationMessage[3] == 255)
161165
{
162-
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - TCP Negotiation Message contained an Invalid Local Node ID");
166+
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + RemoteHost + ":" + Port + "' - TCP Negotiation Message contained an Invalid Local Node ID");
163167
}
164168

165169
_localNodeId = tcpNegotiationMessage[3];
166170

167171
if (tcpNegotiationMessage[7] == 0 || tcpNegotiationMessage[7] == 255)
168172
{
169-
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - TCP Negotiation Message contained an Invalid Remote Node ID");
173+
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + RemoteHost + ":" + Port + "' - TCP Negotiation Message contained an Invalid Remote Node ID");
170174
}
171175

172176
_remoteNodeId = tcpNegotiationMessage[7];
173177
}
174178
catch (OmronException e)
175179
{
176-
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + base.RemoteHost + ":" + base.Port + "'", e);
180+
throw new OmronException("Failed to Negotiate a TCP Connection with Omron PLC '" + RemoteHost + ":" + Port + "'", e);
181+
}
182+
}
183+
184+
private void destroyClient()
185+
{
186+
try
187+
{
188+
_client?.Dispose();
189+
}
190+
finally
191+
{
192+
_client = null;
177193
}
178194
}
179195

@@ -192,13 +208,17 @@ private async Task<SendMessageResult> sendMessageAsync(enTCPCommandCode command,
192208
result.Bytes += await _client.SendAsync(tcpMessage, timeout, cancellationToken);
193209
result.Packets += 1;
194210
}
211+
catch (ObjectDisposedException)
212+
{
213+
throw new OmronException("Failed to Send FINS Message to Omron PLC '" + RemoteHost + ":" + Port + "' - The underlying Socket Connection was Closed");
214+
}
195215
catch (TimeoutException)
196216
{
197-
throw new OmronException("Failed to Send FINS Message within the Timeout Period to Omron PLC '" + base.RemoteHost + ":" + base.Port + "'");
217+
throw new OmronException("Failed to Send FINS Message within the Timeout Period to Omron PLC '" + RemoteHost + ":" + Port + "'");
198218
}
199219
catch (System.Net.Sockets.SocketException e)
200220
{
201-
throw new OmronException("Failed to Send FINS Message to Omron PLC '" + base.RemoteHost + ":" + base.Port + "'", e);
221+
throw new OmronException("Failed to Send FINS Message to Omron PLC '" + RemoteHost + ":" + Port + "'", e);
202222
}
203223

204224
return result;
@@ -239,17 +259,17 @@ private async Task<ReceiveMessageResult> receiveMessageAsync(enTCPCommandCode co
239259

240260
if (receivedData.Count == 0)
241261
{
242-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - No Data was Received");
262+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - No Data was Received");
243263
}
244264

245265
if (receivedData.Count < TCP_HEADER_LENGTH)
246266
{
247-
throw new OmronException("Failed to Receive FINS Message within the Timeout Period from Omron PLC '" + base.RemoteHost + ":" + base.Port + "'");
267+
throw new OmronException("Failed to Receive FINS Message within the Timeout Period from Omron PLC '" + RemoteHost + ":" + Port + "'");
248268
}
249269

250270
if (receivedData[0] != 'F' || receivedData[1] != 'I' || receivedData[2] != 'N' || receivedData[3] != 'S')
251271
{
252-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - The TCP Header was Invalid");
272+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - The TCP Header was Invalid");
253273
}
254274

255275
byte[] tcpHeader = receivedData.GetRange(0, TCP_HEADER_LENGTH).ToArray();
@@ -258,53 +278,53 @@ private async Task<ReceiveMessageResult> receiveMessageAsync(enTCPCommandCode co
258278

259279
if(tcpMessageDataLength <= 0 || tcpMessageDataLength > short.MaxValue)
260280
{
261-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - The TCP Message Length was Invalid");
281+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - The TCP Message Length was Invalid");
262282
}
263283

264284
if(receivedData[11] == 3 || receivedData[15] != 0)
265285
{
266286
switch(receivedData[15])
267287
{
268288
case 1:
269-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: The FINS Identifier (ASCII Code) was Invalid.");
289+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: The FINS Identifier (ASCII Code) was Invalid.");
270290

271291
case 2:
272-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: The Data Length is too Long.");
292+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: The Data Length is too Long.");
273293

274294
case 3:
275-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: The Command is not Supported.");
295+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: The Command is not Supported.");
276296

277297
case 20:
278-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: All Connections are in Use.");
298+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: All Connections are in Use.");
279299

280300
case 21:
281-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: The Specified Node is already Connected.");
301+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: The Specified Node is already Connected.");
282302

283303
case 22:
284-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: Attempt to Access a Protected Node from an Unspecified IP Address.");
304+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: Attempt to Access a Protected Node from an Unspecified IP Address.");
285305

286306
case 23:
287-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: The Client FINS Node Address is out of Range.");
307+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: The Client FINS Node Address is out of Range.");
288308

289309
case 24:
290-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: The same FINS Node Address is being used by the Client and Server.");
310+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: The same FINS Node Address is being used by the Client and Server.");
291311

292312
case 25:
293-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: All the Node Addresses Available for Allocation have been Used.");
313+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: All the Node Addresses Available for Allocation have been Used.");
294314

295315
default:
296-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - Omron TCP Error: Unknown Code '" + receivedData[15] + "'");
316+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - Omron TCP Error: Unknown Code '" + receivedData[15] + "'");
297317
}
298318
}
299319

300320
if(receivedData[8] != 0 || receivedData[9] != 0 || receivedData[10] != 0 || receivedData[11] != (byte)command)
301321
{
302-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - The TCP Command Received '" + receivedData[11] + "' did not match Expected Command '" + (byte)command + "'");
322+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - The TCP Command Received '" + receivedData[11] + "' did not match Expected Command '" + (byte)command + "'");
303323
}
304324

305325
if(command == enTCPCommandCode.FINSFrame && tcpMessageDataLength < FINSResponse.HEADER_LENGTH + FINSResponse.COMMAND_LENGTH + FINSResponse.RESPONSE_CODE_LENGTH)
306326
{
307-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - The TCP Message Length was too short for a FINS Frame");
327+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - The TCP Message Length was too short for a FINS Frame");
308328
}
309329

310330
receivedData.RemoveRange(0, TCP_HEADER_LENGTH);
@@ -335,28 +355,32 @@ private async Task<ReceiveMessageResult> receiveMessageAsync(enTCPCommandCode co
335355

336356
if (receivedData.Count == 0)
337357
{
338-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - No Data was Received after TCP Header");
358+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - No Data was Received after TCP Header");
339359
}
340360

341361
if (receivedData.Count < tcpMessageDataLength)
342362
{
343-
throw new OmronException("Failed to Receive FINS Message within the Timeout Period from Omron PLC '" + base.RemoteHost + ":" + base.Port + "'");
363+
throw new OmronException("Failed to Receive FINS Message within the Timeout Period from Omron PLC '" + RemoteHost + ":" + Port + "'");
344364
}
345365

346366
if (command == enTCPCommandCode.FINSFrame && receivedData[0] != 0xC0 && receivedData[0] != 0xC1)
347367
{
348-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "' - The FINS Header was Invalid");
368+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - The FINS Header was Invalid");
349369
}
350370

351371
result.Message = receivedData.ToArray();
352372
}
373+
catch (ObjectDisposedException)
374+
{
375+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "' - The underlying Socket Connection was Closed");
376+
}
353377
catch (TimeoutException)
354378
{
355-
throw new OmronException("Failed to Receive FINS Message within the Timeout Period from Omron PLC '" + base.RemoteHost + ":" + base.Port + "'");
379+
throw new OmronException("Failed to Receive FINS Message within the Timeout Period from Omron PLC '" + RemoteHost + ":" + Port + "'");
356380
}
357381
catch (System.Net.Sockets.SocketException e)
358382
{
359-
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + base.RemoteHost + ":" + base.Port + "'", e);
383+
throw new OmronException("Failed to Receive FINS Message from Omron PLC '" + RemoteHost + ":" + Port + "'", e);
360384
}
361385

362386
return result;

0 commit comments

Comments
 (0)