using System; using System.Reflection; using System.Text; namespace DanroJump.Hardware.Com { /// /// Создает COM-транспорт через reflection-обертку над System.IO.Ports.SerialPort. /// public sealed class ReflectionSerialComTransportFactory : IComTransportFactory { /// /// Создает транспорт для указанного serial-порта. /// public IComTransport Create(SerialSettings settings) { return ReflectionSerialComTransport.Create(settings); } } /// /// Reflection-адаптер serial-порта, который изолирует проект от прямой ссылки на System.IO.Ports. /// internal sealed class ReflectionSerialComTransport : IComTransport { private const BindingFlags InstancePublicFlags = BindingFlags.Public | BindingFlags.Instance; private static readonly Type SerialPortType = Type.GetType("System.IO.Ports.SerialPort, System"); private static readonly Type ParityType = Type.GetType("System.IO.Ports.Parity, System"); private static readonly Type StopBitsType = Type.GetType("System.IO.Ports.StopBits, System"); private static readonly PropertyInfo IsOpenProperty = SerialPortType?.GetProperty("IsOpen", InstancePublicFlags); private static readonly PropertyInfo DataBitsProperty = SerialPortType?.GetProperty("DataBits", InstancePublicFlags); private static readonly PropertyInfo ParityProperty = SerialPortType?.GetProperty("Parity", InstancePublicFlags); private static readonly PropertyInfo StopBitsProperty = SerialPortType?.GetProperty("StopBits", InstancePublicFlags); private static readonly PropertyInfo ReadTimeoutProperty = SerialPortType?.GetProperty("ReadTimeout", InstancePublicFlags); private static readonly PropertyInfo WriteTimeoutProperty = SerialPortType?.GetProperty("WriteTimeout", InstancePublicFlags); private static readonly PropertyInfo NewLineProperty = SerialPortType?.GetProperty("NewLine", InstancePublicFlags); private static readonly PropertyInfo EncodingProperty = SerialPortType?.GetProperty("Encoding", InstancePublicFlags); private static readonly PropertyInfo DtrEnableProperty = SerialPortType?.GetProperty("DtrEnable", InstancePublicFlags); private static readonly PropertyInfo RtsEnableProperty = SerialPortType?.GetProperty("RtsEnable", InstancePublicFlags); private static readonly MethodInfo OpenMethod = SerialPortType?.GetMethod("Open", InstancePublicFlags, null, Type.EmptyTypes, null); private static readonly MethodInfo CloseMethod = SerialPortType?.GetMethod("Close", InstancePublicFlags, null, Type.EmptyTypes, null); private static readonly MethodInfo DiscardInBufferMethod = SerialPortType?.GetMethod("DiscardInBuffer", InstancePublicFlags, null, Type.EmptyTypes, null); private static readonly MethodInfo DiscardOutBufferMethod = SerialPortType?.GetMethod("DiscardOutBuffer", InstancePublicFlags, null, Type.EmptyTypes, null); private static readonly MethodInfo ReadLineMethod = SerialPortType?.GetMethod("ReadLine", InstancePublicFlags, null, Type.EmptyTypes, null); private static readonly MethodInfo WriteLineMethod = SerialPortType?.GetMethod("WriteLine", InstancePublicFlags, null, new[] { typeof(string) }, null); private readonly object instance; private ReflectionSerialComTransport(object instance) { this.instance = instance; } /// /// Показывает, открыт ли underlying serial-порт. /// public bool IsOpen => GetBoolProperty(IsOpenProperty); /// /// Создает и настраивает экземпляр SerialPort через reflection. /// public static ReflectionSerialComTransport Create(SerialSettings settings) { if (SerialPortType == null) { throw new InvalidOperationException("System.IO.Ports.SerialPort is not available."); } EnsureRequiredMetadata(); object serialPort = Activator.CreateInstance(SerialPortType, settings.PortName, settings.BaudRate); var transport = new ReflectionSerialComTransport(serialPort); transport.SetProperty(DataBitsProperty, settings.DataBits); transport.SetProperty(ParityProperty, ParseEnum(ParityType, settings.Parity.ToString())); transport.SetProperty(StopBitsProperty, ParseEnum(StopBitsType, settings.StopBits.ToString())); transport.SetProperty(ReadTimeoutProperty, settings.ReadTimeoutMs); transport.SetProperty(WriteTimeoutProperty, settings.WriteTimeoutMs); transport.SetProperty(NewLineProperty, "\n"); transport.SetProperty(EncodingProperty, Encoding.UTF8); transport.SetProperty(DtrEnableProperty, true); transport.SetProperty(RtsEnableProperty, true); return transport; } /// /// Открывает serial-порт. /// public void Open() { Invoke(OpenMethod); } /// /// Закрывает serial-порт. /// public void Close() { Invoke(CloseMethod); } /// /// Очищает входной буфер serial-порта. /// public void DiscardInBuffer() { Invoke(DiscardInBufferMethod); } /// /// Очищает выходной буфер serial-порта. /// public void DiscardOutBuffer() { Invoke(DiscardOutBufferMethod); } /// /// Читает одну строку из serial-порта. /// public string ReadLine() { return (string)Invoke(ReadLineMethod); } /// /// Записывает одну строку в serial-порт. /// public void WriteLine(string value) { Invoke(WriteLineMethod, value); } /// /// Освобождает underlying SerialPort, если он поддерживает IDisposable. /// public void Dispose() { if (instance is IDisposable disposable) { disposable.Dispose(); } } /// /// Проверяет наличие всех reflection-членов заранее, чтобы ошибки были явными. /// private static void EnsureRequiredMetadata() { Require(DataBitsProperty, "DataBits"); Require(ParityProperty, "Parity"); Require(StopBitsProperty, "StopBits"); Require(ReadTimeoutProperty, "ReadTimeout"); Require(WriteTimeoutProperty, "WriteTimeout"); Require(NewLineProperty, "NewLine"); Require(EncodingProperty, "Encoding"); Require(DtrEnableProperty, "DtrEnable"); Require(RtsEnableProperty, "RtsEnable"); Require(IsOpenProperty, "IsOpen"); Require(OpenMethod, "Open()"); Require(CloseMethod, "Close()"); Require(DiscardInBufferMethod, "DiscardInBuffer()"); Require(DiscardOutBufferMethod, "DiscardOutBuffer()"); Require(ReadLineMethod, "ReadLine()"); Require(WriteLineMethod, "WriteLine(string)"); } private static void Require(MemberInfo member, string memberName) { if (member == null) { throw new MissingMemberException(SerialPortType?.FullName ?? "System.IO.Ports.SerialPort", memberName); } } /// /// Читает bool-свойство SerialPort через заранее найденный PropertyInfo. /// private bool GetBoolProperty(PropertyInfo property) { return property.GetValue(instance) is bool value && value; } /// /// Записывает свойство SerialPort через заранее найденный PropertyInfo. /// private void SetProperty(PropertyInfo property, object value) { property.SetValue(instance, value); } /// /// Вызывает метод SerialPort через reflection и пробрасывает реальную inner-ошибку. /// private object Invoke(MethodInfo method, params object[] args) { try { return method.Invoke(instance, args); } catch (TargetInvocationException exception) when (exception.InnerException != null) { // Наружу пробрасываем реальную ошибку serial-порта, а не reflection-обертку. throw exception.InnerException; } } private static object ParseEnum(Type enumType, string value) { if (enumType == null) { throw new InvalidOperationException("System.IO.Ports enum metadata is not available."); } return Enum.Parse(enumType, value); } } }