rossmann-engineering / EasyModbusTCP.NET

Modbus TCP, Modbus UDP and Modbus RTU client/server library for .NET implementations
922 stars 396 forks source link

[Issue] System.UnauthorizedAccessException #81

Open NintendoPlayer opened 2 years ago

NintendoPlayer commented 2 years ago

Hi!

I've been using the library in a project. I'm connecting to Modbus RTU flow meters. First I designed the app for one type of them. Now I'm adding a second one.

I wrote a function that test the flow meter connected to guess the type. I read certain registers that are unique of each type and the return the type of the meter. The problem I'm getting is this

` public static KeyValuePair<PingResult, MeterType> TestEquipmentExistsAndGetType(string serialPort, int baudRate = 9600, int unitIdentifier = 1, bool newClient = true, bool disconnectAtEnding = true) { PingResult test = PingResult.Error;

foreach (MeterType i in Enum.GetValues(typeof(MeterType)))

        {

            PingResult test = TestEquipmentExists(serialPort, i, baudRate, unitIdentifier);

            Thread.Sleep(100);

            if (((int)test) < 1)
            {
                test = PingResult.Ok;
                return new KeyValuePair<PingResult, MeterType>(test, i);
            }
        }

`

`public static PingResult TestEquipmentExists(string serialPort, MeterType meterType = MeterType.TypeOne, int baudRate = 9600, int unitIdentifier = 1, bool newClient = true, bool disconnectAtEnding = true) { PingResult result = PingResult.Error; try { if (newClient) { NewStandardClient(serialPort, baudRate, unitIdentifier); client.ConnectionTimeout = 1000; client.NumberOfRetries = 2; client.Connect(); }

            int[] respInstFlow = new int[2];
            switch (meterType)
            {
                case MeterType.TypeOne:
                    respInstFlow = ReadVariable(client, TypeOne.Variables.BASE_ADDRESS, 2);
                    break;

                case MeterType.TypeTwo:
                    respInstFlow = ReadVariable(client, TypeTwo.Parameters.BASE_ADDRESS, 2);
                    break;
            }

            if (respInstFlow[0] == -9876 && respInstFlow[1] == -1234)
            {
                throw new Helper.Exceptions.TimeoutException();
            }

            result = PingResult.Ok;
        }
        catch (Helper.Exceptions.TimeoutException ex)
        {
            result = PingResult.Timeout;
        }
        catch (UnauthorizedAccessException ex)
        {
            result = PingResult.PortInUse;
        }
        finally
        {
            if (disconnectAtEnding)
                Disconnect(true);
        }
        return result;
    }`

public static void Disconnect(bool destroy = false, bool disconnectReadings = true) { string serialPort = client.SerialPort; if (disconnectReadings) { FlagAllowVariableReadings = false; flagAllowParameterReadings = false; } client.Disconnect(); if (destroy) client = null; }

Ok, so this is what I try:

I'm connected to a TypeTwo flowmeter, so:

What the real problem is; When I'm trying to read certain address on the TypeTwo meter, ReadInputRegister throws an EasyModbus.Exceptions.CRCCheckFailedException("Response CRC check failed") or an EasyModbus.Exceptions.StartingAddressInvalidException("Starting address invalid or starting address + quantity invalid"). TypeTwo meter only wants you to read the addresses in a certain fashion, otherwise it throws this error.

The thing is that, if I put a breakpoint in between the two iterations and wait enough time, like 30 seconds or so, the result is as expected. I don't know if this is something related to the meter, like it get's blocked when I try an invalid address, or it has something to do with the code. I know it's a bit convoluted, but maybe someone has a clue.

Thank you!

P.D: Please, fix the code, I can format it correctly :(

Padanian commented 2 years ago

I don't know if this is related to your meters, but here's an hint. Battery-operated flow meters need to save energy, so their communication capability is budgeted to a max number of queries per day or a max frequency of queries. There might be a delay required by the meter before the next query, after the first one. If this is true, skip the first query in debug, and try the second iteration only. Come back to us with the result. I might be wrong, but I've had some experience with flow meters before.

NintendoPlayer commented 2 years ago

I don't know if this is related to your meters, but here's an hint. Battery-operated flow meters need to save energy, so their communication capability is budgeted to a max number of queries per day or a max frequency of queries. There might be a delay required by the meter before the next query, after the first one. If this is true, skip the first query in debug, and try the second iteration only. Come back to us with the result. I might be wrong, but I've had some experience with flow meters before.

Hi @Padanian! Thanks for the help! The thing is that, if I connect directly to the meter, testing it without the first iteration, the connection is flawless as you say. The thing is that after the connection, I read an array of 20 registers each 500 ms and the communications works without an issue, so it must be another thing.

EDIT: Sorry, you try to say that, if the first reading is not right, the meter just waits longer to be able to read again from it?

Padanian commented 2 years ago

What I'm saying is that doesn't matter how many registers you read. The power consumption is not on the data transmission (or let's say there's a very little power consumption). Most of the power goes into polarising the wires, so when the connection is dropped (after first iteration), it might be that the meter wants you to wait before the next session. Is the flow meter battery-operated? If yes, we might be on the right track. Please be gentle on batteries, as your customer might end up with a flow meter that instead of lasting 7-10 years on batteries, might need replacement after 4-6 years, well below the projected life time.

NintendoPlayer commented 2 years ago

This won't be a problem because the meters are not battery operated. What I did and, in fact work, is swithc the order of the enum, so that it first tries to check if the meter is TypeTwo insted of TypeOne. So after the two iterations, the function returns the correct type and the reading is correct.

I will try to test to connect to a TypeOne meter and do the same, iterate first with TypeTwo and second with TypeOne and see if the other meter throws the same System.UnauthorizedAccessException. If not, I will be that TypeTwo meter just wants you to wait before you try a reading after you tried reading an unathorized register. This meter doesn't like you tu read a register, it wants you to read a pair of them and with the right index. If not it throws an error, even with other test software.

I will try and update.