RobotStudio event

Problem with arrays

Options
Hello,

I have a module like this :

MODULE TESTARRAY

VAR num MyArray{4} := [10,11,12,13];

ENDMODULE

and a sample of code like this :

  Controller abbCont = new Controller();
  RapidData abbMyArray = abbCont.Rapid.GetRapidData("T_MOVE", "TESTARRAY", "MyArray");

  ArrayData abbADMyArray = (ArrayData)abbMyArray.Value;
  IRapidData abbN1 = abbADMyArray[1];
  IRapidData abbN2 = abbADMyArray[2];
  IRapidData abbN3 = abbADMyArray[3];
  IRapidData abbN4 = abbADMyArray[4];

  abbMyArray.Dispose();
  abbCont.Dispose();

At the end of my code, I have abbN1 = abbN2 = abbN3 = abbN4 = {13}.

You can see the problem in debug mode :

image
image
image

I can resolve the problem by using the following code instead :

  Num abbN1 = (Num)abbADMyArray[1];

  Num abbN2 = (Num)abbADMyArray[2];

  Num abbN3 = (Num)abbADMyArray[3];

  Num abbN4 = (Num)abbADMyArray[4];

but in my case, I have arrays of record type :

RECORD MyType
  String Name;
  Pos Point;
ENDRECORD

VAR MyType Points{x} = ...;

And I have to copy each field of the record
separately (inside my class derivating from UserDefined) and it's quite painful.

So it's the normal behavior or it is a bug?

The complete solution of my sample :

2008-07-29_122159_TpsViewTestArray.zip

Thank you!
Herv?.

Comments

  • carlosmtz2000
    Options

    I will say it is a bug, since an instance is re-used internally to parse the string value. This is noy happening when casting to a Num, since this is a struct.

    I do not if this will be a workaround, but it is quick to test. Have you tried changing the Mode of the Array to Dynamyc?

    abbMyArray.Mode = ArrayModes.Dynamic;

    Carlos Martinez
    ABB
  • Herve
    Options
    carlosmtz2000,

    I agree with you about the cast to Num.

    I forgot to say that I use FPSDK (5.10). So I've just tried with PCSDK :

      NetworkScanner abbScan = new NetworkScanner();
      ControllerInfo[] abbConts = abbScan.GetControllers();
      if (abbConts.Length == 0)
        return;

      Controller abbCont = ControllerFactory.CreateFrom(abbConts[0]);
      abbCont.Logon(UserInfo.DefaultUser);

      RapidData abbMyArray = abbCont.Rapid.GetRapidData("T_MOVE", "TESTARRAY", "MyArray");

      ArrayData abbADMyArray = (ArrayData)abbMyArray.Value;
      abbADMyArray.Mode = ArrayModes.Snapshot;
      IRapidData abbN1 = abbADMyArray[0];
      IRapidData abbN2 = abbADMyArray[1];
      IRapidData abbN3 = abbADMyArray[2];
      IRapidData abbN4 = abbADMyArray[3];

      abbMyArray.Dispose();
      abbCont.Dispose();

    With PCSDK, it works with Dynamic and Snapshot array mode.
    With FPSDK, there is no Mode of Array.

    Herve.

  • carlosmtz2000
    Options

    Hi Herve,

    This is a bug since internally we are reusing the IRapidData instance. Meanwhile, the safe way to go it will be to define your own struct that represents your RAPID data.

    Pseudo code:

     

    struct MyType : IRapidData

    {

        public String Name;
        public Pos Point;
       
        public MyType(String Name, Pos Point)
        {
            ...
        }
       
        public void FillFromString(string txt)
        {
            ...  
        }
       
        public void FillFromArrayItem(IRapidData arrayItem)
        {
            UserDefined myType = arrayItem (arrayItem);
            this.Name = (Num) myType.Components[0];
            this.Point = (Point) myType.Components[1];
        }

    }

    NOTE: Implementing IRapidData will ask you to define the method FillFromString, in which you will need to parse the RAPID string representation of your data. It is up to you if you would like to implement this interface or not (also depending is this class will be public or not for other of your projects)

     

    Saludos

    Carlos Martinez

    Carlos Martinez
    ABB
  • Herve
    Options
    Hola Carlos,

    I did something a little bit different :

    struct MyType : IRapidData
    {
      UserDefined mData;

      public MyType( RapidDataType iRDT )
      {
        mData = new UserDefined(iRDT);
      }

      public IRapidData Data
      {
        get
        {
           return mData;
        }
        set
        {
           UserDefined data = (UserDefined)value;
           mData.Components[0] = data.Components[0];
           mData.Components[1] = data.Components[1];
        }
      }

      public void FillFromString(string iString)
      {
        ...
      }
    }

    In your case, it's not necessary to call the constructor with the RapidDataType argument, but it's more difficult to return a UserDefined object (like I do with the Data property), sometimes it's useful to set a RapidData object :

    RapidData rd = myModule.GetRapidData(...);
    MyType mytype = new MyType(rd.DataType);
    mytype.Data = mytypeArray[2];
    rd.Value =  mytype;

    Merci,
    Herv?.