using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using ArchestrA.MxAccess;
using ZB.MOM.WW.LmxOpcUa.Host.Domain;
namespace ZB.MOM.WW.LmxOpcUa.Tests.Helpers
{
///
/// Fake IMxProxy for testing without the MxAccess COM runtime.
/// Simulates connections, subscriptions, data changes, and writes.
///
public class FakeMxProxy : IMxProxy
{
private int _nextHandle = 1;
private int _connectionHandle;
private bool _registered;
public event MxDataChangeHandler? OnDataChange;
public event MxWriteCompleteHandler? OnWriteComplete;
public ConcurrentDictionary Items { get; } = new ConcurrentDictionary();
public ConcurrentDictionary AdvisedItems { get; } = new ConcurrentDictionary();
public List<(string Address, object Value)> WrittenValues { get; } = new List<(string, object)>();
public bool IsRegistered => _registered;
public int RegisterCallCount { get; private set; }
public int UnregisterCallCount { get; private set; }
public bool ShouldFailRegister { get; set; }
public bool ShouldFailWrite { get; set; }
public int WriteCompleteStatus { get; set; } = 0; // 0 = success
public int Register(string clientName)
{
RegisterCallCount++;
if (ShouldFailRegister) throw new InvalidOperationException("Register failed (simulated)");
_registered = true;
_connectionHandle = Interlocked.Increment(ref _nextHandle);
return _connectionHandle;
}
public void Unregister(int handle)
{
UnregisterCallCount++;
_registered = false;
_connectionHandle = 0;
}
public int AddItem(int handle, string address)
{
var itemHandle = Interlocked.Increment(ref _nextHandle);
Items[itemHandle] = address;
return itemHandle;
}
public void RemoveItem(int handle, int itemHandle)
{
Items.TryRemove(itemHandle, out _);
}
public void AdviseSupervisory(int handle, int itemHandle)
{
AdvisedItems[itemHandle] = true;
}
public void UnAdviseSupervisory(int handle, int itemHandle)
{
AdvisedItems.TryRemove(itemHandle, out _);
}
public void Write(int handle, int itemHandle, object value, int securityClassification)
{
if (ShouldFailWrite) throw new InvalidOperationException("Write failed (simulated)");
if (Items.TryGetValue(itemHandle, out var address))
WrittenValues.Add((address, value));
// Simulate async write complete callback
var status = new MXSTATUS_PROXY[1];
if (WriteCompleteStatus == 0)
{
status[0].success = 1;
}
else
{
status[0].success = 0;
status[0].detail = (short)WriteCompleteStatus;
}
OnWriteComplete?.Invoke(_connectionHandle, itemHandle, ref status);
}
///
/// Simulates an MXAccess data change event for a specific item handle.
///
public void SimulateDataChange(int itemHandle, object value, int quality = 192, DateTime? timestamp = null)
{
var status = new MXSTATUS_PROXY[1];
status[0].success = 1;
OnDataChange?.Invoke(_connectionHandle, itemHandle, value, quality,
(object)(timestamp ?? DateTime.UtcNow), ref status);
}
///
/// Simulates data change for a specific address (finds handle by address).
///
public void SimulateDataChangeByAddress(string address, object value, int quality = 192, DateTime? timestamp = null)
{
foreach (var kvp in Items)
{
if (string.Equals(kvp.Value, address, StringComparison.OrdinalIgnoreCase))
{
SimulateDataChange(kvp.Key, value, quality, timestamp);
return;
}
}
}
}
}