Commit 4c790e30 authored by Andreas Müller's avatar Andreas Müller

Some fixes on internal structures and a minimal demo

parent 762481e8
......@@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D4E6650D-215
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3B6130C0-9168-4208-A677-29DD20301220}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleDemo", "src\ConsoleDemo\ConsoleDemo.csproj", "{2E6A165D-23DE-40D2-8A43-FE6EFA86F076}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -27,12 +29,17 @@ Global
{BF60D1D4-3767-4EC4-AC53-C1772958BE14}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BF60D1D4-3767-4EC4-AC53-C1772958BE14}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BF60D1D4-3767-4EC4-AC53-C1772958BE14}.Release|Any CPU.Build.0 = Release|Any CPU
{2E6A165D-23DE-40D2-8A43-FE6EFA86F076}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E6A165D-23DE-40D2-8A43-FE6EFA86F076}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E6A165D-23DE-40D2-8A43-FE6EFA86F076}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E6A165D-23DE-40D2-8A43-FE6EFA86F076}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{BF60D1D4-3767-4EC4-AC53-C1772958BE14} = {D4E6650D-2156-4660-B531-0B2AAD475BA1}
{2E6A165D-23DE-40D2-8A43-FE6EFA86F076} = {D4E6650D-2156-4660-B531-0B2AAD475BA1}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {FC7F1581-0E3C-4E35-9DBC-CC7952DE19C1}
......
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Modbus.Tcp\Modbus.Tcp.csproj" />
</ItemGroup>
</Project>
using Modbus.Tcp.Client;
using Modbus.Tcp.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleDemo
{
internal class Program
{
private static bool run = true;
private static void Main(string[] args)
{
Console.CancelKeyPress += Console_CancelKeyPress;
MainAsync(args).Wait();
}
private static async Task MainAsync(string[] args)
{
Console.WriteLine("Console Demo Modbus TCP (Read Holding Registers)");
Console.WriteLine();
Console.WriteLine("Please enter the connection parameters:");
Console.Write("Hostname : ");
var host = Console.ReadLine();
Console.Write("Port : ");
var port = Convert.ToInt32(Console.ReadLine());
Console.Write("Device ID: ");
var id = Convert.ToByte(Console.ReadLine());
var client = new ModbusClient(host, port);
while (run)
{
try
{
Console.WriteLine();
Console.Write("Address : ");
var address = Convert.ToUInt16(Console.ReadLine());
Console.Write("DataType: ");
var type = Console.ReadLine();
Console.WriteLine();
Console.Write("Result : ");
List<Register> result = null;
switch (type.Trim().ToLower())
{
case "byte":
result = await client.ReadHoldingRegisters(id, address, 1);
Console.WriteLine(result.First().GetByte());
break;
case "ushort":
result = await client.ReadHoldingRegisters(id, address, 1);
Console.WriteLine(result.First().GetUInt16());
break;
case "uint":
result = await client.ReadHoldingRegisters(id, address, 2);
Console.WriteLine(result.GetUInt32());
break;
case "ulong":
result = await client.ReadHoldingRegisters(id, address, 4);
Console.WriteLine(result.GetUInt64());
break;
case "sbyte":
result = await client.ReadHoldingRegisters(id, address, 1);
Console.WriteLine(result.First().GetSByte());
break;
case "short":
result = await client.ReadHoldingRegisters(id, address, 1);
Console.WriteLine(result.First().GetInt16());
break;
case "int":
result = await client.ReadHoldingRegisters(id, address, 2);
Console.WriteLine(result.GetInt32());
break;
case "long":
result = await client.ReadHoldingRegisters(id, address, 4);
Console.WriteLine(result.GetInt64());
break;
case "float":
result = await client.ReadHoldingRegisters(id, address, 2);
Console.WriteLine(result.GetSingle());
break;
case "double":
result = await client.ReadHoldingRegisters(id, address, 4);
Console.WriteLine(result.GetDouble());
break;
default:
Console.Write("DataType unknown");
break;
}
}
catch (Exception ex)
{
Console.WriteLine();
Console.WriteLine("ERROR: " + ex.Message);
Console.WriteLine();
}
}
client.Dispose();
}
private static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
run = false;
}
}
}
This diff is collapsed.
......@@ -2,6 +2,18 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<Version>0.9.0</Version>
<Authors>Andreas Müller</Authors>
<Company />
<Product>Modbus.TCP</Product>
<Description>Small library to connect via Modbus TCP on remote devices.</Description>
<Copyright>Copyright (c) 2017 Andreas Müller</Copyright>
<PackageLicenseUrl>https://github.com/BlackyPanther/Modbus/blob/master/LICENSE.txt</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/BlackyPanther/Modbus</PackageProjectUrl>
<RepositoryUrl />
<PackageTags>Modbus</PackageTags>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
......
using Modbus.Tcp.Utils;
using System;
using System.Linq;
using System.Threading;
namespace Modbus.Tcp.Protocol
{
internal class Request
{
private static ushort transactionNumber = 0;
private static int transactionNumber = 0;
private static ushort NextTransactionId
{
get
{
transactionNumber++;
return transactionNumber;
return (ushort)Interlocked.Increment(ref transactionNumber);
}
}
......@@ -28,7 +28,7 @@ namespace Modbus.Tcp.Protocol
public byte DeviceId { get; set; }
public byte Function { get; set; }
public FunctionCode Function { get; set; }
public ushort Address { get; set; }
......@@ -44,7 +44,7 @@ namespace Modbus.Tcp.Protocol
buffer.SetUInt16(2, 0x0000); // Protocol ID
buffer.SetByte(6, DeviceId);
buffer.SetByte(7, Function);
buffer.SetByte(7, (byte)Function);
buffer.SetUInt16(8, Address);
......
......@@ -27,11 +27,11 @@ namespace Modbus.Tcp.Protocol
if ((fn & Consts.ErrorMask) > 0)
{
IsError = true;
Function = (byte)(fn ^ Consts.ErrorMask);
Function = (FunctionCode)(fn ^ Consts.ErrorMask);
}
else
{
Function = fn;
Function = (FunctionCode)fn;
}
if (IsError)
......@@ -68,7 +68,7 @@ namespace Modbus.Tcp.Protocol
public byte DeviceId { get; private set; }
public byte Function { get; private set; }
public FunctionCode Function { get; private set; }
public bool IsError { get; private set; }
......
using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace Modbus.Tcp.Utils
{
internal class Consts
{
public static readonly Dictionary<byte, string> ErrorMessages = new Dictionary<byte, string>
#region Error/Exception
private static Dictionary<byte, string> exceptions = new Dictionary<byte, string>
{
{ 0, "No Error" },
{ 1, "Illegal Function" },
......@@ -19,23 +22,13 @@ namespace Modbus.Tcp.Utils
{ 11, "Gateway Target Device Failed to Respond" }
};
public const byte ErrorMask = 0x80;
public const byte ReadCoilsFunctionNumber = 0x01;
public const byte ReadDiscreteInputsFunctionNumber = 0x02;
public const byte ReadHoldingRegistersFunctionNumber = 0x03;
public static ReadOnlyDictionary<byte, string> ErrorMessages => new ReadOnlyDictionary<byte, string>(exceptions);
public const byte ReadInputRegistersFunctionNumber = 0x04;
public const byte WriteSingleCoilFunctionNumber = 0x05;
public const byte WriteSingleRegisterFunctionNumber = 0x06;
public const byte ErrorMask = 0x80;
public const byte WriteMultipleCoilsFunctionNumber = 0x0F;
#endregion Error/Exception
public const byte WriteMultipleRegistersFunctionNumber = 0x10;
#region Protocol limitations
public const int MinDeviceId = 0x0000;
......@@ -54,5 +47,7 @@ namespace Modbus.Tcp.Utils
public const int MaxRegisterCountRead = 0x7D; // 125
public const int MaxRegisterCountWrite = 0x7B; // 123
#endregion Protocol limitations
}
}
......@@ -8,6 +8,12 @@ namespace Modbus.Tcp.Utils
/// </summary>
internal class DataBuffer
{
#region Fields
private static readonly DateTime UnixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
#endregion Fields
#region Constructors
/// <summary>
......@@ -281,7 +287,7 @@ namespace Modbus.Tcp.Utils
public void SetDateTime(int index, DateTime value)
{
var dt = value.ToUniversalTime();
var ts = value.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc));
var ts = value.Subtract(UnixEpoch);
SetTimeSpan(index, ts);
}
......@@ -480,7 +486,7 @@ namespace Modbus.Tcp.Utils
public void AddDateTime(DateTime value)
{
var dt = value.ToUniversalTime();
var ts = value.Subtract(new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc));
var ts = value.Subtract(UnixEpoch);
AddTimeSpan(ts);
}
......@@ -703,7 +709,7 @@ namespace Modbus.Tcp.Utils
public DateTime GetDateTime(int index, bool localTime = false)
{
var ts = GetTimeSpan(index);
var dt = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).Add(ts);
var dt = UnixEpoch.Add(ts);
return localTime ? dt.ToLocalTime() : dt;
}
......@@ -811,15 +817,16 @@ namespace Modbus.Tcp.Utils
public override string ToString()
{
var sb = new StringBuilder();
sb.AppendLine($"Buffer, Length = {Length}, LittleEndian = {IsLittleEndian}");
sb.AppendLine($"DataBuffer | Length: {Length} Bytes | LittleEndian: {IsLittleEndian}");
sb.Append(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
for (int i = 0; i < Length; i++)
{
if (i % 16 == 0)
{
sb.AppendLine();
sb.Append(i.ToString("X4"));
sb.Append(":");
sb.Append("0x" + i.ToString("X4"));
sb.Append(" ");
}
var hex = Buffer[i].ToString("X2");
sb.Append($" {hex}");
......
using System;
namespace Modbus.Tcp.Utils
namespace Modbus.Tcp.Utils
{
internal enum MessageType
{
......@@ -9,4 +7,16 @@ namespace Modbus.Tcp.Utils
WriteSingle,
WriteMultiple
}
}
internal enum FunctionCode : byte
{
ReadCoils = 0x01,
ReadDiscreteInputs = 0x02,
ReadHoldingRegisters = 0x03,
ReadInputRegisters = 0x04,
WriteSingleCoil = 0x05,
WriteSingleRegister = 0x06,
WriteMultipleCoils = 0x0F,
WriteMultipleRegisters = 0x10
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment