c95824a65d
Full read-only SDK (src/AVEVA.Historian.Client) implementing the CLAUDE.md required
surface against AVEVA Historian's binary WCF protocol — no native AVEVA runtime
dependency. All operations live-verified against a local Historian:
- ProbeAsync, ReadRawAsync, ReadAggregateAsync, ReadAtTimeAsync, ReadEventsAsync
- BrowseTagNamesAsync, GetTagMetadataAsync (17 native data-type codes mapped)
- GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync
- 108/108 unit + integration tests pass
Includes the reverse-engineering toolkit (tools/AVEVA.Historian.ReverseEngineering)
used to decode the protocol: WCF probes, IL inspection via dnlib, and IL-rewrite
instrumentation (instrument-wcf-{write,read}message etc.) plus the .NET Framework
trace harness (tools/AVEVA.Historian.NativeTraceHarness) for parity testing.
Sanitized handoff evidence under docs/reverse-engineering/. Native AVEVA binaries
(current/, aveva-install-x64/, aveva-install-x86/) are gitignored — fetch separately
from the AVEVA installer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
106 lines
5.4 KiB
C#
106 lines
5.4 KiB
C#
using System.Reflection;
|
|
using System.ServiceModel;
|
|
using AVEVA.Historian.Client.Wcf;
|
|
using AVEVA.Historian.Client.Wcf.Contracts;
|
|
|
|
namespace AVEVA.Historian.Client.Tests;
|
|
|
|
public sealed class WcfEvidenceTests
|
|
{
|
|
[Fact]
|
|
public void ServiceContractsUseDecompiledNamesAndNamespace()
|
|
{
|
|
AssertServiceContract<IHistoryServiceContract>("Hist");
|
|
AssertServiceContract<IHistoryServiceContract2>("Hist");
|
|
AssertServiceContract<IRetrievalServiceContract>("Retr");
|
|
AssertServiceContract<IRetrievalServiceContract4>("Retr");
|
|
AssertServiceContract<IStatusServiceContract>("Stat");
|
|
AssertServiceContract<IStatusServiceContract2>("Stat");
|
|
AssertServiceContract<IStorageServiceContract>("Storage");
|
|
AssertServiceContract<ITransactionServiceContract>("Trx");
|
|
}
|
|
|
|
[Fact]
|
|
public void RelayEvidenceIdentifiesHistorySecurityEndpointNames()
|
|
{
|
|
Assert.Equal("HistCert", HistorianWcfServiceNames.HistoryCertificate);
|
|
Assert.Equal("Hist-Integrated", HistorianWcfServiceNames.HistoryIntegrated);
|
|
}
|
|
|
|
[Fact]
|
|
public void KnownOperationAliasesMatchManagedWrapperEvidence()
|
|
{
|
|
AssertOperation<IHistoryServiceContract>(nameof(IHistoryServiceContract.GetInterfaceVersion), "GetV");
|
|
AssertOperation<IHistoryServiceContract>(nameof(IHistoryServiceContract.OpenConnection), "Open");
|
|
AssertOperation<IHistoryServiceContract>(nameof(IHistoryServiceContract.ValidateClient), "VldC");
|
|
AssertOperation<IHistoryServiceContract>(nameof(IHistoryServiceContract.UpdateClientStatus), "UpdC");
|
|
AssertOperation<IHistoryServiceContract2>(nameof(IHistoryServiceContract2.OpenConnection2), "Open2");
|
|
AssertOperation<IHistoryServiceContract2>(nameof(IHistoryServiceContract2.ExchangeKey), "ExKey");
|
|
AssertOperation<IRetrievalServiceContract2>(nameof(IRetrievalServiceContract2.GetTagInfosFromId), "GetTg");
|
|
AssertOperation<IRetrievalServiceContract3>(nameof(IRetrievalServiceContract3.StartTagQuery), "QTB");
|
|
AssertOperation<IRetrievalServiceContract4>(nameof(IRetrievalServiceContract4.GetTagExtendedPropertiesFromName), "GetTepByNm");
|
|
AssertOperation<IStorageServiceContract>(nameof(IStorageServiceContract.OpenStorageConnection), "Open");
|
|
AssertOperation<IStorageServiceContract>(nameof(IStorageServiceContract.LoadBlocks), "LoadB");
|
|
AssertOperation<ITransactionServiceContract>(nameof(ITransactionServiceContract.GetInterfaceVersion), "GetV");
|
|
AssertDefaultOperation<IRetrievalServiceContract>(nameof(IRetrievalServiceContract.StartQuery));
|
|
AssertDefaultOperation<IRetrievalServiceContract4>(nameof(IRetrievalServiceContract4.StartEventQuery));
|
|
AssertDefaultOperation<IStatusServiceContract>(nameof(IStatusServiceContract.GetServerTime));
|
|
AssertDefaultOperation<IStatusServiceContract2>(nameof(IStatusServiceContract2.GetSystemParameter));
|
|
AssertOperation<IStatusServiceContract2>(nameof(IStatusServiceContract2.GetHistorianInfo), "GETHI");
|
|
AssertOperation<IStatusServiceContract2>(nameof(IStatusServiceContract2.PingServer), "PNGS");
|
|
AssertOperation<IStatusServiceContract2>(nameof(IStatusServiceContract2.PingPipe), "PNGP");
|
|
}
|
|
|
|
[Fact]
|
|
public void MdasBindingUsesNetTcpAndCustomContentType()
|
|
{
|
|
var binding = HistorianWcfBindingFactory.CreateMdasNetTcpBinding(TimeSpan.FromSeconds(5));
|
|
var encoder = binding.CreateBindingElements().Find<MdasMessageEncodingBindingElement>();
|
|
var endpoint = HistorianWcfBindingFactory.CreateEndpointAddress("localhost", HistorianWcfBindingFactory.DefaultPort, HistorianWcfServiceNames.History);
|
|
|
|
Assert.NotNull(encoder);
|
|
Assert.Equal("net.tcp://localhost:32568/Hist", endpoint.Uri.AbsoluteUri);
|
|
Assert.Equal(MdasMessageEncoder.MdasContentType, encoder.CreateMessageEncoderFactory().Encoder.ContentType);
|
|
}
|
|
|
|
[Fact]
|
|
public void CertificateBindingUsesMdasEncodingOverTransportSecurity()
|
|
{
|
|
var binding = HistorianWcfBindingFactory.CreateMdasNetTcpCertificateBinding(TimeSpan.FromSeconds(5));
|
|
var elements = binding.CreateBindingElements();
|
|
var encoder = elements.Find<MdasMessageEncodingBindingElement>();
|
|
var security = elements.Find<System.ServiceModel.Channels.SslStreamSecurityBindingElement>();
|
|
|
|
Assert.NotNull(encoder);
|
|
Assert.NotNull(security);
|
|
Assert.Equal(MdasMessageEncoder.MdasContentType, encoder.CreateMessageEncoderFactory().Encoder.ContentType);
|
|
}
|
|
|
|
private static void AssertServiceContract<TContract>(string name)
|
|
{
|
|
var attribute = typeof(TContract).GetCustomAttribute<ServiceContractAttribute>();
|
|
|
|
Assert.NotNull(attribute);
|
|
Assert.Equal(name, attribute.Name);
|
|
Assert.Equal("aa", attribute.Namespace);
|
|
}
|
|
|
|
private static void AssertOperation<TContract>(string methodName, string operationName)
|
|
{
|
|
var method = typeof(TContract).GetMethod(methodName);
|
|
var attribute = method?.GetCustomAttribute<OperationContractAttribute>();
|
|
|
|
Assert.NotNull(attribute);
|
|
Assert.Equal(operationName, attribute.Name);
|
|
}
|
|
|
|
private static void AssertDefaultOperation<TContract>(string methodName)
|
|
{
|
|
var method = typeof(TContract).GetMethod(methodName);
|
|
var attribute = method?.GetCustomAttribute<OperationContractAttribute>();
|
|
|
|
Assert.NotNull(attribute);
|
|
Assert.Null(attribute.Name);
|
|
}
|
|
}
|