Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FluentModbus.ModbusException: 'The protocol identifier is invalid.' #71

Open
kirillivan0ff opened this issue May 30, 2022 · 15 comments
Open

Comments

@kirillivan0ff
Copy link

kirillivan0ff commented May 30, 2022

Seems like a
var unitIdentifier = 0xFF;
giving this error when I using your examples.

The device manual saying:
"INPUT DATA AREA
The following table lists the registers of the input area (produced from the instrument and read by the
master), common to all PROFINET, ETHERCAT, ETHERNET/IP fieldbuses. The registers are 16 bit in size.
The input area is updated at a maximum frequency of 125 Hz (80 Hz in case of FIELBUS). The size
of the output area configured in the master fieldbus must match the size configured in the instrument"
Which seems to be OK. 16bit."

EasyModbus library reading registers but I have other issues with it.

Any suggestions?

@Apollo3zehn
Copy link
Owner

Is it a Modbus TCP or RTU problem? Can I reproduce it somehow?

From the error message it seems that the device is sending an incorrect response header but that is difficult to say.

@kirillivan0ff
Copy link
Author

Is it a Modbus TCP or RTU problem? Can I reproduce it somehow?

From the error message it seems that the device is sending an incorrect response header but that is difficult to say.

It's TCP. Trying to speak with this guy and not really successful. Not sure if you can reproduce it but if I can check the header somehow I'll be glad to try it.

@Apollo3zehn
Copy link
Owner

Apollo3zehn commented May 30, 2022

You could add a breakpoint to that line:

if (protocolIdentifier != 0)

And then see what data the routine received and compare it to the Modbus spec. Alternatively you could try Wireshark to capture the Modbus traffic.

@kirillivan0ff
Copy link
Author

kirillivan0ff commented May 30, 2022 via email

@Apollo3zehn
Copy link
Owner

8224 is not correct but I do not know why. I am sure Wireshark will help.

@kirillivan0ff
Copy link
Author

8224 is not correct but I do not know why. I am sure Wireshark will help.

OK, I think you can close this one for now. I will ping you up if I will find something interesting.

@kirillivan0ff
Copy link
Author

kirillivan0ff commented Jun 1, 2022 via email

@Apollo3zehn
Copy link
Owner

You mean the value 8224? No it does not really make sense to me. Do you have a Wireshark screenshot or log of the full response of your device?

@fred777
Copy link

fred777 commented Aug 2, 2023

Hi! I just run into the same issue and I have absolutely no clue why, see attached sample code which is being compiled with VS2022 for .NET Framework 4.8 x64 on Win10 and FluentModbus 5.0.2. The first block of code will run without any issues, but the second one - which does the very same thing but encapsulated within a class - will always fail after some delay with FluentModbus.ModbusException: 'The protocol identifier is invalid.'

(and it will also fail if I just remove the first code block....)

namespace ModbusTest
{
    using System;
    using System.Net;
    using System.Threading.Tasks;
    using FluentModbus;   

    internal class Program
    {
        static async Task Main(string[] args)
        {
            // this blocks runs without a flaw
            {
                var client = new ModbusTcpClient();
                client.Connect(IPAddress.Parse("192.168.4.25"), ModbusEndianness.BigEndian);

                if (!client.IsConnected) throw new Exception("not connected!");

                var x = (await client.ReadInputRegistersAsync<float>(0, 1000, 1)).Span[0];
                if (x != 123.456f) Console.WriteLine($"Endianness does not seem to be correct! {x}");

                var version = await client.ReadInputRegistersAsync<short>(0, 1002, 2);
                var serial = await client.ReadInputRegistersAsync<int>(0, 1006, 1);

                Console.WriteLine(
                    $"Found device: serial {serial.Span[0]}, firmware {version.Span[1]}, hardware {version.Span[0]}");
                client.Disconnect();
            }

            // this blocks always fails after some delay with FluentModbus.ModbusException: 'The protocol identifier is invalid.'
            {
                var client = new ModbusInterface();
                client.Connect(IPAddress.Parse("192.168.4.25"));

                if (!client.IsConnected) throw new Exception("not connected!");
                var info = await client.GetInfo();
                Console.WriteLine($"Found device: {info}");
            }
        }
    }
	
	internal class ModbusInterface
    {
        public async void Connect(IPAddress ipAddress)
        {
            Disconnect();
            _client = new ModbusTcpClient();
            _client.Connect(ipAddress, ModbusEndianness.BigEndian);
            Console.WriteLine("Connected.");
            {
                const float expected = 123.456f;
                var actual = (await _client.ReadInputRegistersAsync<float>(UnitIdentifier, 1000, 1)).Span[0];
                if (actual != expected)
                {
                    throw new Exception($"Endianness does not seem to be correct! (got {actual} but expected {expected}");
                }
            }
            Console.WriteLine("Verified.");
        }

