diff --git a/src/.vs/obd2NET/v15/sqlite3/storage.ide b/src/.vs/obd2NET/v15/sqlite3/storage.ide new file mode 100644 index 0000000..228ccb7 Binary files /dev/null and b/src/.vs/obd2NET/v15/sqlite3/storage.ide differ diff --git a/src/obd2NET.sln b/src/obd2NET.sln index eaf5c7e..d497ec1 100644 --- a/src/obd2NET.sln +++ b/src/obd2NET.sln @@ -1,9 +1,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.15 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "obd2NET", "obd2NET\obd2NET.csproj", "{388801C6-5CC7-43AE-B82F-C13FE2DBC131}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Obd2NET", "Obd2NET\Obd2NET.csproj", "{4C873365-DFFC-4166-9BB2-2AC43E036BFD}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -11,12 +11,15 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {388801C6-5CC7-43AE-B82F-C13FE2DBC131}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {388801C6-5CC7-43AE-B82F-C13FE2DBC131}.Debug|Any CPU.Build.0 = Debug|Any CPU - {388801C6-5CC7-43AE-B82F-C13FE2DBC131}.Release|Any CPU.ActiveCfg = Release|Any CPU - {388801C6-5CC7-43AE-B82F-C13FE2DBC131}.Release|Any CPU.Build.0 = Release|Any CPU + {4C873365-DFFC-4166-9BB2-2AC43E036BFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4C873365-DFFC-4166-9BB2-2AC43E036BFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4C873365-DFFC-4166-9BB2-2AC43E036BFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4C873365-DFFC-4166-9BB2-2AC43E036BFD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A7CBB526-91B8-46AF-BFCE-6C372C5E60CC} + EndGlobalSection EndGlobal diff --git a/src/obd2NET/ControllerResponse.cs b/src/obd2NET/ControllerResponse.cs index dc14420..8018db2 100644 --- a/src/obd2NET/ControllerResponse.cs +++ b/src/obd2NET/ControllerResponse.cs @@ -1,11 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Text.RegularExpressions; -using System.Threading.Tasks; -namespace obd2NET +namespace Obd2NET { /// <summary> /// Represents the response received from the controller unit. @@ -24,7 +20,7 @@ public Byte[] Value { get { - if(RequestedPID != Vehicle.PID.Unknown && RequestedMode != Vehicle.Mode.Unknown) + if (RequestedPID != Vehicle.PID.Unknown && RequestedMode != Vehicle.Mode.Unknown) { Match matchedPattern = Regex.Match(Raw, @"\n([0-9a-fA-F ]{5})([0-9a-fA-F ]+)\r\n>"); return (matchedPattern.Groups.Count > 2) ? matchedPattern.Groups[2].Value.Replace(" ", "").ToByteArray() : Raw.ToByteArray(); diff --git a/src/obd2NET/DiagnosticTroubleCode.cs b/src/obd2NET/DiagnosticTroubleCode.cs index 75e5618..3900543 100644 --- a/src/obd2NET/DiagnosticTroubleCode.cs +++ b/src/obd2NET/DiagnosticTroubleCode.cs @@ -1,11 +1,8 @@ using System; -using System.Collections; -using System.Collections.Generic; using System.Linq; using System.Text; -using System.Threading.Tasks; -namespace obd2NET +namespace Obd2NET { /// <summary> /// Represents a Diagnostic trouble code which is used for reporting errors within the vehicle. @@ -27,7 +24,7 @@ public enum Location /// <summary> /// Raw and complete trouble code /// </summary> - public Byte[] Code { get; set; } + public byte[] Code { get; set; } /// <summary> /// Human readable text representation @@ -38,24 +35,24 @@ public string TextRepresentation { get { - string representation = ""; - switch(ErrorLocation) + var sb = new StringBuilder(); + switch (ErrorLocation) { - case Location.Powertrain: { representation = "P"; break; } - case Location.Chassis: { representation = "C"; break; } - case Location.Body: { representation = "B"; break; } - case Location.Network: { representation = "U"; break; } + case Location.Powertrain: { sb.Append("P"); break; } + case Location.Chassis: { sb.Append("C"); break; } + case Location.Body: { sb.Append("B"); break; } + case Location.Network: { sb.Append("U"); break; } } - Byte firstByte = Code.First(); - representation += (firstByte >> 4) & 3; - representation += Convert.ToInt32(((firstByte >> 0) & 15).ToString(), 16); + byte firstByte = Code.First(); + sb.Append((firstByte >> 4) & 3); + sb.Append(Convert.ToInt32(((firstByte >> 0) & 15).ToString(), 16)); - Byte secondByte = Code.ElementAt(1); - representation += Convert.ToInt32(((secondByte >> 4) & 15).ToString(), 16); - representation += Convert.ToInt32(((secondByte >> 0) & 15).ToString(), 16); + byte secondByte = Code.ElementAt(1); + sb.Append(Convert.ToInt32(((secondByte >> 4) & 15).ToString(), 16)); + sb.Append(Convert.ToInt32(((secondByte >> 0) & 15).ToString(), 16)); - return representation; + return sb.ToString(); } } @@ -66,17 +63,16 @@ public Location ErrorLocation { get { - Byte firstByte = Code.First(); - return (Location) ((firstByte >> 6) & 3); + return (Location)((Code.First() >> 6) & 3); } } public DiagnosticTroubleCode(string code) { - Code = System.Text.Encoding.Default.GetBytes(code); + Code = Encoding.Default.GetBytes(code); } - public DiagnosticTroubleCode(Byte[] code) + public DiagnosticTroubleCode(byte[] code) { Code = code; } diff --git a/src/obd2NET/Extensions.cs b/src/obd2NET/Extensions.cs index a5f0a60..fb64fbd 100644 --- a/src/obd2NET/Extensions.cs +++ b/src/obd2NET/Extensions.cs @@ -1,10 +1,6 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace obd2NET +namespace Obd2NET { public static class Extensions { @@ -22,4 +18,4 @@ public static byte[] ToByteArray(this string hex) return bytes; } } -} +} \ No newline at end of file diff --git a/src/obd2NET/IOBDConnection.cs b/src/obd2NET/IOBDConnection.cs index 4060b2d..59724c4 100644 --- a/src/obd2NET/IOBDConnection.cs +++ b/src/obd2NET/IOBDConnection.cs @@ -1,10 +1,6 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System.Threading.Tasks; -namespace obd2NET +namespace Obd2NET { /// <summary> /// Interface to be implemented by all connections that are interacting with the OBD interface of the vehicle @@ -15,6 +11,8 @@ public interface IOBDConnection void Open(); void Close(); ControllerResponse Query(Vehicle.Mode parameterMode, Vehicle.PID parameterID); + ValueTask<ControllerResponse> QueryAsync(Vehicle.Mode parameterMode, Vehicle.PID parameterID); ControllerResponse Query(Vehicle.Mode parameterMode); + ValueTask<ControllerResponse> QueryAsync(Vehicle.Mode parameterMode); } } diff --git a/src/obd2NET/Properties/AssemblyInfo.cs b/src/obd2NET/Properties/AssemblyInfo.cs deleted file mode 100644 index 224c5b4..0000000 --- a/src/obd2NET/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -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("obd2NET")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("obd2NET")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[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("8364f400-9c4b-4ab9-bae0-17877596a355")] - -// 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")] diff --git a/src/obd2NET/QueryException.cs b/src/obd2NET/QueryException.cs index c48b7da..054fad6 100644 --- a/src/obd2NET/QueryException.cs +++ b/src/obd2NET/QueryException.cs @@ -1,15 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -namespace obd2NET +namespace Obd2NET { public class QueryException : Exception { - public QueryException(string msg): - base(msg) - { } + public QueryException(string message) : base(message) + { + } } } diff --git a/src/obd2NET/SerialConnection.cs b/src/obd2NET/SerialConnection.cs index 06f6fc1..edb3cde 100644 --- a/src/obd2NET/SerialConnection.cs +++ b/src/obd2NET/SerialConnection.cs @@ -1,40 +1,47 @@ using System; -using System.Collections.Generic; using System.IO.Ports; -using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; -namespace obd2NET +namespace Obd2NET { /// <summary> /// Represents the serial connection to the vehicle's interface /// </summary> - public class SerialConnection : IOBDConnection + public class SerialConnection : IOBDConnection, IDisposable { - /// <summary> - /// Port used for communicating with the vehicle's interface - /// </summary> - public SerialPort Port { get; set; } + public SerialConnection() + : this(new SerialPort()) + { - public SerialConnection(): - this(new SerialPort()) - { } + } + + public SerialConnection(string comPort) + : this(new SerialPort(comPort)) + { - public SerialConnection(string comPort) : - this(new SerialPort(comPort)) - { } + } public SerialConnection(SerialPort port) { Port = port; - Open(); } - ~SerialConnection() + /// <summary> + /// Port used for communicating with the vehicle's interface + /// </summary> + public SerialPort Port { get; set; } + + private bool disposed; + + /// <summary> + /// Closes the connection to the interface if the connection is established + /// </summary> + public void Close() { - Close(); + if (Port.IsOpen) + Port.Close(); } /// <summary> @@ -46,13 +53,9 @@ public void Open() Port.Open(); } - /// <summary> - /// Closes the connection to the interface if the connection is established - /// </summary> - public void Close() + ~SerialConnection() { - if (Port.IsOpen) - Port.Close(); + Close(); } /// <summary> @@ -64,20 +67,58 @@ public void Close() /// <remarks> Blocking until a complete answer has been received </remarks> public ControllerResponse Query(Vehicle.Mode parameterMode, Vehicle.PID parameterID) { - Port.Write(Convert.ToUInt32(parameterMode).ToString("X2") + Convert.ToUInt32(parameterID).ToString("X2") + "\r"); + Port.Write($"{Convert.ToUInt32(parameterMode).ToString("X2")}{Convert.ToUInt32(parameterID).ToString("X2")}{"\r"}"); Thread.Sleep(100); string fullResponse = ""; - while(!fullResponse.Contains(">")) + while (!fullResponse.Contains(">")) { byte[] readBuffer = new byte[1024]; Port.Read(readBuffer, 0, 1024); - fullResponse = System.Text.Encoding.Default.GetString(readBuffer); + fullResponse = Encoding.Default.GetString(readBuffer); + } + + return new ControllerResponse(fullResponse, parameterMode, parameterID); + } + + public async ValueTask<ControllerResponse> QueryAsync(Vehicle.Mode parameterMode, Vehicle.PID parameterID) + { + Port.Write($"{Convert.ToUInt32(parameterMode).ToString("X2")}{Convert.ToUInt32(parameterID).ToString("X2")}{"\r"}"); + await Task.Delay(100); + + string fullResponse = ""; + while (!fullResponse.Contains(">")) + { + byte[] readBuffer = new byte[1024]; + Port.Read(readBuffer, 0, 1024); + fullResponse = Encoding.Default.GetString(readBuffer); } return new ControllerResponse(fullResponse, parameterMode, parameterID); } + /// <summary> + /// Queries data from the vehicle by sending a specific mode + /// </summary> + /// <param name="parameterMode"> <c>Vehicle.Mode</c> used </param> + /// <returns> <c>ControllerResponse</c> object holding the returned data from the controller unit </returns> + /// <remarks> Blocking until a complete answer has been received </remarks> + public async ValueTask<ControllerResponse> QueryAsync(Vehicle.Mode parameterMode) + { + Port.Write(Convert.ToUInt32(parameterMode).ToString("X2") + "\r"); + await Task.Delay(100); + + string fullResponse = ""; + while (!fullResponse.Contains(">")) + { + byte[] readBuffer = new byte[1024]; + Port.Read(readBuffer, 0, 1024); + fullResponse = Encoding.Default.GetString(readBuffer); + } + + return new ControllerResponse(fullResponse, parameterMode); + } + /// <summary> /// Queries data from the vehicle by sending a specific mode /// </summary> @@ -99,5 +140,19 @@ public ControllerResponse Query(Vehicle.Mode parameterMode) return new ControllerResponse(fullResponse, parameterMode); } + + private void Dispose(bool disposed) + { + if (!disposed) + { + Close(); + } + } + + public void Dispose() + { + Dispose(disposed); + disposed = true; + } } } diff --git a/src/obd2NET/Vehicle.cs b/src/obd2NET/Vehicle.cs index 1b0eed1..4329148 100644 --- a/src/obd2NET/Vehicle.cs +++ b/src/obd2NET/Vehicle.cs @@ -1,16 +1,6 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace obd2NET +namespace Obd2NET { - /// <summary> - /// Vehicle that can be accessed through an <c>IOBDConnection</c> - /// </summary> - public static class Vehicle + public class Vehicle { /// <summary> /// List of supported PIDs @@ -40,133 +30,5 @@ public enum Mode FreezeFrameData = 0x02, DiagnosticTroubleCodes = 0x03 } - - /// <summary> - /// Queries the current vehicle speed - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> Speed given in km/h </returns> - public static uint Speed(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.Speed); - if (response.HasValidData()) throw new QueryException("Vehicle speed couldn't be queried, the controller returned no data"); - - // the first value byte represents the speed in km/h - return (response.Value.Length >= 1) ? Convert.ToUInt32(response.Value.First()) : 0; - } - - /// <summary> - /// Queries the current engine temperature - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> Temperature given in celsius </returns> - public static int EngineTemperature(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.EngineTemperature); - if (response.HasValidData()) throw new QueryException("Engine temperature couldn't be queried, the controller returned no data"); - - // the first value byte minus 40 represents the engine temperature in celsius - return (response.Value.Length >= 1) ? Convert.ToInt32(response.Value.First()) - 40 : 0; - } - - /// <summary> - /// Queries the current RPM - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> The retrieved RPM </returns> - public static uint RPM(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.RPM); - if (response.HasValidData()) throw new QueryException("RPM couldn't be queried, the controller returned no data"); - - // RPM is given in 2 bytes - // Formula: ((A*256)+B)/4 - if (response.Value.Length < 2) throw new QueryException("RPM couldn't be queried, retrieved data was uncomplete"); - - uint rpm1 = Convert.ToUInt32(response.Value.First()); - uint rpm2 = Convert.ToUInt32(response.Value.ElementAt(1)); - return ((rpm1 * 256) + rpm2) / 4; - } - - /// <summary> - /// Queries the current throttle position - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> The retrieved throttle position in percentage (0-100) </returns> - public static uint ThrottlePosition(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.ThrottlePosition); - if (response.HasValidData()) throw new QueryException("Throttle position couldn't be queried, the controller returned no data"); - - // given in percentage, first value byte *100/255 - return (response.Value.Length >= 1) ? (Convert.ToUInt32(response.Value.First()) * 100) / 255 : 0; - } - - /// <summary> - /// Queries the current calculated engine load value - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> The retrieved calculated engine load value in percentage (0-100) </returns> - public static uint CalculatedEngineLoadValue(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.CalculatedEngineLoadValue); - if (response.HasValidData()) throw new QueryException("Calculated engine load value couldn't be queried, the controller returned no data"); - - // given in percentage, first value byte *100/255 - return (response.Value.Length >= 1) ? (Convert.ToUInt32(response.Value.First()) * 100) / 255 : 0; - } - - /// <summary> - /// Queries the current fuel pressure - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> The retrieved fuel pressure given in kPa </returns> - public static uint FuelPressure(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.FuelPressure); - if (response.HasValidData()) throw new QueryException("Fuel pressure couldn't be queried, the controller returned no data"); - - // given in kPa, first value byte * 3 - return (response.Value.Length >= 1) ? Convert.ToUInt32(response.Value.First()) * 3 : 0; - } - - /// <summary> - /// Queries the status of the malfunction indicator lamp (MIL) - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> true if the MIL is illuminated, false if not </returns> - public static bool MalfunctionIndicatorLamp(IOBDConnection obdConnection) - { - ControllerResponse response = obdConnection.Query(Mode.CurrentData, PID.MIL); - if (response.HasValidData()) throw new QueryException("Malfunction indicator lamp couldn't be queried, the controller returned no data"); - if (response.Value.Length == 0) return false; - - return Convert.ToBoolean((response.Value.First() >> 7) & 1); - } - - /// <summary> - /// Queries the available diagnostic trouble codes (DTCs) - /// </summary> - /// <param name="obdConnection"> Connection to the vehicle interface used to query the data </param> - /// <returns> List containing all DTCs that could be retrieved </returns> - public static List<DiagnosticTroubleCode> DiagnosticTroubleCodes(IOBDConnection obdConnection) - { - // issue the request for the actual DTCs - ControllerResponse response = obdConnection.Query(Mode.DiagnosticTroubleCodes); - if (response.HasValidData()) throw new QueryException("Diagnostic trouble codes couldn't be queried, the controller returned no data"); - if (response.Value.Length < 2) throw new QueryException("Diagnostic trouble codes couldn't be queried, received data was not complete"); - - var fetchedCodes = new List<DiagnosticTroubleCode>(); - for (int i = 1; i < response.Value.Length; i += 3) // each error code got a size of 3 bytes - { - byte[] troubleCode = new byte[3]; - Array.Copy(response.Value, i, troubleCode, 0, 3); - - if(!troubleCode.All(b => b == default(Byte))) // if the byte array is not entirely empty, add the error code to the parsed list - fetchedCodes.Add(new DiagnosticTroubleCode(troubleCode)); - } - - return fetchedCodes; - } } } diff --git a/src/obd2NET/obd2NET.csproj b/src/obd2NET/obd2NET.csproj index f876674..9973757 100644 --- a/src/obd2NET/obd2NET.csproj +++ b/src/obd2NET/obd2NET.csproj @@ -1,59 +1,11 @@ -<?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> +<Project Sdk="Microsoft.NET.Sdk"> + <PropertyGroup> - <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> - <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> - <ProjectGuid>{388801C6-5CC7-43AE-B82F-C13FE2DBC131}</ProjectGuid> - <OutputType>Library</OutputType> - <AppDesignerFolder>Properties</AppDesignerFolder> - <RootNamespace>obd2NET</RootNamespace> - <AssemblyName>obd2NET</AssemblyName> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> - <FileAlignment>512</FileAlignment> + <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> - <DebugSymbols>true</DebugSymbols> - <DebugType>full</DebugType> - <Optimize>false</Optimize> - <OutputPath>bin\Debug\</OutputPath> - <DefineConstants>DEBUG;TRACE</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> - <DebugType>pdbonly</DebugType> - <Optimize>true</Optimize> - <OutputPath>bin\Release\</OutputPath> - <DefineConstants>TRACE</DefineConstants> - <ErrorReport>prompt</ErrorReport> - <WarningLevel>4</WarningLevel> - </PropertyGroup> - <ItemGroup> - <Reference Include="System" /> - <Reference Include="System.Core" /> - <Reference Include="System.Xml.Linq" /> - <Reference Include="System.Data.DataSetExtensions" /> - <Reference Include="Microsoft.CSharp" /> - <Reference Include="System.Data" /> - <Reference Include="System.Xml" /> - </ItemGroup> + <ItemGroup> - <Compile Include="DiagnosticTroubleCode.cs" /> - <Compile Include="SerialConnection.cs" /> - <Compile Include="ControllerResponse.cs" /> - <Compile Include="Extensions.cs" /> - <Compile Include="IOBDConnection.cs" /> - <Compile Include="Properties\AssemblyInfo.cs" /> - <Compile Include="QueryException.cs" /> - <Compile Include="Vehicle.cs" /> + <PackageReference Include="System.IO.Ports" Version="4.4.0" /> </ItemGroup> - <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> - <!-- To modify your build process, add your task inside one of the targets below and uncomment it. - Other similar extension points exist, see Microsoft.Common.targets. - <Target Name="BeforeBuild"> - </Target> - <Target Name="AfterBuild"> - </Target> - --> -</Project> \ No newline at end of file + +</Project>