Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 53 additions & 4 deletions libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpAssetClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2024 TrakHound Inc., All Rights Reserved.
// Copyright (c) 2026 TrakHound Inc., All Rights Reserved.
// TrakHound Inc. licenses this file to you under the MIT license.

using MTConnect.Assets;
Expand All @@ -20,12 +20,13 @@ namespace MTConnect.Clients
public class MTConnectHttpAssetClient : IMTConnectAssetClient
{
private const int DefaultTimeout = 15000;
private static readonly HttpClient _httpClient;
private static readonly HttpClient _defaultHttpClient;
private readonly HttpClient _httpClient;

static MTConnectHttpAssetClient()
{
_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromMilliseconds(DefaultTimeout);
_defaultHttpClient = new HttpClient();
_defaultHttpClient.Timeout = TimeSpan.FromMilliseconds(DefaultTimeout);
}


Expand All @@ -42,6 +43,7 @@ static MTConnectHttpAssetClient()
/// <param name="documentFormat">Gets or Sets the Document Format to return</param>
public MTConnectHttpAssetClient(string authority, string assetId, string documentFormat = MTConnect.DocumentFormat.XML)
{
_httpClient = _defaultHttpClient;
Init();
Authority = authority;
AssetId = assetId;
Expand All @@ -64,6 +66,53 @@ public MTConnectHttpAssetClient(string authority, string assetId, string documen
/// <param name="documentFormat">Gets or Sets the Document Format to return</param>
public MTConnectHttpAssetClient(string authority, long count = -1, string type = null, string device = null, string documentFormat = MTConnect.DocumentFormat.XML)
{
_httpClient = _defaultHttpClient;
Init();
Authority = authority;
Device = device;
Type = type;
Count = count;
DocumentFormat = documentFormat;
ContentType = MimeTypes.Get(documentFormat);
}

/// <summary>
/// Initializes a new instance of the MTConnectAssetClient class that is used to perform
/// an Assets request from an MTConnect Agent using the MTConnect HTTP REST Api protocol
/// </summary>
/// <param name="authority">
/// The authority portion consists of the DNS name or IP address associated with an Agent and an optional
/// TCP port number[:port] that the Agent is listening to for incoming Requests from client software applications.
/// If the port number is the default Port 80, port is not required.
/// </param>
/// <param name="assetId">The Id of the requested Asset</param>
/// <param name="documentFormat">Gets or Sets the Document Format to return</param>
public MTConnectHttpAssetClient(HttpClient httpClient, string authority, string assetId, string documentFormat = MTConnect.DocumentFormat.XML)
{
_httpClient = httpClient != null ? httpClient : _defaultHttpClient;
Init();
Authority = authority;
AssetId = assetId;
DocumentFormat = documentFormat;
ContentType = MimeTypes.Get(documentFormat);
}

/// <summary>
/// Initializes a new instance of the MTConnectAssetClient class that is used to perform
/// an Assets request from an MTConnect Agent using the MTConnect HTTP REST Api protocol
/// </summary>
/// <param name="authority">
/// The authority portion consists of the DNS name or IP address associated with an Agent and an optional
/// TCP port number[:port] that the Agent is listening to for incoming Requests from client software applications.
/// If the port number is the default Port 80, port is not required.
/// </param>
/// <param name="type">The Type of Assets to retrieve</param>
/// <param name="device">The Device to retrieve Assets for</param>
/// <param name="count">Specifies the maximum number of MTConnectAssets Response Documents returned in an MTConnectAssets Response Document</param>
/// <param name="documentFormat">Gets or Sets the Document Format to return</param>
public MTConnectHttpAssetClient(HttpClient httpClient, string authority, long count = -1, string type = null, string device = null, string documentFormat = MTConnect.DocumentFormat.XML)
{
_httpClient = httpClient != null ? httpClient : _defaultHttpClient;
Init();
Authority = authority;
Device = device;
Expand Down
75 changes: 63 additions & 12 deletions libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2024 TrakHound Inc., All Rights Reserved.
// Copyright (c) 2026 TrakHound Inc., All Rights Reserved.
// TrakHound Inc. licenses this file to you under the MIT license.

using MTConnect.Assets;
Expand All @@ -13,6 +13,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -23,6 +24,7 @@ namespace MTConnect.Clients
/// </summary>
public class MTConnectHttpClient : IMTConnectClient, IMTConnectEntityClient
{
private readonly HttpClient _httpClient;
private readonly Dictionary<string, IDevice> _devices = new Dictionary<string, IDevice>();
private readonly Dictionary<string, IComponent> _cachedComponents = new Dictionary<string, IComponent>();
private readonly Dictionary<string, IDataItem> _cachedDataItems = new Dictionary<string, IDataItem>();
Expand Down Expand Up @@ -84,6 +86,55 @@ public MTConnectHttpClient(string hostname, int port, string device = null, stri
DocumentFormat = documentFormat;
}

/// <summary>
/// Initializes a new instance of the MTConnectClient class that is used to perform
/// the full request and stream protocol from an MTConnect Agent using the MTConnect HTTP REST Api protocol
/// </summary>
/// <param name="authority">
/// The authority portion consists of the DNS name or IP address associated with an Agent and an optional
/// TCP port number[:port] that the Agent is listening to for incoming Requests from client software applications.
/// If the port number is the default Port 80, port is not required.
/// </param>
/// <param name="device">
/// If present, specifies that only the Equipment Metadata for the piece of equipment represented by the name or uuid will be published.
/// If not present, Metadata for all pieces of equipment associated with the Agent will be published.
/// </param>
/// <param name="documentFormat">Gets or Sets the Document Format to return</param>
public MTConnectHttpClient(HttpClient httpClient, string authority, string device = null, string documentFormat = MTConnect.DocumentFormat.XML)
{
_httpClient = httpClient;
Id = Guid.NewGuid().ToString();
Init();
Authority = authority;
Device = device;
DocumentFormat = documentFormat;
}

/// <summary>
/// Initializes a new instance of the MTConnectClient class that is used to perform
/// the full request and stream protocol from an MTConnect Agent using the MTConnect HTTP REST Api protocol
/// </summary>
/// <param name="hostname">
/// The Hostname of the MTConnect Agent
/// </param>
/// <param name="port">
/// The Port of the MTConnect Agent
/// </param>
/// <param name="device">
/// If present, specifies that only the Equipment Metadata for the piece of equipment represented by the name or uuid will be published.
/// If not present, Metadata for all pieces of equipment associated with the Agent will be published.
/// </param>
/// <param name="documentFormat">Gets or Sets the Document Format to return</param>
public MTConnectHttpClient(HttpClient httpClient, string hostname, int port, string device = null, string documentFormat = MTConnect.DocumentFormat.XML)
{
_httpClient = httpClient;
Id = Guid.NewGuid().ToString();
Init();
Authority = CreateUrl(hostname, port);
Device = device;
DocumentFormat = documentFormat;
}

private void Init()
{
Interval = 500;
Expand Down Expand Up @@ -490,7 +541,7 @@ public void Reset()
/// </summary>
public IDevicesResponseDocument GetProbe()
{
var client = new MTConnectHttpProbeClient(Authority, Device, DocumentFormat);
var client = new MTConnectHttpProbeClient(_httpClient, Authority, Device, DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -514,7 +565,7 @@ public async Task<IDevicesResponseDocument> GetProbeAsync()
/// </summary>
public async Task<IDevicesResponseDocument> GetProbeAsync(CancellationToken cancellationToken)
{
var client = new MTConnectHttpProbeClient(Authority, Device, DocumentFormat);
var client = new MTConnectHttpProbeClient(_httpClient, Authority, Device, DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -531,7 +582,7 @@ public async Task<IDevicesResponseDocument> GetProbeAsync(CancellationToken canc
/// </summary>
public IStreamsResponseDocument GetCurrent(long at = 0, string path = null)
{
var client = new MTConnectHttpCurrentClient(Authority, Device, at: at, path: path, documentFormat: DocumentFormat);
var client = new MTConnectHttpCurrentClient(_httpClient, Authority, Device, at: at, path: path, documentFormat: DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -555,7 +606,7 @@ public async Task<IStreamsResponseDocument> GetCurrentAsync(long at = 0, string
/// </summary>
public async Task<IStreamsResponseDocument> GetCurrentAsync(CancellationToken cancellationToken, long at = 0, string path = null)
{
var client = new MTConnectHttpCurrentClient(Authority, Device, at: at, path: path, documentFormat: DocumentFormat);
var client = new MTConnectHttpCurrentClient(_httpClient, Authority, Device, at: at, path: path, documentFormat: DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -572,7 +623,7 @@ public async Task<IStreamsResponseDocument> GetCurrentAsync(CancellationToken ca
/// </summary>
public IStreamsResponseDocument GetSample(long from = 0, long to = 0, int count = 0, string path = null)
{
var client = new MTConnectHttpSampleClient(Authority, Device, from: from, to: to, count: count, path: path, documentFormat: DocumentFormat);
var client = new MTConnectHttpSampleClient(_httpClient, Authority, Device, from: from, to: to, count: count, path: path, documentFormat: DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -596,7 +647,7 @@ public async Task<IStreamsResponseDocument> GetSampleAsync(long from = 0, long t
/// </summary>
public async Task<IStreamsResponseDocument> GetSampleAsync(CancellationToken cancellationToken, long from = 0, long to = 0, int count = 0, string path = null)
{
var client = new MTConnectHttpSampleClient(Authority, Device, from: from, to: to, count: count, path: path, documentFormat: DocumentFormat);
var client = new MTConnectHttpSampleClient(_httpClient, Authority, Device, from: from, to: to, count: count, path: path, documentFormat: DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -613,7 +664,7 @@ public async Task<IStreamsResponseDocument> GetSampleAsync(CancellationToken can
/// </summary>
public IAssetsResponseDocument GetAssets(long count = 100)
{
var client = new MTConnectHttpAssetClient(Authority, count, null, null, DocumentFormat);
var client = new MTConnectHttpAssetClient(_httpClient, Authority, count, null, null, DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -637,7 +688,7 @@ public async Task<IAssetsResponseDocument> GetAssetsAsync(ulong count = 100)
/// </summary>
public async Task<IAssetsResponseDocument> GetAssetsAsync(CancellationToken cancellationToken, ulong count = 100)
{
var client = new MTConnectHttpAssetClient(Authority, (long)count, null, null, DocumentFormat);
var client = new MTConnectHttpAssetClient(_httpClient, Authority, (long)count, null, null, DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -654,7 +705,7 @@ public async Task<IAssetsResponseDocument> GetAssetsAsync(CancellationToken canc
/// </summary>
public IAssetsResponseDocument GetAsset(string assetId)
{
var client = new MTConnectHttpAssetClient(Authority, assetId, DocumentFormat);
var client = new MTConnectHttpAssetClient(_httpClient, Authority, assetId, DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand All @@ -678,7 +729,7 @@ public async Task<IAssetsResponseDocument> GetAssetAsync(string assetId)
/// </summary>
public async Task<IAssetsResponseDocument> GetAssetAsync(string assetId, CancellationToken cancellationToken)
{
var client = new MTConnectHttpAssetClient(Authority, assetId, DocumentFormat);
var client = new MTConnectHttpAssetClient(_httpClient, Authority, assetId, DocumentFormat);
client.Timeout = Timeout;
client.ContentEncodings = ContentEncodings;
client.ContentType = ContentType;
Expand Down Expand Up @@ -772,7 +823,7 @@ private async Task Worker()
if (CurrentOnly) url = CreateCurrentUrl(Authority, Device, Interval, _streamPath);

// Create and Start the Stream
using (_stream = new MTConnectHttpClientStream(url, DocumentFormat))
using (_stream = new MTConnectHttpClientStream(_httpClient, url, DocumentFormat))
{
_stream.Timeout = Heartbeat * 3;
_stream.ContentEncodings = ContentEncodings;
Expand Down
31 changes: 28 additions & 3 deletions libraries/MTConnect.NET-HTTP/Clients/MTConnectHttpClientStream.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2024 TrakHound Inc., All Rights Reserved.
// Copyright (c) 2026 TrakHound Inc., All Rights Reserved.
// TrakHound Inc. licenses this file to you under the MIT license.

using MTConnect.Errors;
Expand All @@ -25,8 +25,15 @@ public class MTConnectHttpClientStream : IDisposable
private const byte CarriageReturn = 13;
private const byte Dash = 45;
private static readonly byte[] _trimBytes = new byte[] { LineFeed, CarriageReturn };
private static readonly HttpClient _defaultHttpClient;
private readonly HttpClient _httpClient;

static MTConnectHttpClientStream()
{
_defaultHttpClient = new HttpClient();
_defaultHttpClient.Timeout = TimeSpan.FromMilliseconds(DefaultTimeout);
}

private CancellationTokenSource _stop;
private string _documentFormat = DocumentFormat.XML;

Expand All @@ -42,15 +49,33 @@ public class MTConnectHttpClientStream : IDisposable
/// <param name="documentFormat">Document format key (e.g. <c>xml</c>, <c>json</c>) to request and to parse the response with.</param>
public MTConnectHttpClientStream(string url, string documentFormat = DocumentFormat.XML)
{
_httpClient = _defaultHttpClient;
Id = Guid.NewGuid().ToString();
Url = url;
Timeout = DefaultTimeout;
_documentFormat = documentFormat;
ContentEncodings = HttpContentEncodings.DefaultAccept;
ContentType = MimeTypes.Get(documentFormat);
}

_httpClient = new HttpClient();
_httpClient.Timeout = TimeSpan.FromMilliseconds(DefaultTimeout);
/// <summary>
/// Constructs an HTTP streaming reader for the MTConnect long-poll <c>sample</c> response
/// at <paramref name="url"/>. The stream assigns itself a fresh <see cref="Id"/>, defaults
/// the read timeout to five minutes, advertises the standard
/// <see cref="HttpContentEncodings.DefaultAccept"/> on <c>Accept-Encoding</c>, and picks
/// the matching <c>Accept</c> MIME type for <paramref name="documentFormat"/>.
/// </summary>
/// <param name="url">The fully built <c>sample</c> URL with <c>interval</c> / <c>heartbeat</c> parameters.</param>
/// <param name="documentFormat">Document format key (e.g. <c>xml</c>, <c>json</c>) to request and to parse the response with.</param>
public MTConnectHttpClientStream(HttpClient httpClient, string url, string documentFormat = DocumentFormat.XML)
{
_httpClient = httpClient != null ? httpClient : _defaultHttpClient;
Id = Guid.NewGuid().ToString();
Url = url;
Timeout = DefaultTimeout;
_documentFormat = documentFormat;
ContentEncodings = HttpContentEncodings.DefaultAccept;
ContentType = MimeTypes.Get(documentFormat);
}

/// <summary>Disposes the underlying <see cref="HttpClient"/>. The stream itself should be <see cref="Stop"/>ped first; <see cref="Dispose"/> does not cancel pending reads.</summary>
Expand Down
Loading
Loading