Skip to content

Commit

Permalink
Merge pull request #15 from mycroes/request-timeout
Browse files Browse the repository at this point in the history
Request timeout
  • Loading branch information
mycroes committed May 15, 2023
2 parents 6a0c326 + 8fd542a commit c255a98
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 89 deletions.
97 changes: 75 additions & 22 deletions src/AdsClient/AdsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Ads.Client.CommandResponse;
using Ads.Client.Commands;
using Ads.Client.Common;
using Ads.Client.Conversation;
Expand All @@ -13,6 +14,7 @@
using Ads.Client.Conversation.ReadUploadInfo;
using Ads.Client.Conversation.WriteMultiple;
using Ads.Client.Helpers;
using Ads.Client.Internal;
using Ads.Client.Special;
using Ads.Client.Variables;

Expand Down Expand Up @@ -68,6 +70,34 @@ public AdsClient(IAdsConnectionSettings settings)
this.Name = settings.Name;
}

/// <summary>
/// The default timeout (5 seconds) for performing requests.
/// Set the actual value using <see cref="RequestTimeout"/>.
/// </summary>
public static TimeSpan DefaultRequestTimeout => TimeSpan.FromSeconds(5);

private TimeSpan requestTimeout = DefaultRequestTimeout;

/// <summary>
/// Gets or sets the timeout for performing requests.
/// </summary>
/// <remarks>
/// The default value is <see cref="DefaultRequestTimeout"/>.
/// </remarks>
/// <exception cref="ArgumentOutOfRangeException">
/// <paramref name="value"/>.TotalMilliseconds is less than -1 or greater than maximum allowed timer duration.
/// </exception>
public TimeSpan RequestTimeout
{
get => requestTimeout;
set
{
Assertions.AssertTimeoutIsValid(value);

requestTimeout = value;
}
}

