libplctag / libplctag.NET

A .NET wrapper for libplctag.

Home Page:https://libplctag.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to read/write strings

Aman1999isak opened this issue · comments

Hi,

I am trying to retrieve a string from my micro800 Allen bradley PLC. I am getting this fault.
image
I am not using string[] on the definition, so why do i get this error. Is it becouse the library dosent support this??
Bool,int work!
Thanks for the help. I will keep trying, but please let me know if it is not supported.

here is what i have on the micro 800. The string is on the global variables.
image

Hello, did you get this solved? Looks like I am having the same issue

I have the same problem, I half solved it, the way I used to solve it was that instead of using StringPlcMapper I am using a SintPlcMapper expecting an array of type sbyte[].

Actually I can Read any String but I can not Write a String that is longer than the one written in the PLC, that is if I write a word of 10 letters I can write a word between 1 to 10 letters, but if I write a word of 8 now I am limited again to write a word of 8 or less, with the ErrorOutOfBounds error.

I sincerely believe that the main problem is a Bug in the library to read this type of Strings since it is not really a String, the Micro820 handles it as an Int8 Array where the first position is the length of the stored word, that is if I have a word of 4 letters I will get an array of 5 where in the first position is stored the length of the String and in the other 4 the encoding of the String in ASCII.

IIRC, Micro8x0 uses a different string format from Control/CompactLogix. Some PLCs send the entire string buffer from the PLC. Some only send the "used" bytes. It looks like the Micro8x0 only sends the used bytes. You will need to increase the size of the tag to write a longer string.

The underlying C library makes sure that the local buffer (in the PC) has enough space for the data returned from the PLC. If you read a string with 4 characters, that will be 5 bytes for a Micro8x0. If you want to write a longer string, you will need to resize the local tag's buffer. In the underlying C library, you can call specific functions to do this. I am not sure what the .Net methods are but they are probably roughly the same names and hopefully the auto-complete feature of your IDE will figure it out.

Hi Kyle.

Yes, the method to create a tag in the wrapper has a parameter called ArrayDimensions that you can predefine it or it autocalculates if you send a null, but the autocalculation or change the dimension in this case that is the String in this controller does not work, in any other type of variable works correctly, only when manipulating strings in the Micro820 is not possible, it may be more a matter of the wrapper than the native library.

Do you have an example of how to read a string from the native function in a Micro820 PLC? I have been trying to read it but I get an error when creating the tag, I think I am not understanding how to use the native library from C# for the Micro820.

@ElegantCatterpillar

The way types are encoded in C#/.NET memory is (often) different to how they are encoded in PLC memory.
The .NET wrapper (libplctag.NET) abstracts this away by offering the Mapper system in addition to the basic Tag class, but there are gaps for these because we don't know how all of the controllers encode and communicate tag data for the common types.

There is an example here of using the NativeImport library from C#
https://github.com/libplctag/libplctag.NET/blob/master/src/Examples/CSharp%20NativeImport/NativeImportExample.cs

and more examples of using the String APIs in C
https://github.com/libplctag/libplctag/blob/release/src/examples/string_standard.c
https://github.com/libplctag/libplctag/blob/release/src/examples/test_string.c

When libplctag first initializes a tag, it performs a read from the PLC, which allows libplctag to create a buffer in memory to store the data.
For other controllers (e.g. the ControlLogic range) - the data that is read from the PLC, is enough to hold a string up to its maximum length.
It seems like for the Micro820 that is not the case, the data coming back from the PLC is only enough to hold the current value.

When you want to write a string that is larger than its first value, you'll first need to resize that buffer using SetSize.

This is not there in the built-in StringMapper, but you could develop your own where you do actually resize the buffer before calling SetString - e.g.

MyStringMapper : PlcMapperBase<string>
{

  ....
  ...

  override public void Encode(Tag tag, int offset, string value)
  {
    tag.SetSize(offset, value.Length + 2?);
    tag.SetString(offset, value);
  }
}

I don't know if this is the logic that is needed - I don't know how strings are encoded in the Micro820.

ArrayDimensions is used for an array of strings (or DINT/SINT/TIMER/etc),
i.e. 10 strings - as opposed to 1 string with max length 10.

