348 lines
11 KiB
C#
348 lines
11 KiB
C#
|
|
||
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Reflection;
|
||
|
using System.Runtime.CompilerServices;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Text;
|
||
|
|
||
|
|
||
|
namespace Autolabor
|
||
|
{
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential)]
|
||
|
public struct SerialDevice
|
||
|
{
|
||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||
|
public string port;
|
||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||
|
public string description;
|
||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||
|
public string hardware_id;
|
||
|
public UInt16 vid;
|
||
|
public UInt16 pid;
|
||
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)]
|
||
|
public string serial_num;
|
||
|
|
||
|
}
|
||
|
public class SerialBaseException : Exception
|
||
|
{
|
||
|
public SerialBaseException(string message)
|
||
|
:base(message)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class SerialBase
|
||
|
{
|
||
|
public static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
|
||
|
{
|
||
|
|
||
|
if (libraryName == "serial_base")
|
||
|
{
|
||
|
|
||
|
if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||
|
{
|
||
|
if (System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||
|
{
|
||
|
|
||
|
return NativeLibrary.Load("libserial_base-mac-arm64.dylib", assembly, searchPath);
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return NativeLibrary.Load("libserial_base-mac.dylib", assembly, searchPath);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||
|
{
|
||
|
if (System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||
|
{
|
||
|
return NativeLibrary.Load("libserial_base-linux-arm64.so", assembly, searchPath);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return NativeLibrary.Load("libserial_base-linux.so", assembly, searchPath);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else if (System.Runtime.InteropServices.RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||
|
{
|
||
|
if (System.Runtime.InteropServices.RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
|
||
|
{
|
||
|
return IntPtr.Zero;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return NativeLibrary.Load("serial_base-win.dll", assembly, searchPath);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
return IntPtr.Zero;
|
||
|
}
|
||
|
|
||
|
[DllImport("serial_base", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_enumerate_ports(IntPtr excep,IntPtr[] portlist ,ref int portsize);
|
||
|
|
||
|
|
||
|
[DllImport("serial_base",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_create(IntPtr excep,ref IntPtr serial,string port, int baudrate, int timeout);
|
||
|
|
||
|
[DllImport("serial_base", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_open(IntPtr excep,IntPtr serial);
|
||
|
|
||
|
[DllImport("serial_base", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_close(IntPtr excep,IntPtr serial);
|
||
|
|
||
|
[DllImport("serial_base",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_is_open(IntPtr excep,IntPtr serial,ref bool opened);
|
||
|
|
||
|
[DllImport("serial_base",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_write(IntPtr excep,IntPtr serial, IntPtr data,int size);
|
||
|
|
||
|
[DllImport("serial_base",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
|
||
|
private static extern bool al_serial_available(IntPtr excep,IntPtr serial, ref int avaiable_size);
|
||
|
|
||
|
[DllImport("serial_base",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_read(IntPtr excep,IntPtr serial, int buffer_size,IntPtr data,ref int recev_size);
|
||
|
|
||
|
[DllImport("serial_base",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_readline(IntPtr excep,IntPtr serial, StringBuilder data,ref int recev_size);
|
||
|
|
||
|
[DllImport("serial_base", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
||
|
private static extern bool al_serial_delete(IntPtr excep,ref IntPtr serial);
|
||
|
|
||
|
|
||
|
private IntPtr _serialPtr;
|
||
|
|
||
|
public string Port=string.Empty;
|
||
|
|
||
|
public int Baudrate = 0;
|
||
|
|
||
|
public int ReadTimeout = 80;
|
||
|
|
||
|
|
||
|
private const int PortInfoBufferSize = 32;
|
||
|
private const int ReadBufferSize = 1024;
|
||
|
private const int WriteBufferSize = 1024;
|
||
|
private const int ExceptionBufferSize = 256; //固定尺寸,不能修改
|
||
|
|
||
|
private IntPtr _readBuffer;
|
||
|
private IntPtr _writeBuffer;
|
||
|
private IntPtr _exceptionBuffer;
|
||
|
|
||
|
private static bool dynamicLibLoaded = false;
|
||
|
|
||
|
public SerialBase()
|
||
|
{
|
||
|
_serialPtr=IntPtr.Zero;
|
||
|
_readBuffer = Marshal.AllocHGlobal(ReadBufferSize);
|
||
|
_writeBuffer = Marshal.AllocHGlobal(WriteBufferSize);
|
||
|
_exceptionBuffer = Marshal.AllocHGlobal(ExceptionBufferSize);
|
||
|
|
||
|
if (dynamicLibLoaded == false)
|
||
|
{
|
||
|
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), SerialBase.DllImportResolver);
|
||
|
|
||
|
dynamicLibLoaded = true;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static List<SerialDevice> EnumeratePorts()
|
||
|
{
|
||
|
if (dynamicLibLoaded == false)
|
||
|
{
|
||
|
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), SerialBase.DllImportResolver);
|
||
|
|
||
|
dynamicLibLoaded = true;
|
||
|
}
|
||
|
|
||
|
|
||
|
IntPtr[] portinfoPtrs = new IntPtr[PortInfoBufferSize];
|
||
|
|
||
|
IntPtr _tmpExceptionBuffer = Marshal.AllocHGlobal(ExceptionBufferSize);
|
||
|
|
||
|
List<SerialDevice> result = new List<SerialDevice>();
|
||
|
|
||
|
int copySize = 0;
|
||
|
|
||
|
for (int i = 0; i < PortInfoBufferSize; i++)
|
||
|
{
|
||
|
portinfoPtrs[i] = Marshal.AllocHGlobal(Marshal.SizeOf<SerialDevice>());
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
bool success = al_serial_enumerate_ports(_tmpExceptionBuffer, portinfoPtrs, ref copySize);
|
||
|
|
||
|
if (success == false)
|
||
|
{
|
||
|
for (int i = 0; i < PortInfoBufferSize; i++)
|
||
|
{
|
||
|
Marshal.FreeHGlobal(portinfoPtrs[i]);
|
||
|
}
|
||
|
|
||
|
throw new SerialBaseException(_tmpExceptionBuffer.ToString());
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < copySize; i++)
|
||
|
{
|
||
|
|
||
|
result.Add(Marshal.PtrToStructure<SerialDevice>(portinfoPtrs[i]));
|
||
|
|
||
|
}
|
||
|
|
||
|
for (int i = 0; i < PortInfoBufferSize; i++)
|
||
|
{
|
||
|
Marshal.FreeHGlobal(portinfoPtrs[i]);
|
||
|
}
|
||
|
|
||
|
Marshal.FreeHGlobal(_tmpExceptionBuffer);
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
public void Open()
|
||
|
{
|
||
|
|
||
|
if (_serialPtr == IntPtr.Zero)
|
||
|
{
|
||
|
|
||
|
if (al_serial_create(_exceptionBuffer, ref _serialPtr, Port, Baudrate, ReadTimeout) == false) {
|
||
|
|
||
|
throw new SerialBaseException(_exceptionBuffer.ToString());
|
||
|
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (al_serial_open(_exceptionBuffer,_serialPtr) == false)
|
||
|
{
|
||
|
throw new SerialBaseException(_exceptionBuffer.ToString());
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public void Close()
|
||
|
{
|
||
|
|
||
|
|
||
|
if (_serialPtr == IntPtr.Zero)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
|
||
|
if (al_serial_close(_exceptionBuffer,_serialPtr) == false)
|
||
|
{
|
||
|
throw new SerialBaseException(_exceptionBuffer.ToString());
|
||
|
}
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
public bool IsOpen()
|
||
|
{
|
||
|
|
||
|
|
||
|
if (_serialPtr == IntPtr.Zero)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool opened = false;
|
||
|
|
||
|
|
||
|
if (al_serial_is_open(_exceptionBuffer, _serialPtr,ref opened) == false)
|
||
|
{
|
||
|
throw new SerialBaseException(_exceptionBuffer.ToString());
|
||
|
}
|
||
|
|
||
|
return opened;
|
||
|
|
||
|
}
|
||
|
|
||
|
public void Write(byte[] data)
|
||
|
{
|
||
|
|
||
|
if (_serialPtr == IntPtr.Zero)
|
||
|
{
|
||
|
throw new SerialBaseException("Serial is not open.");
|
||
|
}
|
||
|
|
||
|
int sendCount = 0;
|
||
|
|
||
|
// 分批发送数据
|
||
|
while (sendCount < data.Length) {
|
||
|
|
||
|
int needSend = (data.Length - sendCount) >= WriteBufferSize
|
||
|
? WriteBufferSize
|
||
|
: (data.Length - sendCount);
|
||
|
|
||
|
Marshal.Copy(data,sendCount,_writeBuffer, needSend);
|
||
|
|
||
|
if (al_serial_write(_exceptionBuffer, _serialPtr, _writeBuffer,needSend) == false)
|
||
|
{
|
||
|
|
||
|
throw new SerialBaseException(_exceptionBuffer.ToString());
|
||
|
}
|
||
|
|
||
|
sendCount += needSend;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
public byte[] Read()
|
||
|
{
|
||
|
|
||
|
|
||
|
if (_serialPtr == IntPtr.Zero)
|
||
|
{
|
||
|
throw new SerialBaseException("Serial is not open.");
|
||
|
}
|
||
|
|
||
|
|
||
|
int recvSize = 0;
|
||
|
|
||
|
if (al_serial_read(_exceptionBuffer, _serialPtr,ReadBufferSize, _readBuffer,ref recvSize) == false)
|
||
|
{
|
||
|
throw new SerialBaseException(_exceptionBuffer.ToString());
|
||
|
}
|
||
|
|
||
|
|
||
|
Byte[] recvData = new byte[recvSize];
|
||
|
|
||
|
Marshal.Copy(_readBuffer, recvData, 0, recvSize);
|
||
|
|
||
|
return recvData;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
~SerialBase() {
|
||
|
al_serial_delete(_exceptionBuffer, ref _serialPtr);
|
||
|
_serialPtr=IntPtr.Zero;
|
||
|
Marshal.FreeHGlobal(_readBuffer);
|
||
|
Marshal.FreeHGlobal(_writeBuffer);
|
||
|
Marshal.FreeHGlobal(_exceptionBuffer);
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|