/// <summary>
/// Controls the default multi read result factory. The result type of <see cref="ReadVariablesAsync"/> is
/// affected by the factory, ensure results are properly disposed if required by the factory implementation.
Expand Down Expand Up @@ -163,7 +193,7 @@ public async Task<uint> GetSymhandleByNameAsync(string varName, CancellationToke

// It was not retrieved before - get it from the control.
var adsCommand = new AdsWriteReadCommand(0x0000F003, 0x00000000, varName.ToAdsBytes(), 4);
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
if (result == null || result.Data == null || result.Data.Length < 4)
return 0;

Expand Down Expand Up @@ -210,7 +240,7 @@ private Task ReleaseSymhandleAsyncInternal(uint symhandle, CancellationToken can
{
// Run the release command.
var adsCommand = new AdsWriteCommand(0x0000F006, 0x00000000, BitConverter.GetBytes(symhandle));
return adsCommand.RunAsync(this.ams, cancellationToken);
return RunCommandAsync(adsCommand, cancellationToken);
}

/// <summary>
Expand All @@ -223,21 +253,21 @@ private Task ReleaseSymhandleAsyncInternal(uint symhandle, CancellationToken can
public async Task<byte[]> ReadBytesAsync(uint varHandle, uint readLength, CancellationToken cancellationToken = default)
{
AdsReadCommand adsCommand = new AdsReadCommand(0x0000F005, varHandle, readLength);
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
return result.Data;
}

public async Task<byte[]> ReadBytesI_Async(uint offset, uint readLength, CancellationToken cancellationToken = default)
{
AdsReadCommand adsCommand = new AdsReadCommand(0x0000F020, offset, readLength);
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
return result.Data;
}

public async Task<byte[]> ReadBytesQ_Async(uint offset, uint readLength, CancellationToken cancellationToken = default)
{
AdsReadCommand adsCommand = new AdsReadCommand(0x0000F030, offset, readLength);
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
return result.Data;
}

Expand Down Expand Up @@ -296,7 +326,7 @@ public async Task<byte[]> ReadBytesQ_Async(uint offset, uint readLength, Cancell
adsCommand.CycleTime = cycleTime;
adsCommand.UserData = userData;
adsCommand.TypeOfValue = typeOfValue;
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
adsCommand.Notification.NotificationHandle = result.NotificationHandle;
return result.NotificationHandle; ;
}
Expand Down Expand Up @@ -327,7 +357,7 @@ public async Task<byte[]> ReadBytesQ_Async(uint offset, uint readLength, Cancell
public Task DeleteNotificationAsync(uint notificationHandle, CancellationToken cancellationToken = default)
{
var adsCommand = new AdsDeleteDeviceNotificationCommand(notificationHandle);
return adsCommand.RunAsync(this.ams, cancellationToken);
return RunCommandAsync(adsCommand, cancellationToken);
}

/// <summary>
Expand All @@ -339,7 +369,7 @@ public Task DeleteNotificationAsync(uint notificationHandle, CancellationToken c
public Task WriteBytesAsync(uint varHandle, IEnumerable<byte> varValue, CancellationToken cancellationToken = default)
{
AdsWriteCommand adsCommand = new AdsWriteCommand(0x0000F005, varHandle, varValue);
return adsCommand.RunAsync(this.ams, cancellationToken);
return RunCommandAsync(adsCommand, cancellationToken);
}

/// <summary>
Expand All @@ -363,7 +393,7 @@ public Task WriteAsync<T>(uint varHandle, T varValue, CancellationToken cancella
public async Task<AdsDeviceInfo> ReadDeviceInfoAsync(CancellationToken cancellationToken = default)
{
AdsReadDeviceInfoCommand adsCommand = new AdsReadDeviceInfoCommand();
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
return result.AdsDeviceInfo;
}

Expand All @@ -375,7 +405,7 @@ public async Task<AdsDeviceInfo> ReadDeviceInfoAsync(CancellationToken cancellat
public async Task<AdsState> ReadStateAsync(CancellationToken cancellationToken = default)
{
var adsCommand = new AdsReadStateCommand();
var result = await adsCommand.RunAsync(this.ams, cancellationToken);
var result = await RunCommandAsync(adsCommand, cancellationToken);
return result.AdsState;
}

Expand Down Expand Up @@ -412,10 +442,10 @@ public async Task ReleaseActiveSymhandlesAsync(CancellationToken cancellationTok
/// </returns>
public async Task<List<AdsSymbol>> GetSymbolsAsync(CancellationToken cancellationToken = default)
{
var uploadInfo = await ams.PerformRequestAsync(new AdsReadUploadInfoConversation(), cancellationToken)
var uploadInfo = await PerformRequestAsync(new AdsReadUploadInfoConversation(), cancellationToken)
.ConfigureAwait(false);

var symbols = await ams.PerformRequestAsync(new AdsReadSymbolsConversation(uploadInfo), cancellationToken)
var symbols = await PerformRequestAsync(new AdsReadSymbolsConversation(uploadInfo), cancellationToken)
.ConfigureAwait(false);

return symbols;
Expand All @@ -431,10 +461,10 @@ public async Task<List<AdsSymbol>> GetSymbolsAsync(CancellationToken cancellatio
/// </returns>
public async Task<List<AdsDataTypeDto>> GetDataTypesAsync(CancellationToken cancellationToken = default)
{
var uploadInfo = await ams.PerformRequestAsync(new AdsReadUploadInfoConversation(), cancellationToken)
var uploadInfo = await PerformRequestAsync(new AdsReadUploadInfoConversation(), cancellationToken)
.ConfigureAwait(false);

var types = await ams.PerformRequestAsync(new AdsReadDataTypesConversation(uploadInfo), cancellationToken)
var types = await PerformRequestAsync(new AdsReadDataTypesConversation(uploadInfo), cancellationToken)
.ConfigureAwait(false);

return types;
Expand All @@ -449,12 +479,10 @@ public async Task<List<AdsDataTypeDto>> GetDataTypesAsync(CancellationToken canc
/// A task that represents the asynchronous operation.
/// The task result contains an array of the variable handles as <see cref="uint"/>.
/// </returns>
public async Task<uint[]> CreateVariableHandlesAsync(string[] variableNames,
public Task<uint[]> CreateVariableHandlesAsync(string[] variableNames,
CancellationToken cancellationToken = default)
{
return await ams
.PerformRequestAsync(new AdsCreateVariableHandlesConversation(variableNames), cancellationToken)
.ConfigureAwait(false);
return PerformRequestAsync(new AdsCreateVariableHandlesConversation(variableNames), cancellationToken);
}

/// <summary>
Expand Down Expand Up @@ -487,11 +515,11 @@ public async Task<List<AdsDataTypeDto>> GetDataTypesAsync(CancellationToken canc
/// A task that represents the asynchronous operation.
/// The task result contains the result generated by the <paramref name="resultFactory"/>.
/// </returns>
public async Task<TResult> ReadVariablesAsync<TResult>(IVariableAddressAndSize[] variables,
public Task<TResult> ReadVariablesAsync<TResult>(IVariableAddressAndSize[] variables,
IReadResultFactory<TResult> resultFactory, CancellationToken cancellationToken = default)
{
return await ams.PerformRequestAsync(new AdsReadMultipleConversation<TResult>(variables, resultFactory),
cancellationToken).ConfigureAwait(false);
return PerformRequestAsync(new AdsReadMultipleConversation<TResult>(variables, resultFactory),
cancellationToken);
}

/// <summary>
Expand All @@ -504,9 +532,34 @@ public async Task<List<AdsDataTypeDto>> GetDataTypesAsync(CancellationToken canc
/// </returns>
public async Task WriteVariablesAsync(IVariableData[] variables, CancellationToken cancellationToken = default)
{
await ams.PerformRequestAsync(new AdsWriteMultipleConversation(variables), cancellationToken)
await PerformRequestAsync(new AdsWriteMultipleConversation(variables), cancellationToken)
.ConfigureAwait(false);
}
#endregion

private CancellationTokenSource CreateRequestTimeoutCancellationTokenSource(CancellationToken userToken)
{
var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(userToken);
linkedCts.CancelAfter(RequestTimeout);

return linkedCts;
}

private async Task<TResult> PerformRequestAsync<TRequest, TResult>(
IAdsConversation<TRequest, TResult> conversation, CancellationToken cancellationToken) where TRequest : struct, IAdsRequest
{
using var linkedCts = CreateRequestTimeoutCancellationTokenSource(cancellationToken);

return await ams.PerformRequestAsync(conversation, linkedCts.Token).ConfigureAwait(false);
}

private async Task<TResponse> RunCommandAsync<TResponse>(AdsCommand<TResponse> command,
CancellationToken cancellationToken) where TResponse : AdsCommandResponse, new()
{
using var linkedCts = CreateRequestTimeoutCancellationTokenSource(cancellationToken);

return await command.RunAsync(ams, linkedCts.Token).ConfigureAwait(false);
}

}
}
9 changes: 1 addition & 8 deletions src/AdsClient/Commands/AdsAddDeviceNotificationCommand.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Ads.Client.CommandResponse;
using Ads.Client.Common;

namespace Ads.Client.Commands
{
public class AdsAddDeviceNotificationCommand : AdsCommand
public class AdsAddDeviceNotificationCommand : AdsCommand<AdsAddDeviceNotificationCommandResponse>
{
public AdsAddDeviceNotificationCommand(uint indexGroup, uint indexOffset, uint readLength, AdsTransmissionMode transmissionMode)
: base(AdsCommandId.AddDeviceNotification)
Expand Down Expand Up @@ -70,10 +68,5 @@ protected override void RunBefore(Ams ams)
{
ams.NotificationRequests.Add(Notification);
}

public Task<AdsAddDeviceNotificationCommandResponse> RunAsync(Ams ams, CancellationToken cancellationToken)
{
return RunAsync<AdsAddDeviceNotificationCommandResponse>(ams, cancellationToken);
}
}
}
17 changes: 8 additions & 9 deletions src/AdsClient/Commands/AdsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace Ads.Client.Commands
{
public abstract class AdsCommand
public abstract class AdsCommand<TResponse> where TResponse : AdsCommandResponse, new()
{
public AdsCommand(ushort commandId)
{
Expand All @@ -33,10 +33,10 @@ protected virtual void RunAfter(Ams ams)
{
}

protected async Task<T> RunAsync<T>(Ams ams, CancellationToken cancellationToken) where T : AdsCommandResponse, new()
public async Task<TResponse> RunAsync(Ams ams, CancellationToken cancellationToken)
{
RunBefore(ams);
var result = await ams.PerformRequestAsync(new AdsCommandConversation<T>(this),
var result = await ams.PerformRequestAsync(new AdsCommandConversation(this),
AmsPortTargetOverride ?? ams.AmsPortTarget, cancellationToken).ConfigureAwait(false);
RunAfter(ams);

Expand All @@ -47,10 +47,10 @@ protected async Task<T> RunAsync<T>(Ams ams, CancellationToken cancellationToken

private readonly struct AdsCommandRequest : IAdsRequest
{
private readonly AdsCommand command;
private readonly AdsCommand<TResponse> command;
private readonly Lazy<IEnumerable<byte>> data;

public AdsCommandRequest(AdsCommand command)
public AdsCommandRequest(AdsCommand<TResponse> command)
{
this.command = command;
data = new Lazy<IEnumerable<byte>>(command.GetBytes);
Expand All @@ -77,12 +77,11 @@ public int BuildRequest(Span<byte> span)
}
}

private class AdsCommandConversation<TResponse> : IAdsConversation<AdsCommandRequest, TResponse>
where TResponse : AdsCommandResponse, new()
private class AdsCommandConversation : IAdsConversation<AdsCommandRequest, TResponse>
{
public AdsCommand Command { get; }
public AdsCommand<TResponse> Command { get; }

public AdsCommandConversation(AdsCommand command)
public AdsCommandConversation(AdsCommand<TResponse> command)
{
Command = command;
}
Expand Down
9 changes: 1 addition & 8 deletions src/AdsClient/Commands/AdsDeleteDeviceNotificationCommand.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Ads.Client.CommandResponse;
using Ads.Client.Common;

namespace Ads.Client.Commands
{
public class AdsDeleteDeviceNotificationCommand : AdsCommand
public class AdsDeleteDeviceNotificationCommand : AdsCommand<AdsDeleteDeviceNotificationCommandResponse>
{
public AdsDeleteDeviceNotificationCommand(uint notificationHandle)
: base(AdsCommandId.DeleteDeviceNotification)
Expand All @@ -31,10 +29,5 @@ protected override void RunAfter(Ams ams)
if (notification != null)
ams.NotificationRequests.Remove(notification);
}

public Task<AdsDeleteDeviceNotificationCommandResponse> RunAsync(Ams ams, CancellationToken cancellationToken)
{
return RunAsync<AdsDeleteDeviceNotificationCommandResponse>(ams, cancellationToken);
}
}
}
9 changes: 1 addition & 8 deletions src/AdsClient/Commands/AdsReadCommand.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Ads.Client.CommandResponse;
using Ads.Client.Common;

namespace Ads.Client.Commands
{
internal class AdsReadCommand : AdsCommand
internal class AdsReadCommand : AdsCommand<AdsReadCommandResponse>
{
public AdsReadCommand(uint indexGroup, uint indexOffset, uint readLength)
: base(AdsCommandId.Read)
Expand All @@ -30,10 +28,5 @@ internal override IEnumerable<byte> GetBytes()

return data;
}

public Task<AdsReadCommandResponse> RunAsync(Ams ams, CancellationToken cancellationToken)
{
return RunAsync<AdsReadCommandResponse>(ams, cancellationToken);
}
}
}
9 changes: 1 addition & 8 deletions src/AdsClient/Commands/AdsReadDeviceInfoCommand.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Ads.Client.CommandResponse;
using Ads.Client.Common;

namespace Ads.Client.Commands
{
public class AdsReadDeviceInfoCommand : AdsCommand
public class AdsReadDeviceInfoCommand : AdsCommand<AdsReadDeviceInfoCommandResponse>
{

public AdsReadDeviceInfoCommand()
Expand All @@ -19,10 +17,5 @@ internal override IEnumerable<byte> GetBytes()
{
return new List<byte>();
}

public Task<AdsReadDeviceInfoCommandResponse> RunAsync(Ams ams, CancellationToken cancellationToken)
{
return RunAsync<AdsReadDeviceInfoCommandResponse>(ams, cancellationToken);
}
}
}

0 comments on commit c255a98

Please sign in to comment.