        public void Disconnect()
        {
            if (_client == null) return;
            Console.WriteLine("Disconnecting...");
            _client.Disconnect();
            _client = null;
        }

        public bool IsConnected
        {
            get
            {
                if (_client == null) return false;
                return _client.IsConnected;
            }
        }

        public async Task<string> GetInfo()
        {
            if (!IsConnected) return "not connected!";

            // this always fails after some delay with FluentModbus.ModbusException: 'The protocol identifier is invalid.'
            var version = await _client.ReadInputRegistersAsync<short>(UnitIdentifier, 1002, 2);
            var serial = await _client.ReadInputRegistersAsync<int>(UnitIdentifier, 1006, 1);

            return $"serial {serial.Span[0]}, firmware {version.Span[1]}, hardware {version.Span[0]}";
        }


        private ModbusTcpClient _client;
        private const int UnitIdentifier = 0;
    }
}

@karateboy
Copy link

I ran into the same situation.
I also take the aysnc/await usage.
I use it for device calibration. In this case, dely during between reading/writing is very common practice.

@karateboy
Copy link

@Apollo3zehn Please mark this issue as open. I may take sometime study how to fix it.

@Apollo3zehn Apollo3zehn reopened this Feb 8, 2024
@karateboy
Copy link

It is common to use a modbus gateway to map Modbus RTU devices into Modbus TCP registers.
Since they are in fact different devices, my application are using different tasks with individual modbus clients to read/write those Modbus registers/coils.

I found that without locking the read/write may fail randomly. (after enforcing SemaphoreSlim locking, this kind of error disappear) I don't think it a bug. However, if the library can maintain a semaphore for the modbus device (ID is IP + slave ID), it can save a lot of time for people like me to figure out what went wrong.

As for the 'The protocol identifier is invalid.' error, I suspect it may result from my "configureAwit(false)". I just chain all my assync modbus actions into "configureAwit(true)" to see if the error disappear. It may take some time to test.

@Apollo3zehn
Copy link
Owner

It would be great if you create a pull request or show me some code to illustrate the required changes :-) Thank you

@karateboy
Copy link

karateboy commented Feb 25, 2024

I wish I can provide you a pull request but I don't have time to prepare it and fully tested.
So instead, I provide you my workaround for youre reference.

I hava a wrapper class called ModbusClient which encapsulate your ModbusTcpClient.
ModbusClient has a static ConcurrentDictionary storing pairs of semaphore and IP/Port and a internal semaphore reference.

public class ModbusClient : IDisposable
{
private record ModbusTarget(IPAddress Address, int Port);
private static readonly ConcurrentDictionary<ModbusTarget, SemaphoreSlim> TargetLockMap = new();
private readonly SemaphoreSlim _targetLock;
...
}

In the constructor, It try to get/add the semaphore from/to the concurrent map. Thus, for modbus clients of the same IP, they will share the same semaphore.

public ModbusClient(Device device...)
{
...
var modbusTarget = GetModbusTarget(device); // Generate record according to device IP and port
TargetLockMap.TryGetValue(modbusTarget, out var targetLock);
_targetLock = targetLock ?? TargetLockMap.GetOrAdd(modbusTarget, new SemaphoreSlim(1, 1));
}

For each modbus read/write operation, e.g. read coil. ModbusClient has to wait for the semaphore before the operation.

public async Task<List<(DeviceSignalIo.IDeviceSignal, bool)>> ReadSignalAsync(CancellationToken cancellationToken){
try
{
await _targetLock.WaitAsync(cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();

                var coilMemory =
                    await client.ReadCoilsAsync((byte)device.SlaveId, (ushort)signal.Address + signal.Offset, 1,
                        cancellationToken);
    }
    catch (Exception ex)
    {

    }
    finally
    {
        _targetLock.Release();
    }
    ....

}

For async task, it is possible different tasks executing on the same thread may read/write modbus master at the same time. Without a semaphore, multiple read/write may try to access the underlying network stream at the same time. A Semaphore(1, 1) ensure only one operaton can be performed until the previous result come back.

In contrast, for disk or other high throughput devices, they support outstanding I/O, which means the master can issue multiple requests before the previous result return. However, for modbus devices, this kind of operation is not supported.

@karateboy
Copy link

Here is my scenario, I hava multiple timely tasks to reading modbus devies data at the same time. Also I have a task to perform the calibration of the modbus devices. Each task has its onw ModbusClient and may operate at the same time.
By the way, after I add those logics, the protocol identifier invalid disappear.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants