diff --git a/OBD.NET/OBD.NET.Common/OBD.NET.Common.csproj b/OBD.NET/OBD.NET.Common/OBD.NET.Common.csproj index dd002d0..856ad40 100644 --- a/OBD.NET/OBD.NET.Common/OBD.NET.Common.csproj +++ b/OBD.NET/OBD.NET.Common/OBD.NET.Common.csproj @@ -1,7 +1,7 @@  - netstandard1.6 + netstandard1.4 Wyrez / Roman Lumetsberger - OBD.NET diff --git a/OBD.NET/OBD.NET.sln b/OBD.NET/OBD.NET.sln index 2d47b29..5ffecc7 100644 --- a/OBD.NET/OBD.NET.sln +++ b/OBD.NET/OBD.NET.sln @@ -5,6 +5,8 @@ VisualStudioVersion = 15.0.26403.7 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OBD.NET.Common", "OBD.NET.Common\OBD.NET.Common.csproj", "{D985B70E-CDF3-4CF1-AB5D-8D19C7FE7B31}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ODB.NET.Desktop", "ODB.NET.Desktop\ODB.NET.Desktop.csproj", "{14CB98E1-95DE-4923-8896-FDF5171AA49E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,6 +17,10 @@ Global {D985B70E-CDF3-4CF1-AB5D-8D19C7FE7B31}.Debug|Any CPU.Build.0 = Debug|Any CPU {D985B70E-CDF3-4CF1-AB5D-8D19C7FE7B31}.Release|Any CPU.ActiveCfg = Release|Any CPU {D985B70E-CDF3-4CF1-AB5D-8D19C7FE7B31}.Release|Any CPU.Build.0 = Release|Any CPU + {14CB98E1-95DE-4923-8896-FDF5171AA49E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {14CB98E1-95DE-4923-8896-FDF5171AA49E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {14CB98E1-95DE-4923-8896-FDF5171AA49E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {14CB98E1-95DE-4923-8896-FDF5171AA49E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/OBD.NET/ODB.NET.Desktop/Communication/EnhancedSerialPort.cs b/OBD.NET/ODB.NET.Desktop/Communication/EnhancedSerialPort.cs new file mode 100644 index 0000000..f92267b --- /dev/null +++ b/OBD.NET/ODB.NET.Desktop/Communication/EnhancedSerialPort.cs @@ -0,0 +1,163 @@ +// Copyright 2013 Antanas Veiverys www.veiverys.com +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using System; +using System.ComponentModel; +using System.IO; +using System.IO.Ports; +using System.Reflection; +using System.Runtime.InteropServices; + +// Source: http://antanas.veiverys.com/mono-serialport-datareceived-event-workaround-using-a-derived-class/ +namespace OBD.NET.Communication +{ + [DesignerCategory("Code")] + public class EnhancedSerialPort : SerialPort + { + #region Properties & Fields + + // private member access via reflection + private int _fd; + private FieldInfo _disposedFieldInfo; + private object _dataReceived; + + #endregion + + #region DLLImports + + [DllImport("MonoPosixHelper", SetLastError = true)] + private static extern bool poll_serial(int fd, out int error, int timeout); + + [DllImport("libc")] + private static extern IntPtr strerror(int errnum); + + #endregion + + #region Constructors + + public EnhancedSerialPort() + : base() + { } + + public EnhancedSerialPort(IContainer container) + : base(container) + { } + + public EnhancedSerialPort(string portName) + : base(portName) + { } + + public EnhancedSerialPort(string portName, int baudRate) + : base(portName, baudRate) + { } + + public EnhancedSerialPort(string portName, int baudRate, Parity parity) + : base(portName, baudRate, parity) + { } + + public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits) + : base(portName, baudRate, parity, dataBits) + { } + + public EnhancedSerialPort(string portName, int baudRate, Parity parity, int dataBits, StopBits stopBits) + : base(portName, baudRate, parity, dataBits, stopBits) + { } + + #endregion + + #region Methods + + public new void Open() + { + base.Open(); + + if (!IsWindows) + { + FieldInfo fieldInfo = BaseStream.GetType().GetField("fd", BindingFlags.Instance | BindingFlags.NonPublic); + _fd = (int)fieldInfo.GetValue(BaseStream); + _disposedFieldInfo = BaseStream.GetType().GetField("disposed", BindingFlags.Instance | BindingFlags.NonPublic); + fieldInfo = typeof(SerialPort).GetField("data_received", BindingFlags.Instance | BindingFlags.NonPublic); + _dataReceived = fieldInfo.GetValue(this); + + new System.Threading.Thread(EventThreadFunction).Start(); + } + } + + private static bool IsWindows + { + get + { + PlatformID id = Environment.OSVersion.Platform; + return (id == PlatformID.Win32Windows) || (id == PlatformID.Win32NT); // WinCE not supported + } + } + + private void EventThreadFunction() + { + do + { + try + { + Stream stream = BaseStream; + if (stream == null) + return; + + if (Poll(stream, ReadTimeout)) + OnDataReceived(null); + } + catch + { + return; + } + } while (IsOpen); + } + + private void OnDataReceived(SerialDataReceivedEventArgs args) + { + SerialDataReceivedEventHandler handler = Events[_dataReceived] as SerialDataReceivedEventHandler; + handler?.Invoke(this, args); + } + + private bool Poll(Stream stream, int timeout) + { + CheckDisposed(stream); + if (IsOpen == false) + throw new Exception("port is closed"); + int error; + + bool pollResult = poll_serial(_fd, out error, ReadTimeout); + if (error == -1) + ThrowIOException(); + + return pollResult; + } + + private static void ThrowIOException() + { + int errnum = Marshal.GetLastWin32Error(); + string errorMessage = Marshal.PtrToStringAnsi(strerror(errnum)); + + throw new IOException(errorMessage); + } + + private void CheckDisposed(Stream stream) + { + bool disposed = (bool)_disposedFieldInfo.GetValue(stream); + if (disposed) + throw new ObjectDisposedException(stream.GetType().FullName); + } + + #endregion + } +} diff --git a/OBD.NET/ODB.NET.Desktop/Communication/SerialConnection.cs b/OBD.NET/ODB.NET.Desktop/Communication/SerialConnection.cs new file mode 100644 index 0000000..f580314 --- /dev/null +++ b/OBD.NET/ODB.NET.Desktop/Communication/SerialConnection.cs @@ -0,0 +1,108 @@ +using System; +using System.IO.Ports; +using System.Text; +using System.Threading; + +namespace OBD.NET.Communication +{ + public class SerialConnection : ISerialConnection + { + #region Properties & Fields + + private readonly EnhancedSerialPort _serialPort; + private readonly int _timeout; + + public bool IsOpen => _serialPort?.IsOpen ?? false; + + private readonly byte[] _readBuffer = new byte[1024]; + private readonly StringBuilder _lineBuffer = new StringBuilder(); + + private readonly AutoResetEvent _hasPrompt = new AutoResetEvent(true); + + #endregion + + #region Events + + public event EventHandler MessageReceived; + + #endregion + + #region Constructors + + public SerialConnection(string port, int baudRate = 38400, Parity parity = Parity.None, StopBits stopBits = StopBits.One, + Handshake handshake = Handshake.None, int timeout = 5000) + { + this._timeout = timeout; + _serialPort = new EnhancedSerialPort(port, baudRate, parity) + { + StopBits = stopBits, + Handshake = handshake, + ReadTimeout = timeout, + WriteTimeout = timeout + }; + + _serialPort.DataReceived += SerialPortOnDataReceived; + } + + #endregion + + #region Methods + + public void Connect() + { + _serialPort.Open(); + Thread.Sleep(5000); + Write("\r"); + } + + public void Write(string text) + { + if (!_hasPrompt.WaitOne(_timeout)) + throw new TimeoutException("No prompt received"); + + _serialPort.Write(text); + } + + private void SerialPortOnDataReceived(object sender, SerialDataReceivedEventArgs serialDataReceivedEventArgs) + { + int count = _serialPort.Read(_readBuffer, 0, _serialPort.BytesToRead); + for (int i = 0; i < count; i++) + { + char c = (char)_readBuffer[i]; + switch (c) + { + case '\r': + FinishLine(); + break; + + case '>': + _hasPrompt.Set(); + break; + + case '\n': + case (char)0x00: + break; // ignore + + default: + _lineBuffer.Append(c); + break; + } + } + } + + private void FinishLine() + { + string line = _lineBuffer.ToString(); + _lineBuffer.Clear(); + + MessageReceived?.Invoke(this, line); + } + + public void Dispose() + { + _serialPort?.Dispose(); + } + + #endregion + } +} diff --git a/OBD.NET/ODB.NET.Desktop/ODB.NET.Desktop.csproj b/OBD.NET/ODB.NET.Desktop/ODB.NET.Desktop.csproj new file mode 100644 index 0000000..083888b --- /dev/null +++ b/OBD.NET/ODB.NET.Desktop/ODB.NET.Desktop.csproj @@ -0,0 +1,54 @@ + + + + + Debug + AnyCPU + {14CB98E1-95DE-4923-8896-FDF5171AA49E} + Library + Properties + ODB.NET.Desktop + ODB.NET.Desktop + v4.6.1 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + {d985b70e-cdf3-4cf1-ab5d-8d19c7fe7b31} + OBD.NET.Common + + + + \ No newline at end of file diff --git a/OBD.NET/ODB.NET.Desktop/Properties/AssemblyInfo.cs b/OBD.NET/ODB.NET.Desktop/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0ae86dc --- /dev/null +++ b/OBD.NET/ODB.NET.Desktop/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("ODB.NET.Desktop")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("ODB.NET.Desktop")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("14cb98e1-95de-4923-8896-fdf5171aa49e")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]