I am looking through the C code and I thought I had broken out the Micro8x0 separately from the Logix PLCs. But it looks like I did not. Can you set the following (these are the C tag string attributes, so you'll need to translate them for .Net)?

str_is_counted = 1

str_is_fixed_length = 0

str_is_byte_swapped = 0 (? this might be true or 1, need to test)

str_count_word_bytes = 1

str_max_capacity = 255

str_total_length = 0

str_pad_bytes = 0

These attributes are set when you create a Tag object.

This might allow strings to work out-of-the-box.

Okidoke - unfortunately the Mapper based tag type does not yet have the ability to configure a tag prior to initialization (#212) - so will need to use the Tag type.

That would look something like this:

var myString = Tag()
{
  Gateway = "127.0.0.1",
  Name = "MyString",
  ... other parameters ...
  
  StringIsCounted = true,
  StringIsFixedLength = false,
  StringIsByteSwapped = false, // this might be true, need to test
  StringCountWordBytes = 1,
  StringMaxCapacity = 255,
  StringTotalLength = 0,
  StringPadBytes = 0,
};

await myString.InitializeAsync();
// myString.SetSize(???);
myString.SetString(0, "Hello World");
await myString.WriteAsync();

Thank you very much @timyhac and @kyle-github for your time and information shared.

I will keep testing to see if I find a stable solution on how to read it and I will share here the progress whether it is good or bad.

Best regards.

Based on the code, I think you will need to set the size of the tag. There is a check that looks to see what the maximum size is and what the index of the last character in the string is. The last index is compared to the size of the existing tag buffer not the maximum possible size of the string.

How are you doing, today I was testing my work using your recommended information, from the SetSize and SetString function I could never write the Tag, it returned an error with an answer of the current string length, although they did not work that way after some tests I solved it using the SetBuffer in the following way:

      public async Task WriteStringDirectAsync(string tagName, string value)
      {
          var myString = new Tag()
          {
              Gateway = IpDispositivo,
              Name = tagName,
              Timeout = TimeSpan.FromMilliseconds(TimeOut),
              Protocol = Protocol.ab_eip,
              UseConnectedMessaging = true,
              PlcType = PlcType.Micro800,
              StringIsCounted = true,
              StringIsFixedLength = false,
              StringIsByteSwapped = false,
              StringCountWordBytes = 1,
              StringMaxCapacity = 255,
              StringTotalLength = 0,
              StringPadBytes = 0,
          };

          await myString.InitializeAsync();

          byte[] stringBytes = Encoding.ASCII.GetBytes(value);
          byte[] byteArray = new byte[value.Length + 1];
          byteArray[0] = (byte)value.Length;
          Array.Copy(stringBytes, 0, byteArray, 1, stringBytes.Length);

          myString.SetBuffer(byteArray);
          await myString.WriteAsync();
      }

Thank you very much for your help @kyle-github @timyhac .
If you need any additional information, please contact me.

ElegantCatterpillar

Can I use the myString Initialized by the StringMapper or i need to perform

var myString = new Tag()
          {
              Gateway = IpDispositivo,
              Name = tagName,
              Timeout = TimeSpan.FromMilliseconds(TimeOut),
              Protocol = Protocol.ab_eip,
              UseConnectedMessaging = true,
              PlcType = PlcType.Micro800,
              StringIsCounted = true,
              StringIsFixedLength = false,
              StringIsByteSwapped = false,
              StringCountWordBytes = 1,
              StringMaxCapacity = 255,
              StringTotalLength = 0,
              StringPadBytes = 0,
          };

I will test it to omron PLC

num = -7 (BadConfig) (This when i run WriteStringDirectAsync func, but in sync )

public void Initialize()
{
	ThrowIfAlreadyDisposed();
	ThrowIfAlreadyInitialized();
	int timeout = (int)Timeout.TotalMilliseconds;
	SetUpEvents();
	string attributeString = GetAttributeString();
	int num = _native.plc_tag_create_ex(attributeString, coreLibCallbackFuncExDelegate, IntPtr.Zero, timeout);
	if (num < 0)
	{
		throw new LibPlcTagException((Status)num);
	}
	nativeTagHandle = num;
	_isInitialized = true;
}

I overrided StringPlcMapper by inserting Omron's maximum size (256) into ElementSize, while in the encode I added the solution that seemed to work for @ElegantCatterpillar but not for me.

public class StringPlcMapper : PlcMapperBase<string>
{
    public override int? ElementSize => base.PlcType switch
    {
        PlcType.ControlLogix => 88,
        PlcType.Plc5 => 84,
        PlcType.Slc500 => 84,
        PlcType.LogixPccc => 84,
        PlcType.Micro800 => 256,
        PlcType.MicroLogix => 84,
        PlcType.Omron => 256,
        _ => throw new NotImplementedException(),
    };

    public override string Decode(Tag tag, int offset)
    {
        return tag.GetString(offset);
    }

    public override void Encode(Tag tag, int offset, string value)
    {
        Exception sizeEx, WriteEx;

        int sizePre, sizePost;
        try
        {
            byte[] stringBytes = Encoding.ASCII.GetBytes(value);

            byte[] byteArray = new byte[value.Length + 1];

            byteArray[0] = (byte)value.Length;

            Array.Copy(stringBytes, 0, byteArray, 1, stringBytes.Length);

            tag.SetBuffer(byteArray);

            sizePre = tag.GetSize();

            try
            {
                tag.SetSize(value.Length + 2);
            }
            catch (Exception ex)
            {
                sizeEx = ex;
            }

            sizePost = tag.GetSize();

            tag.SetString(offset, value);
        }
        catch (Exception ex)
        {
            WriteEx = ex;
        }
    }
}

image

First of all I don't understand why tag.SetSize(value.Length + 2) returns an error, it should return the value of the previous size, but it returns in the form of an error, I think the code is wrong, because it always returns a value other than 0, which is intended to be non-error.

image

image

After setting the size, if you read the size it appears that it has been set, but in reality it is not, and this is demonstrated by the exception, and also by the tag_rw command

tag_rw2 --type=uint8 '--tag=protocol=ab-eip&gateway=192.168.1.10&path=1,0&plc=omron-njnx&name=ZIOPAPERONE' --debug=4 > log.txt

Library version 2.5.4.
Processing argument 1 "--type=uint8".
Processing argument 2 "--tag=protocol=ab-eip&gateway=192.168.1.10&path=1,0&plc=omron-njnx&name=ZIOPAPERONE".
Processing argument 3 "--debug=4".
data[0]=0 (0)
data[1]=0 (0)

If I initialize the tag as ElegantCaterpillar, but modifying StringMaxCapacity and StringCountWordBytes, adapted to Omron, it still gives me an error:

Tag tag = new()
{
    Gateway = 127.0.0.1,
    Name = itemModel.ItemID,
    Timeout = TimeSpan.FromMilliseconds(configuration.ItemTimeOut),
    Protocol = (Protocol)configuration.Protocol,
    UseConnectedMessaging = false,
    PlcType = Omron,
    StringIsCounted = true,
    StringIsFixedLength = false,
    StringIsByteSwapped = false,
    StringCountWordBytes = 2,
    StringMaxCapacity = 256,
    StringTotalLength = 0,
    StringPadBytes = 0,
};

num = -7 (BadConfig)

public void Initialize()
{
	ThrowIfAlreadyDisposed();
	ThrowIfAlreadyInitialized();
	int timeout = (int)Timeout.TotalMilliseconds;
	SetUpEvents();
	string attributeString = GetAttributeString();
	int num = _native.plc_tag_create_ex(attributeString, coreLibCallbackFuncExDelegate, IntPtr.Zero, timeout);
	if (num < 0)
	{
		throw new LibPlcTagException((Status)num);
	}
	nativeTagHandle = num;
	_isInitialized = true;
}
tag_rw2 --type=uint8  '--tag=protocol=ab_eip&gateway=192.168.1.10&plc=omron-njnx&name=PIPPO&use_connected_msg=0&str_count_word_bytes=2&str_is_byte_swapped=0&str_is_counted=1&str_is_fixed_length=0&str_is_zero_terminated=0&str_max_capacity=256&str_pad_bytes=0&str_total_length=0'  --debug=4 > log.txt

2024-03-06 10:28:39.641 thread(1) tag(0) INFO plc_tag_create_ex:833 Starting
2024-03-06 10:28:39.642 thread(1) tag(0) INFO initialize_modules:177 Starting.
2024-03-06 10:28:39.642 thread(1) tag(0) INFO initialize_modules:186 Creating library mutex.
2024-03-06 10:28:39.643 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.643 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.643 thread(1) tag(0) INFO initialize_modules:207 Initialized library modules.
2024-03-06 10:28:39.643 thread(1) tag(0) INFO lib_init:135 Setting up global library data.
2024-03-06 10:28:39.643 thread(1) tag(0) INFO lib_init:137 Creating tag hashtable.
2024-03-06 10:28:39.643 thread(1) tag(0) INFO hashtable_create:75 Starting
2024-03-06 10:28:39.643 thread(1) tag(0) INFO hashtable_create:99 Done
2024-03-06 10:28:39.643 thread(1) tag(0) INFO lib_init:143 Creating tag hashtable mutex.
2024-03-06 10:28:39.644 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.644 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.644 thread(1) tag(0) INFO lib_init:149 Creating tag condition variable.
2024-03-06 10:28:39.644 thread(1) tag(0) DETAIL cond_create:1087 Starting.
2024-03-06 10:28:39.644 thread(1) tag(0) DETAIL cond_create:1114 Done.
2024-03-06 10:28:39.644 thread(1) tag(0) INFO lib_init:155 Creating tag tickler thread.
2024-03-06 10:28:39.644 thread(1) tag(0) DETAIL thread_create:884 Starting.
2024-03-06 10:28:39.645 thread(1) tag(0) DETAIL thread_create:917 Done.
2024-03-06 10:28:39.645 thread(1) tag(0) INFO lib_init:161 Done.
2024-03-06 10:28:39.645 thread(1) tag(0) INFO initialize_modules:210 Initializing AB module.
2024-03-06 10:28:39.645 thread(2) tag(0) INFO tag_tickler_func:508 Starting.
2024-03-06 10:28:39.645 thread(1) tag(0) INFO ab_init:123 Initializing AB protocol library.
2024-03-06 10:28:39.645 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.645 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.645 thread(1) tag(0) INFO ab_init:132 Finished initializing AB protocol library.
2024-03-06 10:28:39.645 thread(1) tag(0) INFO initialize_modules:215 Initializing Modbus module.
2024-03-06 10:28:39.645 thread(1) tag(0) INFO mb_init:2410 Starting.
2024-03-06 10:28:39.645 thread(1) tag(0) DETAIL mb_init:2412 Setting up mutex.
2024-03-06 10:28:39.645 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.646 thread(1) tag(0) INFO mb_init:2421 Done.
2024-03-06 10:28:39.646 thread(1) tag(0) INFO initialize_modules:226 Done initializing library modules.
2024-03-06 10:28:39.646 thread(1) tag(0) INFO initialize_modules:231 Done.
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL attr_create_from_str:131 Starting.
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "protocol=ab_eip".
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "protocol":"ab_eip".
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "protocol":"ab_eip".
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "gateway=192.168.1.10".
2024-03-06 10:28:39.646 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "gateway":"192.168.1.10".
2024-03-06 10:28:39.647 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "gateway":"192.168.1.10".
2024-03-06 10:28:39.647 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "plc=omron-njnx".
2024-03-06 10:28:39.647 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "plc":"omron-njnx".
2024-03-06 10:28:39.647 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "plc":"omron-njnx".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "name=PIPPO".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "name":"PIPPO".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "name":"PIPPO".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "use_connected_msg=0".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "use_connected_msg":"0".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "use_connected_msg":"0".
2024-03-06 10:28:39.648 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_count_word_bytes=2".
2024-03-06 10:28:39.649 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_count_word_bytes":"2".
2024-03-06 10:28:39.649 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_count_word_bytes":"2".
2024-03-06 10:28:39.649 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_is_byte_swapped=0".
2024-03-06 10:28:39.649 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_is_byte_swapped":"0".
2024-03-06 10:28:39.649 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_is_byte_swapped":"0".
2024-03-06 10:28:39.650 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_is_counted=1".
2024-03-06 10:28:39.650 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_is_counted":"1".
2024-03-06 10:28:39.651 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_is_counted":"1".
2024-03-06 10:28:39.651 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_is_fixed_length=0".
2024-03-06 10:28:39.651 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_is_fixed_length":"0".
2024-03-06 10:28:39.651 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_is_fixed_length":"0".
2024-03-06 10:28:39.651 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "clearstr_is_zero_terminated=0".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "clearstr_is_zero_terminated":"0".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "clearstr_is_zero_terminated":"0".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_max_capacity=256".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_max_capacity":"256".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_max_capacity":"256".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_pad_bytes=0".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_pad_bytes":"0".
2024-03-06 10:28:39.652 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_pad_bytes":"0".
2024-03-06 10:28:39.653 thread(1) tag(0) DETAIL attr_create_from_str:160 Key-value pair "str_total_length=0".
2024-03-06 10:28:39.653 thread(1) tag(0) DETAIL attr_create_from_str:175 Key-value pair before trimming "str_total_length":"0".
2024-03-06 10:28:39.653 thread(1) tag(0) DETAIL attr_create_from_str:187 Key-value pair after trimming "str_total_length":"0".
2024-03-06 10:28:39.653 thread(1) tag(0) DETAIL attr_create_from_str:218 Done.
2024-03-06 10:28:39.653 thread(1) tag(0) INFO find_tag_create_func:99 Matched protocol=ab_eip
2024-03-06 10:28:39.653 thread(1) tag(0) INFO ab_tag_create:173 Starting.
2024-03-06 10:28:39.653 thread(1) tag(0) INFO rc_alloc_impl:111 Starting, called from ab_tag_create:180
2024-03-06 10:28:39.653 thread(1) tag(0) INFO rc_alloc_impl:130 Done
2024-03-06 10:28:39.653 thread(1) tag(0) DETAIL rc_alloc_impl:135 Returning memory pointer 000001E19512CB90
2024-03-06 10:28:39.653 thread(1) tag(0) DETAIL ab_tag_create:186 tag=000001E19512CB90
2024-03-06 10:28:39.654 thread(1) tag(0) INFO plc_tag_generic_init_tag:462 Starting.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL cond_create:1087 Starting.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL cond_create:1114 Done.
2024-03-06 10:28:39.654 thread(1) tag(0) INFO plc_tag_generic_init_tag:493 Done.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL get_plc_type:937 Found OMRON NJ/NX Series PLC.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL get_plc_type:937 Found OMRON NJ/NX Series PLC.
2024-03-06 10:28:39.654 thread(1) tag(0) DETAIL session_find_or_create:235 Starting
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL session_find_or_create:259 Creating new session.
2024-03-06 10:28:39.655 thread(1) tag(0) INFO session_create_unsafe:451 Starting
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL session_create_unsafe:454 Session should use connected messaging.
2024-03-06 10:28:39.655 thread(1) tag(0) INFO rc_alloc_impl:111 Starting, called from session_create_unsafe:459
2024-03-06 10:28:39.655 thread(1) tag(0) INFO rc_alloc_impl:130 Done
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL rc_alloc_impl:135 Returning memory pointer 000001E19512EFF0
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL cip_encode_path:71 Starting
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL cip_encode_path:130 PLC needs connection, adding path to the router object.
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL cip_encode_path:162 IOI size before 4
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL cip_encode_path:183 Done
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL session_create_unsafe:507 Setting connection_group_id to 0.
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL session_create_unsafe:547 Set maximum payload size to 1996 bytes.
2024-03-06 10:28:39.655 thread(1) tag(0) DETAIL add_session_unsafe:315 Starting
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL add_session_unsafe:325 Done
2024-03-06 10:28:39.656 thread(1) tag(0) INFO session_create_unsafe:564 Done
2024-03-06 10:28:39.656 thread(1) tag(0) INFO session_init:580 Starting.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL mutex_create:726 Starting.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL mutex_create:753 Done.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL cond_create:1087 Starting.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL cond_create:1114 Done.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL thread_create:884 Starting.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL thread_create:917 Done.
2024-03-06 10:28:39.656 thread(1) tag(0) INFO session_init:602 Done.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL session_find_or_create:305 Done
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL ab_tag_create:287 using session=000001E19512EFF0
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL get_tag_data_type:532 Starting.
2024-03-06 10:28:39.656 thread(1) tag(0) DETAIL get_tag_data_type:663 Done.
2024-03-06 10:28:39.656 thread(3) tag(0) INFO session_handler:1014 Starting thread for session 000001E19512EFF0
2024-03-06 10:28:39.657 thread(1) tag(0) DETAIL ab_tag_create:405 Setting up OMRON NJ/NX Series tag.
2024-03-06 10:28:39.657 thread(1) tag(0) WARN ab_tag_create:408 A path is required for this PLC type.
2024-03-06 10:28:39.657 thread(1) tag(0) WARN plc_tag_create_ex:887 Warning, PLCTAG_ERR_BAD_PARAM error found while creating tag!
2024-03-06 10:28:39.657 thread(3) tag(0) DETAIL session_handler:1034 in SESSION_OPEN_SOCKET_START state.
2024-03-06 10:28:39.657 thread(3) tag(0) INFO session_open_socket:620 Starting.
2024-03-06 10:28:39.657 thread(3) tag(0) DETAIL socket_create:1311 Starting.
2024-03-06 10:28:39.657 thread(1) tag(0) DETAIL rc_dec_impl:242 Calling cleanup functions due to call at plc_tag_create_ex:890 for 000001E19512CB90.
2024-03-06 10:28:39.657 thread(1) tag(0) INFO refcount_cleanup:256 Starting
2024-03-06 10:28:39.657 thread(1) tag(0) INFO ab_tag_destroy:800 Starting.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL ab_tag_abort:732 Starting.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL ab_tag_abort:741 Called without a request in flight.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL ab_tag_abort:748 Done.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL ab_tag_destroy:815 Getting ready to release tag session 000001E19512EFF0
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL ab_tag_destroy:817 Removing tag from session.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL rc_dec_impl:242 Calling cleanup functions due to call at ab_tag_destroy:818 for 000001E19512EFF0.
2024-03-06 10:28:39.658 thread(1) tag(0) INFO refcount_cleanup:256 Starting
2024-03-06 10:28:39.658 thread(1) tag(0) INFO session_destroy:791 Starting.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL remove_session:376 Starting.
2024-03-06 10:28:39.658 thread(1) tag(0) DETAIL remove_session_unsafe:352 Starting
2024-03-06 10:28:39.658 thread(3) tag(0) DETAIL socket_create:1334 Done.
2024-03-06 10:28:39.659 thread(1) tag(0) DETAIL remove_session_unsafe:367 Done
2024-03-06 10:28:39.659 thread(3) tag(0) DETAIL session_open_socket:654 Using default port 44818.
2024-03-06 10:28:39.659 thread(3) tag(0) DETAIL socket_connect_tcp_start:1355 Starting.
2024-03-06 10:28:39.659 thread(1) tag(0) DETAIL remove_session:384 Done.
2024-03-06 10:28:39.659 thread(1) tag(0) INFO session_destroy:802 Session sent 0 packets.
2024-03-06 10:28:39.659 thread(1) tag(0) DETAIL session_destroy:813 Destroying session thread.
2024-03-06 10:28:39.660 thread(3) tag(0) DETAIL socket_connect_tcp_start:1404 Found numeric IP address: 192.168.1.10
2024-03-06 10:28:39.660 thread(3) tag(0) DETAIL socket_connect_tcp_start:1444 Setting up wake pipe.
2024-03-06 10:28:39.660 thread(3) tag(0) INFO sock_create_event_wakeup_channel:2161 Starting.
2024-03-06 10:28:39.661 thread(3) tag(0) INFO sock_create_event_wakeup_channel:2308 Done.
2024-03-06 10:28:39.661 thread(3) tag(0) DETAIL socket_connect_tcp_start:1475 Socket connection attempt 0 started successfully.
2024-03-06 10:28:39.661 thread(3) tag(0) DETAIL socket_connect_tcp_start:1501 Done.
2024-03-06 10:28:39.662 thread(3) tag(0) INFO session_open_socket:669 Done.
2024-03-06 10:28:39.662 thread(3) tag(0) DETAIL session_handler:1050 Connect started, going to state SESSION_OPEN_SOCKET_WAIT.
2024-03-06 10:28:39.662 thread(3) tag(0) DETAIL session_handler:1301 Critical block.
2024-03-06 10:28:39.662 thread(1) tag(0) INFO session_close_socket:772 Starting.
2024-03-06 10:28:39.662 thread(1) tag(0) INFO socket_close:2083 Starting.
2024-03-06 10:28:39.662 thread(1) tag(0) INFO socket_close:2119 Done.
2024-03-06 10:28:39.663 thread(1) tag(0) INFO socket_destroy:2128 Starting.
2024-03-06 10:28:39.663 thread(1) tag(0) INFO socket_close:2083 Starting.
2024-03-06 10:28:39.663 thread(1) tag(0) INFO socket_close:2119 Done.
2024-03-06 10:28:39.663 thread(1) tag(0) INFO socket_destroy:2145 Done.
2024-03-06 10:28:39.663 thread(1) tag(0) INFO session_close_socket:780 Done.
2024-03-06 10:28:39.663 thread(1) tag(0) DETAIL session_destroy:861 Destroying session condition variable.
2024-03-06 10:28:39.663 thread(1) tag(0) DETAIL cond_destroy:1242 Starting.
2024-03-06 10:28:39.663 thread(1) tag(0) DETAIL cond_destroy:1253 Done.
2024-03-06 10:28:39.663 thread(1) tag(0) DETAIL session_destroy:868 Destroying session mutex.
2024-03-06 10:28:39.663 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 000001E195130060
2024-03-06 10:28:39.664 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.664 thread(1) tag(0) DETAIL session_destroy:874 Cleaning up allocated memory for paths and host name.
2024-03-06 10:28:39.664 thread(1) tag(0) INFO session_destroy:890 Done.
2024-03-06 10:28:39.664 thread(1) tag(0) INFO refcount_cleanup:268 Done.
2024-03-06 10:28:39.664 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 000001E19512CBC8
2024-03-06 10:28:39.664 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.664 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 000001E19512CBD0
2024-03-06 10:28:39.664 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.665 thread(1) tag(0) DETAIL cond_destroy:1242 Starting.
2024-03-06 10:28:39.665 thread(1) tag(0) DETAIL cond_destroy:1253 Done.
2024-03-06 10:28:39.665 thread(1) tag(0) INFO ab_tag_destroy:849 Finished releasing all tag resources.
2024-03-06 10:28:39.665 thread(1) tag(0) INFO ab_tag_destroy:851 done
2024-03-06 10:28:39.665 thread(1) tag(0) INFO refcount_cleanup:268 Done.
2024-03-06 10:28:39.665 thread(1) tag(0) WARN lookup_tag:4222 Tag with ID -7 not found.
2024-03-06 10:28:39.665 thread(1) tag(0) WARN plc_tag_status:1699 Called with an error status PLCTAG_ERR_BAD_PARAM!
2024-03-06 10:28:39.665 thread(1) tag(0) INFO plc_tag_destroy:1474 Starting.
2024-03-06 10:28:39.666 thread(1) tag(0) WARN plc_tag_destroy:1477 Called with zero or invalid tag!
2024-03-06 10:28:39.667 thread(1) tag(0) INFO ab_teardown:142 Releasing global AB protocol resources.
2024-03-06 10:28:39.667 thread(1) tag(0) INFO ab_teardown:153 IO thread already stopped.
2024-03-06 10:28:39.667 thread(1) tag(0) INFO ab_teardown:156 Freeing session information.
2024-03-06 10:28:39.667 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 00007FFA763774D0
2024-03-06 10:28:39.667 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.668 thread(1) tag(0) INFO ab_teardown:162 Done.
2024-03-06 10:28:39.668 thread(1) tag(0) INFO mb_teardown:2391 Starting.
2024-03-06 10:28:39.668 thread(1) tag(0) DETAIL mb_teardown:2395 Destroying Modbus mutex.
2024-03-06 10:28:39.668 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 00007FFA763774E8
2024-03-06 10:28:39.668 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.668 thread(1) tag(0) INFO mb_teardown:2401 Done.
2024-03-06 10:28:39.668 thread(1) tag(0) INFO lib_teardown:171 Tearing down library.
2024-03-06 10:28:39.668 thread(1) tag(0) INFO lib_teardown:176 Signaling tag tickler condition var.
2024-03-06 10:28:39.669 thread(1) tag(0) INFO lib_teardown:181 Tearing down tag tickler thread.
2024-03-06 10:28:39.669 thread(1) tag(0) INFO lib_teardown:188 Tearing down tag tickler condition var.
2024-03-06 10:28:39.669 thread(1) tag(0) DETAIL cond_destroy:1242 Starting.
2024-03-06 10:28:39.669 thread(1) tag(0) DETAIL cond_destroy:1253 Done.
2024-03-06 10:28:39.669 thread(1) tag(0) INFO lib_teardown:194 Tearing down tag lookup mutex.
2024-03-06 10:28:39.670 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 00007FFA76377498
2024-03-06 10:28:39.670 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.670 thread(1) tag(0) INFO lib_teardown:200 Destroying tag hashtable.
2024-03-06 10:28:39.670 thread(1) tag(0) INFO hashtable_destroy:260 Starting
2024-03-06 10:28:39.670 thread(1) tag(0) INFO hashtable_destroy:272 Done
2024-03-06 10:28:39.670 thread(1) tag(0) INFO lib_teardown:207 Done.
2024-03-06 10:28:39.671 thread(1) tag(0) DETAIL mutex_destroy:841 destroying mutex 00007FFA76377488
2024-03-06 10:28:39.671 thread(1) tag(0) DETAIL mutex_destroy:854 Done.
2024-03-06 10:28:39.671 thread(1) tag(0) DETAIL plc_tag_unregister_logger:1303 Starting
2024-03-06 10:28:39.671 thread(1) tag(0) DETAIL plc_tag_unregister_logger:1307 Done.

I've made some progress.

Since the size doesn't seem to be set in the tag with SetSize or SetBuffer, I tried writing to a variable that already had data, 10 to be exact.

"I can write to the variable, but only once, the other times it doesn't give any exceptions, if I do a read afterwards I see the value old, so he doesn't write anything."

While, if I try it from tag_rw it doesn't write anything in both as it doesn't set the buffer size

public override void Encode(Tag tag, int offset, string value)
{
    Exception sizeEx, WriteEx;

    int sizePre, sizePost;

    byte[] preBuffer, postBuffer;

    string preBufferString, postBufferString;

    try
    {
        byte[] stringBytes = Encoding.ASCII.GetBytes(value);

        byte[] byteArray = new byte[value.Length + 2];

        byteArray[0] = (byte)value.Length;

        byteArray[1] = 0;

        Array.Copy(stringBytes, 0, byteArray, 2, stringBytes.Length);

        sizePre = tag.GetSize();

        preBuffer = tag.GetBuffer();

        preBufferString = Encoding.ASCII.GetString(preBuffer);

        tag.SetBuffer(byteArray);

        postBuffer = tag.GetBuffer();

        postBufferString = Encoding.ASCII.GetString(postBuffer);

        try
        {
            tag.SetSize(value.Length + 2);
        }
        catch (Exception ex)
        {
            sizeEx = ex;
        }

        sizePost = tag.GetSize();

        tag.SetString(offset, value);
    }
    catch (Exception ex)
    {
        WriteEx = ex;
    }
}

image

In the output log from tag_rw2, I see the following warning:

2024-03-06 10:28:39.657 thread(1) tag(0) WARN ab_tag_create:408 A path is required for this PLC type.
2024-03-06 10:28:39.657 thread(1) tag(0) WARN plc_tag_create_ex:887 Warning, PLCTAG_ERR_BAD_PARAM error found while creating tag!

This is preventing tag_rw2 from getting very far in the process of connecting to the PLC.

You need to have a path element and for Omron it should be "18,". In your case that would be:

path=18,192.168.1.10

I do not know why, but Omron seems to require the use of a bridge-type path.

To see if you can write with tag_rw2, try writing a string that is the same length but with a different value. I.e. if you have the value "test1" in the tag, try writing "test2". If I remember correctly, tag_rw2 will not try to extend the tag buffer for longer strings.

Sorry, you're right, in path I always put 1.0 and it always went for all other types of Read/Write variables, even for reading strings 1.0 is fine.
But even with 18, everything seems to be fine

Library version 2.5.4.
Processing argument 1 "--type=uint8".
Processing argument 2 "--tag=protocol=ab_eip&gateway=192.168.1.10&path=18,192.168.1.10&plc=omron-njnx&name=PIPPO_10&use_connected_msg=0&str_count_word_bytes=2&str_is_byte_swapped=0&str_is_counted=1&str_is_fixed_length=0&str_is_zero_terminated=0&str_max_capacity=256&str_pad_bytes=0&str_total_length=0".
Processing argument 3 "--debug=4".
data[0]=10 (a)
data[1]=0 (0)
data[2]=49 (31)
data[3]=50 (32)
data[4]=51 (33)
data[5]=52 (34)
data[6]=53 (35)
data[7]=54 (36)
data[8]=55 (37)
data[9]=56 (38)
data[10]=57 (39)
data[11]=48 (30)

I tried to enter 18, for string write but the error is still the same

Library version 2.5.4.
Processing argument 1 "--type=string".
Setting type to TYPE_STRING.
Processing argument 2 "--tag=protocol=ab_eip&gateway=192.168.1.10&path=18,192.168.1.10&plc=omron-njnx&name=PIPPO_10&use_connected_msg=0&str_count_word_bytes=2&str_is_byte_swapped=0&str_is_counted=1&str_is_fixed_length=0&str_is_zero_terminated=0&str_max_capacity=256&str_pad_bytes=0&str_total_length=0".
Processing argument 3 "--debug=4".
Processing argument 4 "--write=1234567890".
Number of write args 1.
data[0]="1234567890"
Error while setting the string 0, error: PLCTAG_ERR_BAD_CONFIG!
2024-03-06 16:05:54.550 thread(1) tag(11) DETAIL plc_tag_set_string:3414 Starting with string 1234567890.
2024-03-06 16:05:54.550 thread(2) tag(11) DETAIL plc_tag_generic_tickler:276 Tickling tag 11.
2024-03-06 16:05:54.550 thread(2) tag(11) DETAIL plc_tag_generic_tickler:381 Done.
2024-03-06 16:05:54.550 thread(1) tag(11) WARN plc_tag_set_string:3460 string_capacity=256, string_last_offset=258, tag_size=12.
2024-03-06 16:05:54.550 thread(1) tag(11) WARN plc_tag_set_string:3472 Bad configuration? String capacity/size is larger than the tag buffer!
2024-03-06 16:05:54.551 thread(1) tag(11) DETAIL plc_tag_set_string:3574 Done with status PLCTAG_ERR_BAD_CONFIG.

In my opinion we should focus on my last comment, by setting Size and Buffer correctly I can write, but only the first time, the other times everything seems to go well without exceptions but if a read is done the value has not changed.

maybe we can mention some user who works on the c part to understand if there is something wrong there?

ElegantCatterpillar

Can I use the myString Initialized by the StringMapper or i need to perform

var myString = new Tag()
          {
              Gateway = IpDispositivo,
              Name = tagName,
              Timeout = TimeSpan.FromMilliseconds(TimeOut),
              Protocol = Protocol.ab_eip,
              UseConnectedMessaging = true,
              PlcType = PlcType.Micro800,
              StringIsCounted = true,
              StringIsFixedLength = false,
              StringIsByteSwapped = false,
              StringCountWordBytes = 1,
              StringMaxCapacity = 255,
              StringTotalLength = 0,
              StringPadBytes = 0,
          };

I will test it to omron PLC

I recommend using the base tag because it is designed to work with almost no default settings so I used it that way.

I really don't know how the String will work on the Omron PLC, I run pure Allen Bradley PLC's so I wouldn't know the configuration but on the Micro820 which is where I did it I got this error, so instead of treating it as a String that if it worked in the higher end PLCs of the brand I used it as a byte array through the ASCII codes of what I wanted to write in addition to adding in position 0 the new length of the ASCII Array, so always the array that had to write bytes should be 1 position more than normal.

What model of Omron PLC are you using? You may see natively from the PLC programming software how the String is built to help you to transfer it to use the library, are you using C#?

@kyle-github However, I don't quite understand why I should put path=18,IP.

I've always worked with path=1,0 with Micrologix, Allen Bradley, I remember reading somewhere that 1,0 was generic for everyone.

The PLC is an Omron NX1P2, from your writing example, I didn't follow the initialization of the tag as it gave me a configuration error (-7), I managed to write a string, as you can see from the comment above, only that this works only once, the other times the writing no longer works but no error is generated, it is as if it were fine for the library, but not for the PLC, only the first write is fine.

Yes, I use C#, I initialize the tags via mapper

internal class EEIPItem<M, T> : IItem where M : IPlcMapper<T>, new()
{
    internal EEIPItem(EEIPItemModel itemModel, EEIPConfiguration configuration)
    {
        tag = new Tag<M, T>
        {
            Name = itemModel.ItemID,

            Gateway = configuration.ServerIp,

            Path = configuration.ServerPath,

            PlcType = configuration.PlcType == Enums.EEIPPlcType.CompactLogix ? PlcType.ControlLogix : (PlcType)configuration.PlcType,

            Protocol = (Protocol)configuration.Protocol,

            Timeout = TimeSpan.FromMilliseconds(configuration.ItemTimeOut),
        };
    }
}

I found a library written in python (https://github.com/aphyt/aphytcomm) that might help with Omron.

It also has tag discover, as well as read/write (works on strings)

Any news on this? I am having the same issue.
PLC: Omron NX1P2
Libplctag.NET version 1.2.1

I tried using the suggested python library, it works. Trying the same with libplctag gives a different wireshark output.

Can you capture the Wireshark output of writing a string, @MountainKing91? I don't have an Omron PLC so it is hard to debug this. I have seen aphyt before but without an Omron, I cannot capture its packets to make sure I understand what it is doing.

If the PLC returns strings with only the valid characters, then the tag buffer will not be as long as the maximum length. You should be able to set the size of the tag to the length needed by the string. Looking at the docs, it appears that Omron strings have a 2-byte count word followed by the character data. So something like this (this is in C, you'll have to translate for the C# version):

length = strlen(myString);
rc = plc_tag_set_size(tag, length + 2); //  +2 includes the count word.
...
rc = plc_tag_set_string(tag, 0 /* offset in the tag */, myString);
...
rc = plc_tag_write(tag, timeout);
...

I can do it.

This is the custom mapper that should work like your example:

public class OmronStringPlcMapper2 : PlcMapperBase<string>, IPlcMapper<string>, IPlcMapper<string[]>
    {
        public override int? ElementSize => 256;

        public override string Decode(Tag tag, int offset)
        {
            return tag.GetString(0);
        }

        public override void Encode(Tag tag, int offset, string value)
        {
            tag.SetSize(value.Length + 2);

            tag.SetString(0, value);
        }
    }

This is me using the mapper:

    class TestMapper
    {
        public TestMapper()
        {
            var tag = new Tag<OmronStringPlcMapper2, string>()
            {
                Name = "TestString",
                Gateway = "192.168.250.1",
                Path = "1,0",
                PlcType = PlcType.Omron,
                Protocol = Protocol.ab_eip,
                Timeout = TimeSpan.FromMilliseconds(5000),
            };

            try
            {
                tag.Read();
                Console.WriteLine(tag.Value);

                tag.Write("this is a test");

                tag.Read();
                Console.WriteLine(tag.Value);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

This produces an error.

This is the wireshark output of successfully writing the value "MyStringInitialValue" to the variable "TestString" using the python library:
image

0000   00 00 0a c9 db b8 a0 29 19 df 3c fb 08 00 45 00
0010   01 6c 59 1a 40 00 80 06 00 00 c0 a8 fa c9 c0 a8
0020   fa 01 e9 1e af 12 1f 23 f4 82 55 ac c3 94 50 18
0030   02 00 77 7b 00 00 6f 00 2c 01 70 01 65 00 00 00
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0050   00 00 08 00 02 00 00 00 00 00 b2 00 1c 01 4d 0a
0060   91 0a 54 65 73 74 53 74 72 69 6e 67 80 03 00 00
0070   00 00 00 01 d0 00 01 00 00 01 4d 79 53 74 72 69
0080   6e 67 49 6e 69 74 69 61 6c 56 61 6c 75 65 00 00
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00f0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0100   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0110   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0120   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0130   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0140   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0150   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0160   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0170   00 00 00 00 00 00 00 00 00 00

I can test whatever you need, no problem, I'm just a bit unfamiliar with wireshark.

Full wireshark captures with libplctag and aphyt
Wireshark captures.zip

I'm trying to understand what I am seeing here while looking at the Aphyt code. This is using UCMM, unconnected messaging. Here's the packet:

0000   00 00 0a c9 db b8 a0 29 19 df 3c fb 08 00 

Above is the Ethernet header I think.

Now the IP header. 20 bytes.

000E   45 00
0010   01 6c 59 1a 40 00 80 06 00 00 
001A   c0 a8 fa c9 - source IP addr 192.168.250.201
001E   c0 a8 fa 01  - destination IP addr 192.168.250.1

Now we start the TCP header (usually 20-24 bytes)

0022   e9 1e - source port
0024   af 12 - destination port, CIP 44818
0026   1f 23 f4 82 - TCP sequence number
002A   55 ac c3 94 - TCP Ack number
002E   50 18 02 00 77 7b 00 00 - rest of the TCP header

Then the EIP header

0036   6f 00 -  EIP command send unconnected message
0038   2c 01 - payload is 300 bytes total
003A   70 01 65 00 - EIP session handle
003E   00 00 00 00 - EIP status
0042   00 00 00 00 00 00 00 00 - EIP sender context data
004A   00 00 00 00 - EIP options

We start the CPF (common packet format) section. This looks like a UCMM message.

004E   00 00 00 00 - interface handle
0052   08 00 - router timeout
0054   02 00 - two address/data items
0056   00 00 - NULL address item
0058   00 00 - of zero length - consistent with UCMM
005A   b2 00 - unconnected data item
005C   1c 01 - 284 bytes in the payload.  This checks.

Now comes the actual request payload.

005E   4d - CIP write
005F   0a - 10 words in the path, the path has two segments, a string and a data segment.
0060   91 0a 54 65 73 74 53 74 72 69 6e 67 - Tag name "TestString"
006C   80 - simple data segment
006D   03  - number of words in data segment
006E   00 00 00 00 - offset in tag
0072   00 01 - 256 bytes of data in the request. End of chunked data header and path
0074   d0 00 - STRING data type
0076   01 00 - number of elements.  1.   Number of elements in the array or request?
0078   00 01 - size of the data?   Size of the tag or the request?

And finally this is the data we are going to write. There are 256 bytes of it.

007A   4d 79 53 74 72 69 6e 67 49 6e 69 74 69 61 6c 56 61 6c 75 65 - "MyStringInitialValue"
008E   00 00
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00f0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0100   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0110   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0120   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0130   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0140   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0150   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0160   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0170   00 00 00 00 00 00 00 00 00 00

OK. So I think I see the problem. When you are writing a string, you need to write the entire thing. And strings are up to 256 bytes long. And the count word is omitted.

So... Maybe this will work?

const char *my_str = "MyStringInitialValue";
int tag_size = 256;
rc = plc_tag_set_size(tag, tag_size);
...
/* copy the string in manually without a count word */

/* get the string length */
len = strlen(my_str);

/* we are going to set _all_ the data in the tag buffer, some of
   which will be the string data.
*/
for(i=0; i < tag_size; i++) {
    if(i < len) {
        plc_tag_set_uint8(tag, i, my_str[i]);
    } else {
        plc_tag_set_uint8(tag, i, 0);
    }
}

/* now try to write. */
rc = plc_tag_write(tag, 5000);

I am going to try as soon as I can get back to the plc, tonight or tomorrow morning.

In general, in omron world strings are from STRING[1] to STRING[1986]. Default is STRING[256].

Hi @MountainKing91, thanks for the support,

Please also take a look at my comments above, they might be very helpful, from the .net library it is not possible to change the buffer size, it throws the error with code: the size.

In this comment, I even managed to write to it once in the created string, but then nothing more.

#359 (comment)

Here you will find the Mapper with ElementiSize 256.

#359 (comment)

Come on, let's fix this Bug!!!!

I tried, with no success.

@zN3utr4l this throws exception 22

int tagSize = 256;
tag.SetSize(tagSize);

I can build the 256 bytes buffer for "MyStringInitialValue", but because of the SetSize error i guess I cannot actually write it?
The buffer (should be ok, it's the same as @kyle-github ):

4D 79 53 74 72 69 6E 67 49 6E 69 74 69 61 6C 56 
61 6C 75 65 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

Is it really the tag.SetSize(256) that is creating the issue?

With this encode method I create the buffer above

        public override void Encode(Tag tag, int offset, string value)
        {
            try
            {

                int tagSize = 256;
                byte[] buffer = new byte[tagSize];
                byte[] stringBytes = Encoding.ASCII.GetBytes(value);

                Array.Copy(stringBytes, 0, buffer, 0, stringBytes.Length);

                //string hexString = BitConverter.ToString(buffer);
                //Console.WriteLine(hexString.Replace('-', ' '));

                try
                {
                    tag.SetSize(tagSize);
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"SetSize exception: {ex.Message}");
                }

                for (int i = 0; i < tagSize; i++)
                {
                    if (i < value.Length)
                    {
                        tag.SetUInt8(i, stringBytes[i]);
                    }
                    else
                    {
                        tag.SetUInt8(i, 0);
                    }
                }

                //tag.SetBuffer(buffer); // ALTERNATIVE, SAME RESULT...

                byte[] b = tag.GetBuffer();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

wireshark dump for write command:

0000   00 00 0a c9 db b8 a0 29 19 df 3c fb 08 00 45 00   .......)..<...E.
0010   01 68 7e f2 40 00 80 06 00 00 c0 a8 fa c9 c0 a8   .h~.@...........
0020   fa 01 e5 99 af 12 bc 73 c8 de bd fc bf 1c 50 18   .......s......P.
0030   02 00 77 77 00 00 70 00 28 01 71 01 1c 00 00 00   ..ww..p.(.q.....
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 01 00 02 00 a1 00 04 00 41 0e 9e 72 b1 00   ..........A..r..
0060   14 01 03 00 4d 06 91 0a 54 65 73 74 53 74 72 69   ....M...TestStri
0070   6e 67 d0 00 01 00 4d 79 53 74 72 69 6e 67 49 6e   ng....MyStringIn
0080   69 74 69 61 6c 56 61 6c 75 65 00 00 00 00 00 00   itialValue......
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00f0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0100   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0110   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0120   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0130   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0140   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0150   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0160   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0170   00 00 00 00 00 00                                 ......

wireshark dump for write response "Not enough data":

0000   a0 29 19 df 3c fb 00 00 0a c9 db b8 08 00 45 00   .)..<.........E.
0010   00 5a 08 1d 40 00 40 06 bc 64 c0 a8 fa 01 c0 a8   .Z..@.@..d......
0020   fa c9 af 12 e5 b1 3b fb ec 71 34 54 41 b8 50 18   ......;..q4TA.P.
0030   27 ec 9c ed 00 00 70 00 1a 00 71 01 1e 00 00 00   '.....p...q.....
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 00 00 02 00 a1 00 04 00 e7 61 00 00 b1 00   ...........a....
0060   06 00 03 00 cd 00 13 00                           ........

I don't think that is correctly constructed. But I am not an Omron person so I am not sure. It does not look right to me though:

14 01 - size of the payload, 276 bytes?
03 00 - connection sequence ID, this is included in that size.
4d - command, CIP write
06 91 0a 54 65 73 74 53 74 72 69 6e 67 - tag name 
d0 00 - string type
01 00 - number of elements
XX XX - there is a count of _something_ here in the Wireshark packet.  
              In that it is 256.  Whether that is the size of the string or the
              size of the data, I do not know.
4d 79 53 74 72 69 6e 67 49 6e 69 74 69 61 6c 56 61 6c 75 65 ... - string data.

I think you need 00 01 in the XX XX bytes above.

Hmm, this makes some sense if the plc is interpreting the first two bytes of the data as a byte count. It would see 0x794d as the byte count.

There is a bug in the wrapper causing SetSize to throw an exception - a fix will be released with libplctag.NET 1.3.1

@kyle-github shifting the buffer of 2 bytes and setting the first two elements to 0x0 and 0x1 gives me this wireshark dump and ErrorTooSmall, I had already tried. Something is still off.

0000   00 00 0a c9 db b8 a0 29 19 df 3c fb 08 00 45 00   .......)..<...E.
0010   01 68 7f 62 40 00 80 06 00 00 c0 a8 fa c9 c0 a8   .h.b@...........
0020   fa 01 c6 b4 af 12 2d c5 a7 d8 dd 68 4d c8 50 18   ......-....hM.P.
0030   02 00 77 77 00 00 70 00 28 01 71 01 08 00 00 00   ..ww..p.(.q.....
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 01 00 02 00 a1 00 04 00 41 04 57 5e b1 00   ..........A.W^..
0060   14 01 02 00 4d 06 91 0a 54 65 73 74 53 74 72 69   ....M...TestStri
0070   6e 67 d0 00 01 00 00 01 4d 79 53 74 72 69 6e 67   ng......MyString
0080   49 6e 69 74 69 61 6c 56 61 6c 75 65 00 00 00 00   InitialValue....
0090   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00a0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
00f0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0100   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0110   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0120   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0130   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0140   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0150   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0160   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0170   00 00 00 00 00 00                                 ......

@timyhac ok good to know, can we try to override in some way to at least make it work for some tests with the omron cpu?

@MountainKing91 - I'm not sure what you want to override but you can always download both libplctag and the wrapper and tweak to your heart's content. Including your own libplctag core binary is straightforward - https://github.com/libplctag/libplctag.NET/blob/master/docs/Using-a-non-packaged-version-of-the-native-libplctag-library.md

@timyhac Sorry, I didn't notice you already released 1.4, my bad. I updated to 1.4 and the exception is gone. Same error "too small" though.

All good thanks for testing

I'm trying to grasp where the issue could be
image
left side: write successful, the actual payload starts at byte 007A (4d 79 53 ...) preceded by 00 01 which is the byte count
right side: ErrorTooSmall, the actual payload starts at byte 0078 preceded by 00 01 again

So it looks like something is off before the payload part I am manipulating; I can't figure out why there is a two bytes offset: even shifting the buffer by 2 more bytes in order to start the payload a byte 007a does not solve it.

C# code:

int tagSize = 256;
byte[] buffer = new byte[tagSize];
byte[] stringBytes = Encoding.ASCII.GetBytes(value);

Array.Copy(stringBytes, 0, buffer, 2, stringBytes.Length);

buffer[0] = 0;
buffer[1] = 1;

tag.SetSize(tagSize);
tag.SetBuffer(buffer);

Maybe try a raw tag and copy the working CIP command into the raw tag directly?

The total packet lengths are going to be somewhat different as it looks like Aphyt is using UCMM (unconnected message) and libplctag uses connected messaging where possible (less overhead).

If you want to turn off connected messaging, use the use_connected_msg=0 option. It looks like the .Net version of that is UseConnectedMessaging. This will turn off the connected negotiation and you should see packets that look much closer.

Maybe I got it working (looks like yes):

code:

    class TestMapper
    {
        public TestMapper()
        {

            var tag = new Tag<OmronStringPlcMapper, string>()
            {
                Name = "TestString",
                Gateway = "192.168.250.1",
                Path = "1,0",
                PlcType = PlcType.Omron,
                Protocol = Protocol.ab_eip,
                Timeout = TimeSpan.FromMilliseconds(5000),
            };

            try
            {
                tag.Read();
                Console.WriteLine(tag.Value);

                tag.Write("MyStringInitialValue");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    public class OmronStringPlcMapper : PlcMapperBase<string>, IPlcMapper<string>, IPlcMapper<string[]>
    {
        public override int? ElementSize => 256;

        public override string Decode(Tag tag, int offset)
        {
            return tag.GetString(0);
        }

        public override void Encode(Tag tag, int offset, string value)
        {
            try
            {
                int tagSize = 256;
                short len = (short)value.Length; // we have 2 bytes for the byte count, but value.Length returns an int (4 bytes)

                byte[] buffer = new byte[value.Length + 2];
                byte[] valueBytes = Encoding.ASCII.GetBytes(value);
                byte[] countBytes = BitConverter.GetBytes(len);

                Array.Copy(countBytes, 0, buffer, 0, countBytes.Length);
                Array.Copy(valueBytes, 0, buffer, 2, valueBytes.Length);

                tag.SetSize(tagSize);
                tag.SetBuffer(buffer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

wireshark:

0000   00 00 0a c9 db b8 a0 29 19 df 3c fb 08 00 45 00   .......)..<...E.
0010   00 7e 32 16 40 00 80 06 00 00 c0 a8 fa c9 c0 a8   .~2.@...........
0020   fa 01 cf e9 af 12 9e 36 b5 b0 01 6b 6d 18 50 18   .......6...km.P.
0030   02 00 76 8d 00 00 70 00 3e 00 71 01 46 00 00 00   ..v...p.>.q.F...
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 01 00 02 00 a1 00 04 00 41 22 b1 22 b1 00   ..........A"."..
0060   2a 00 03 00 4d 06 91 0a 54 65 73 74 53 74 72 69   *...M...TestStri
0070   6e 67 d0 00 01 00 14 00 4d 79 53 74 72 69 6e 67   ng......MyString
0080   49 6e 69 74 69 61 6c 56 61 6c 75 65               InitialValue

full capture:
capture.zip

@kyle-github this is not sending a 256 bytes payload, right? Not sure if it is ok, it looked like we needed to send the full 256 all the time..
@zN3utr4l can you take a look?

I'm not very familiar with Wireshark, like you, I think, I'm a developer who ran into this problem and tried to understand something about it, but I don't know anything about this CIP or UCMM protocol.

Your Encode function looks very similar to mine (#359 (comment)), they differ only by the tag.SetString(offset, value); line, I don't remember why I added it.

As @kyle-github says, try UseConnectedMessaging to false, he's the expert on this.

It works in both ways, with UseConnectedMessaging set to true or false.

Perfect I would say, then we just wait for this behavior to be merged into the Native string Mapper.
As well as adding ElementSize for Omron.

Or just override like you did.

Thank you @MountainKing91 for your support in resolving this issue.

I will give it a try with STRING[1986] and see how it goes.
By the way, with this solution there is no fixed offset when using strings inside udts ☹️

Maybe I got it working (looks like yes):

code:

    class TestMapper
    {
        public TestMapper()
        {

            var tag = new Tag<OmronStringPlcMapper, string>()
            {
                Name = "TestString",
                Gateway = "192.168.250.1",
                Path = "1,0",
                PlcType = PlcType.Omron,
                Protocol = Protocol.ab_eip,
                Timeout = TimeSpan.FromMilliseconds(5000),
            };

            try
            {
                tag.Read();
                Console.WriteLine(tag.Value);

                tag.Write("MyStringInitialValue");
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
    public class OmronStringPlcMapper : PlcMapperBase<string>, IPlcMapper<string>, IPlcMapper<string[]>
    {
        public override int? ElementSize => 256;

        public override string Decode(Tag tag, int offset)
        {
            return tag.GetString(0);
        }

        public override void Encode(Tag tag, int offset, string value)
        {
            try
            {
                int tagSize = 256;
                short len = (short)value.Length; // we have 2 bytes for the byte count, but value.Length returns an int (4 bytes)

                byte[] buffer = new byte[value.Length + 2];
                byte[] valueBytes = Encoding.ASCII.GetBytes(value);
                byte[] countBytes = BitConverter.GetBytes(len);

                Array.Copy(countBytes, 0, buffer, 0, countBytes.Length);
                Array.Copy(valueBytes, 0, buffer, 2, valueBytes.Length);

                tag.SetSize(tagSize);
                tag.SetBuffer(buffer);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

wireshark:

0000   00 00 0a c9 db b8 a0 29 19 df 3c fb 08 00 45 00   .......)..<...E.
0010   00 7e 32 16 40 00 80 06 00 00 c0 a8 fa c9 c0 a8   .~2.@...........
0020   fa 01 cf e9 af 12 9e 36 b5 b0 01 6b 6d 18 50 18   .......6...km.P.
0030   02 00 76 8d 00 00 70 00 3e 00 71 01 46 00 00 00   ..v...p.>.q.F...
0040   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00   ................
0050   00 00 01 00 02 00 a1 00 04 00 41 22 b1 22 b1 00   ..........A"."..
0060   2a 00 03 00 4d 06 91 0a 54 65 73 74 53 74 72 69   *...M...TestStri
0070   6e 67 d0 00 01 00 14 00 4d 79 53 74 72 69 6e 67   ng......MyString
0080   49 6e 69 74 69 61 6c 56 61 6c 75 65               InitialValue

full capture: capture.zip

@kyle-github this is not sending a 256 bytes payload, right? Not sure if it is ok, it looked like we needed to send the full 256 all the time.. @zN3utr4l can you take a look?

This works for me, thank you so much. Anyone managed to read array of Strings?

I did not try yet @Ashpatell1.
The code you quoted has a little bug that I am trying to figure out at the moment.

@MountainKing91 / @Ashpatell1 - if either of you try working with string arrays on the Omron I would be interested to know what data structure they return and how that changes with the content of the string.

I would to - I am a bit overwhelmed with work lately, I couldn't find time to dedicate :/
I still have accesso to the NX though, I'll see what I can do.

Here we go.

I created an array of two STRING[256]
image

Let's see what I tried to read the values:

  1. Read the array passing the first index in the tag name
var myArrayTag = new Tag()
{
    Name = "StringArray_256[0]",
    Gateway = "192.168.250.1",
    Path = "1,0",
    PlcType = PlcType.Omron,
    Protocol = Protocol.ab_eip,
    ElementSize = 256,
    ElementCount = 1,
    Timeout = TimeSpan.FromMilliseconds(5000),
};

try
{
    myArrayTag.Initialize();
    myArrayTag.Read();
    byte[] buffer = myArrayTag.GetBuffer();

    Console.WriteLine(buffer.Length);
    string hexString = BitConverter.ToString(buffer);
    Console.WriteLine(hexString.Replace('-', ' '));

}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

Result with

StringArray_256[0] := '';
StringArray_256[1] := '';

00 00

Result with

StringArray_256[0] := 'testing';
StringArray_256[1] := '';

07 00 74 65 73 74 69 6E 67
image

It appears that the behaviour is the same as a standalone STRING[256] tag - only the non 00 values are returned and the first two bytes hold the length in bytes of the actual string value. This could be because I am simply reading element 0 of the array though.

If I pass ElementCount = 2 I get ErrorBadParam.
details.txt

  1. Read the array passing the actual tag name
var myArrayTag = new Tag()
{
    Name = "StringArray_256",
    Gateway = "192.168.250.1",
    Path = "1,0",
    PlcType = PlcType.Omron,
    Protocol = Protocol.ab_eip,
    ElementSize = 256,
    ElementCount = 2,
    Timeout = TimeSpan.FromMilliseconds(5000),
};

try
{
    myArrayTag.Initialize();
    myArrayTag.Read();
    byte[] buffer = myArrayTag.GetBuffer();

    Console.WriteLine(buffer.Length);
    string hexString = BitConverter.ToString(buffer);
    Console.WriteLine(hexString.Replace('-', ' '));

}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

I get ErrorBadParam.
details.txt

Thanks @MountainKing91 - I have quickly glanced at the logs and although this is currently outside of my range of expertise, I believe the error response is coming from the PLC so I guess the problem is that either libplctag core isn't creating the correct request structure, or Omron PLCs are just not capable of returning String array tags and you would need to request each element individually.

You cannot ask for two elements when you use the bare name for the string array

Yep my bad, I keep doing this error over and over again!

@timyhac requesting each element of the array individually is something I am already doing in case of arrays of large data structure, where you can easily hit the 199x bytes limit. I imagine this might be an ok solution for string arrays too.

I am looking at Omron documentation to see if I can find some hints.