Beckhoff / TF6000_ADS_DOTNET_V5_Samples

Sample code for the Version 6.X series of the TwinCAT ADS .NET Packages
https://infosys.beckhoff.com/content/1033/tc3_ads.net/9407515403.html?id=6770980177009971601
BSD Zero Clause License
37 stars 15 forks source link

Connection.WriteAsync not working #30

Closed drvic10k closed 2 years ago

drvic10k commented 2 years ago

with the AdsSymbolicServer, I cannot use Connection.WriteAsync, I had to replace the call with Connection.WriteValueAsync

is there some other method I need to override to make WriteAsync work? I am overriding protected override AdsErrorCode OnWriteRawValue(ISymbol symbol, ReadOnlySpan<byte> span)

RalfHeitmann commented 2 years ago

Please give more information which overload of WriteAsync you are using because there are several paramater sets. Another important information what type of instance you want to write and the addressing (PrimitiveType, ComplexType). What is the system reaction? Is the call just ignored or do you get an Exception? Is OnWriteRawValue called in this situation?

drvic10k commented 2 years ago

I am using this method:

public async Task<ResultWrite> WriteAsync(
      uint indexGroup,
      uint indexOffset,
      ReadOnlyMemory<byte> writeBuffer,
      CancellationToken cancel)

when writing one symbol I get this exception, it's a boolean value: Parameter size is incorrect. (AdsErrorCode: 1797, 0x705) but with Connection.WriteValueAsync it works

another boolean value works with both methods

RalfHeitmann commented 2 years ago

The relevant implementation part in the SymbolServer base class could be this one here:


protected virtual Task<ResultWrite> OnWriteIgIoAsync(AmsAddress sender, uint invokeId, uint indexGroup, uint indexOffset, ReadOnlyMemory<byte> writeData, CancellationToken cancel)
        {
            AdsErrorCode errorCode = AdsErrorCode.DeviceServiceNotSupported;

            SymbolIterator iter = new SymbolIterator(this.symbols, true);
            IAdsSymbol? symbol = iter.Cast<IAdsSymbol>().FirstOrDefault(s => s.IndexGroup == indexGroup && s.IndexOffset == indexOffset);

            if (symbol != null)
            {
                if (writeData.Length == symbol.GetValueMarshalSize())
                {
                    byte[] data = new byte[symbol.GetValueMarshalSize()];

                    try
                    {
                        errorCode = OnWriteRawValue(symbol, writeData.Span);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                        errorCode = AdsErrorCode.InternalError;
                    }
                }
                else
                {
                    errorCode = AdsErrorCode.DeviceInvalidSize;
                }
            }
            return Task.FromResult(new ResultWrite(errorCode,invokeId));
        }

Could it be that the IndexGroup, IndexOffset combination returns a symbol that is ambigous or doesn't return the boolean symbol you expect?

Edit: Changed the Read method to Write

drvic10k commented 2 years ago

I tried to override the method and set a breakpoint there, it doesn't hit it before the exception is thrown

RalfHeitmann commented 2 years ago

another boolean value works with both methods

I really don't know where this issue should come from. It must have to do with the Symbol/Value: If I add the following code into the AdSymbolicServer program.cs file (behind registering all Notifications):

 // Receiving notifications on all Symbols
                        SymbolIterator iter = new SymbolIterator(symbols,true);
                        IList<ISymbol> allSymbols = iter.ToList();
                        disposables = ReceiveNotifications(session,allSymbols);

                       // Add this part for WriteAsync usage
                        IAdsSymbol b1 = (IAdsSymbol)symbols["Main.bool1"];
                        IAdsSymbol b2 = (IAdsSymbol)symbols["Globals.bool1"];
                        byte[] bytes = new byte[1];

                        for (int i = 0; i < 10; i++)
                        {
                            await Task.Delay(TimeSpan.FromSeconds(1.0));
                            bytes[0] = (bytes[0] == (byte)1) ? (byte)0 : (byte)1;
                            await session.Connection.WriteAsync(b1.IndexGroup, b1.IndexOffset, bytes.AsMemory(), CancellationToken.None);
                            await session.Connection.WriteAsync(b2.IndexGroup, b2.IndexOffset, bytes.AsMemory(), CancellationToken.None);
                        }

I even get the Ads Notifications from the Value toggling properly. Also I don't see the potential for an DeviceInvalidSize return code other than described above (picking up the wrong symbol or Incorrect designed Symbol from IndexGroup/IndexOfset, or MemorySpan.Length != 1. Relevant is here the ByteSize of the Symbol (should be 1 for an boolean), and that WriteAsync gets the correct buffer size (also 1-byte).

RalfHeitmann commented 2 years ago

when writing one symbol I get this exception, it's a boolean value: Parameter size is incorrect. (AdsErrorCode: 1797, 0x705) but with Connection.WriteValueAsync it works

another boolean value works with both methods

Is the problem still reproducable on your side?

drvic10k commented 2 years ago

yes, it is reproducible, I will try do some more investigation today to see what is the difference between those two values

drvic10k commented 2 years ago

so delving deeper, I found out, that the result of the operation is DeviceInvalidSize

can this be cause by the way the subsymbols are mapped?

image

here you can see that the struct has the same offset as the field

and I am trying to write the value like this: Connection.WriteAsync(symbol.IndexGroup, symbol.IndexOffset, memory, cancel), offset is 4154, allocated memory is one byte (4154 dec = 103A hex)

when writing the next value (103B), it works fine

same problem happens when reading

drvic10k commented 2 years ago

I tried to follow the code execution of this method, because it can write the symbol correctly, to see the difference:

public async Task<ResultWrite> WriteValueAsync(
      ISymbol symbol,
      object val,
      CancellationToken cancel)

there is a strange thing happening, this symbol image is transformed to this: image

in this method:

public async Task<ResultWrite> WriteValueAsync(
      string name,
      object value,
      CancellationToken cancel)

and then with this different index group and offset it somehow correctly arrives in the simulator with the original group and offset

RalfHeitmann commented 2 years ago

That explains the problem. There is a side effect in the AdsClient-side cached ISymbols when you use the "ReadValueXXX" and "WriteValueXXX" overloads (Changing the IndexGroup to IndexGroupSymbolAccess.ValueByHandle and the Handle as IndexOffset). This one never caused trouble with other AdsServers - but with the specific SymbolicServer implementation. I need to have a look how to change this - but that would need a little bit of work, it's not an easy one ...

RalfHeitmann commented 2 years ago

Please try out the 6.0.190 ...

drvic10k commented 2 years ago

now it works, thanks