DotNetSiemensPLCToolBoxLibrary (LibNoDave) Zugriff auf Dual-Port RAM / FB15

Hi zusammen,
das Thema ist leider schon etwas älter aber vielleicht finden sich hier noch einige User die mir weiterhelfen können.
Ich würde gerne von einer Siemens 840D Kanal und GUD Variablen einlesen. Der Zugriff auf Datenbausteine z.b. DB211.DBW123 funktioniert super.
Gibt es eine Möglichkeit auch "/Channel/State/progToolIdent" (aktueller Werkzeugname in Spindel) und GUD-Variablen z.b. "/NC/_N_NC_GD2_ACX/<Variablenname>" (/nck/GD2/<Variablenname>)

Vielen Dank und Grüße
Rolf
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hast du ein Siemens Gerät welches diese Daten abfragen kann?
Wenn ja, dann könntest du mit Wireshark eine Aufzeichnung machen wenn du diese Daten abfragst, und diese Datei hier anhängen. Dann kann man prüfen wie die Abfrage aussieht, ob und wie das mit den bestehenden Funktionen möglich ist, oder ob da etwas zu ergänzen ist.
 
Eine direkter Zugriff per Name ist nicht möglich.

Es steht jedoch alles was du brauchst bereits im Verlauf dieses Themas.
In der "DotNetSiemensPlcToolbox" sowie der zugehörigen LibNoDave ist bereits alles implementiert, was du für die Abfrage benötigst.

Mit dem NC-VAR-Selector kommst du an die benötigten Informationen.

Übrigens "progToolIdent" ist das Programmierte aber nicht zwangsläufig das aktuelle Werkzeug in der Spindel. Dafür gibts die "actToolIdent".

Der NC-Var-Selector liefert z.B. für actToolIdent diese Struktur die ja für den FB2(Get), FB3(Put) benötigt werden. Diese Informationen kannst du auch der "DotNetSiemensPlcToolbox" übergeben (PlcNckTag) und du bekommst den Namen.


Code:
 C1_S_actToolIdent1:
   STRUCT
   SYNTAX_ID : BYTE := B#16#82;
   bereich_u_einheit : BYTE := B#16#41;
   spalte : WORD := W#16#21;
   zeile : WORD := W#16#1;
   bausteintyp : BYTE := B#16#7F;
   ZEILENANZAHL : BYTE := B#16#1;
   typ : BYTE := B#16#13;
   laenge : BYTE := B#16#20;
   END_STRUCT ;


 END_STRUCT ;

Du bekommst auch die Startadresse der GUD (Component GDx, Name Dummy )

Um die Offsets zu berechnen kannst du entweder die ACX Datei interpretieren oder du legst fest, dass immer ein Bestimmter Bereich für die GUDs verwendet werden muss (z.B. Immer GUD4 und immer in der selben Reihenfolge) dann kannst du dir das Interpretieren sparen.
 
Hallo Hans,
vielen Dank für deine Hilfe! Leider bekomme ich die Daten aus dem NC-Var Selector nicht in den Nck Tag. Welcher Wert aus dem Selector muss in
welchen Parameter des Tag geschrieben werden damit das Auslesen funktioniert.
Ich bekomme immer den Fehler "Operation failed due to error from PLC 33028: context is not supported. Step7 says:Function not implemented or error in telgram."
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hab dafür noch die Klasse NC_Var geschrieben. Dieser kannst du die Werte übergeben und bekommst den PlcNckTag zurück.
Außerdem kannst du der Methode GetNckTag noch einen Offset für Zeile oder Spalte mitgeben, der z.B. für weitere Kanäle oder weitere Spindel benötigt wird. (Grundstruktur der Adressen ist ja immer gleich)

Code:
PLCConnection _myConn = new PLCConnection("ReadFromNck");


_myConn.Configuration.CpuIP = "192.168.214.1";
_myConn.Configuration.CpuSlot = 4;
_myConn.Connect();

var tag = new NC_Var(0x82, 0x41, 0x21, 0x1, 0x7F, 0x1, 0x13, 0x20).GetNckTag();
//var tag = new NC_Var(0x82, [FONT=arial black]0x40[/FONT], 0x21, 0x1, 0x7F, 0x1, 0x13, 0x20).GetNckTag([FONT=arial black]1[/FONT]); //Kanal
_myConn.ReadValue(tag);
 
Vielen Dank! Hab es mit dem Fork der Library von Hans54216 und der genannten Klasse hinbekommen.
Gibt es schon Erfahrungen mit dem Interpretieren der ACX Files? Es wäre super wenn man über den Symbolischen Namen den Nck Tag bekommen und mit diesem dann die Werte lesen könnte.

 
Zuviel Werbung?
-> Hier kostenlos registrieren
Hallo Jochen, mein Fork ist der von Nick135. Darin sind ein paar Erweiterungen sowie Fehlerbehebungen zum Thema NCK vorhanden. Hab bis jetzt nur noch keine Zeit, Lust gefunden nen PullRequest zu machen. Du verwendest ja eh keine 840d ;)
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Mit dem Kode solltest du die GUD2 lesen können.

Bei Zeiten möchte ich das mal sauber in die Lib übernehmen. Wird aber wohl ein bisschen dauern.

Code:
        private void abc()
        {
            PLCConnection _myConn = new PLCConnection("");
            string IPAddress = "192.168.214.1";


            try
            {
                _myConn.Configuration.CpuIP = !string.IsNullOrEmpty(IPAddress) ? IPAddress : "192.168.214.1";
                _myConn.Configuration.CpuSlot = 4;
                _myConn.Connect();


                #region ACX File laden
                byte[] bAr = _myConn.BinaryUploadFromNC("_N_NC_GD2_ACX");
                byte[] GudFile = new byte[bAr.Length - 24];
                Array.Copy(bAr, 24, GudFile, 0, GudFile.Length);
                #endregion


                #region parse
                var bArX = Separate(GudFile, new byte[] { 4, 0, 0, 0, 0 });
                List<GudVar> lGUD = new List<GudVar>();
                for (int i = 1; i < bArX.Length; i++)
                {
                    string name = string.Empty;
                    int gudIndex = 0;
                    int size = 0;
                    int dim1 = 0;
                    int dim2 = 0;
                    int dim3 = 0;
                    int[] range = new int[0];
                    GudVar.E_NCK_Type nckType = GudVar.E_NCK_Type.NULL;


                    int index = 3;
                    name = Encoding.Default.GetString(bArX[i], index, bArX[i][0]);
                    index += bArX[i][0];


                    for (int j = index; j < bArX[i].Length; j++)
                    {
                        if (bArX[i][j] == 0x20)
                        {
                            int valueBytesCount = bArX[i][j - 2];
                            var value = getValue(bArX[i], valueBytesCount, j + 1);
                            switch (bArX[i][j - 1])
                            {
                                case 0x85:  //String länge
                                    size = value;
                                    break;


                                case 0x88:  //Index der Variablen im GUD-Bereich
                                    gudIndex = value;
                                    break;


                                case 0x89:  //Datentype
                                    getNckType(value, ref size, ref nckType);
                                    break;


                                case 0x98:  //Größe Dimension1
                                    dim1 = value;
                                    break;


                                case 0x99:  //Größe Dimension2
                                    dim2 = value;
                                    break;


                                default:
                                    break;
                            }
                            j += valueBytesCount;
                        }
                        else if (bArX[i][j] == 0x25)
                        {
                            int valueBytesCount = bArX[i][j - 2];
                            var value = getValue(bArX[i], valueBytesCount, j + 1);
                            switch (bArX[i][j - 1])
                            {
                                case 0x67:  //Größe Dimension3
                                    dim3 = value;
                                    break;


                                default:
                                    break;
                            }
                            j += valueBytesCount;
                        }
                    }


                    if (dim3 != 0)
                        range = new int[] { dim1, dim2, dim3 };
                    else if (dim2 != 0)
                        range = new int[] { dim1, dim2 };
                    else if (dim1 != 0)
                        range = new int[] { dim1 };


                    GudVar gud = new GudVar(gudIndex, dim1 != 0, name, range, size, nckType);


                    lGUD.Add(gud);
                }
                #endregion


                var _gud = lGUD[0];
                var ncVar = new NC_Var(0x82, 0x1, _gud.GudNumber, (_gud.IsArray ? 0x0 : 0x1), 0x2D, 1, (int)_gud.Type, _gud.Size);
                var tag = ncVar.GetNckTag();
                _myConn.ReadValue(tag);
                var _value = tag.Value;
            }
            catch (Exception)
            {
            }
            if (_myConn != null)
            {
                if (_myConn.Connected)
                    _myConn.Disconnect();
                _myConn.Dispose();
            }
        }


        private static dynamic getValue(byte[] source, int lenth, int index)
        {
            switch (lenth)
            {
                case 1:
                    return source[index];
                case 2:
                    return BitConverter.ToInt16(source, index);
                case 4:
                    return BitConverter.ToInt32(source, index);
                case 8:
                    return BitConverter.ToInt64(source, index);
                default:
                    var bAr = new byte[lenth];
                    Array.Copy(source, index, bAr, 0, bAr.Length);
                    return bAr;
            }
        }


        private static void getNckType(byte type, ref int size, ref GudVar.E_NCK_Type nckType)
        {
            switch ((GudVar.E_NCK_AcxType)type)
            {
                case GudVar.E_NCK_AcxType.BOOL:
                    nckType = GudVar.E_NCK_Type.BOOL;
                    size = 1;
                    break;


                case GudVar.E_NCK_AcxType.INT:
                    nckType = GudVar.E_NCK_Type.INT;
                    size = 4;
                    break;


                case GudVar.E_NCK_AcxType.CHAR:
                    nckType = GudVar.E_NCK_Type.CHAR;
                    size = 1;
                    break;


                case GudVar.E_NCK_AcxType.REAL:
                    nckType = GudVar.E_NCK_Type.REAL;
                    size = 8;
                    break;


                case GudVar.E_NCK_AcxType.STRING:
                    nckType = GudVar.E_NCK_Type.STRING;
                    break;


                case GudVar.E_NCK_AcxType.AXIS:
                    nckType = GudVar.E_NCK_Type.AXIS;
                    size = 1;
                    break;


                case GudVar.E_NCK_AcxType.FRAME:
                    nckType = GudVar.E_NCK_Type.FRAME;
                    size = 1;
                    break;


                default:
                    break;
            }
        }


        public static byte[][] Separate(byte[] source, byte[] separator)
        {
            var Parts = new List<byte[]>();
            var Index = 0;
            byte[] Part;
            for (var i = 0; i < source.Length; ++i)
            {
                if (Equals(source, separator, i))
                {
                    Part = new byte[i - Index];
                    Array.Copy(source, Index, Part, 0, Part.Length);
                    Parts.Add(Part);
                    Index = i + separator.Length;
                    i += separator.Length - 1;
                }
            }
            Part = new byte[source.Length - Index];
            Array.Copy(source, Index, Part, 0, Part.Length);
            Parts.Add(Part);
            return Parts.ToArray();
        }


        static bool Equals(byte[] source, byte[] separator, int index)
        {
            for (int i = 0; i < separator.Length; ++i)
                if (index + i >= source.Length || source[index + i] != separator[i])
                    return false;
            return true;
        }



    internal class GudVar
    {
        public string Name = string.Empty;
        public E_NCK_Type Type;
        public bool IsArray;
        public int[] Range;
        public int Size;
        public ushort GudNumber;


        /// <summary>
        /// 
        /// </summary>
        /// <param name="GudNumber">Position im GUD-Bereich</param>
        /// <param name="IsArray"></param>
        /// <param name="Name"></param>
        /// <param name="Range">Anzahl an Dimensionen</param>
        /// <param name="Size"></param>
        /// <param name="Type">Datentyp</param>
        internal GudVar(int GudNumber, bool IsArray, string Name, int[] Range, int Size, E_NCK_Type Type)
        {
            this.GudNumber = (ushort)GudNumber;
            this.IsArray = IsArray;
            this.Name = Name;
            this.Range = Range;
            this.Size = Size;
            this.Type = Type;
        }


        internal enum E_NCK_AcxType
        {
            BOOL = 0x0,
            INT = 0x3,
            CHAR = 0x5,
            REAL = 0xA,
            STRING = 0xC,
            FRAME = 0xD,
            AXIS = 0xE
        }


        internal enum E_NCK_Area
        {
            NCK = 0x1,
            CHAN = 0x41 //Channel 1
            //CHAN = 0x3
        }


        internal enum E_NCK_Type
        {
            NULL = 0x0,
            BOOL = 0x1,
            AXIS = 0x2,      //???  
            FRAME = 0x2,    //???
            BYTE = 0x3,
            WORD = 0x4,
            INT = 0x5,
            DWORD = 0x6,
            DINT = 0x7,
            FLOAT = 0x8,
            REAL = 0xF,
            INT64 = 0x12,
            CHAR = 0x13,
            STRING = 0x13
            //FRAME = 0x14 //???
            //AXIS = 0x15 //???            
        }
    }
 
@Jochen

Gibt´s in der Lib ne Methode, mit der ich "symbolisch" auf die SPS (317 und Step7 v5.5) zugreifen kann?
Soweit ich weiß sind die Symbole ja nur offline vorhanden. Somit müsste die Methode aus dem Projekt z.B. den DB oder FB laden und dann anhand des symbolischen Namens die Adresse liefern oder besser gleich den Wert.
 
Wir verwenden an einem Teil unserer Maschinen ein neues Bedienteil und dieses Verursacht, dass z.B. SinuComNc (Siemens) und auch mein FileUpload nicht mehr funktioniert. Der FileUpload vom Sinumeric Operate (HMI) funktioniert jedoch.

WireShark logs im Anhang

@Thomas_v2.1: Kannst du mir wieder beim anpassen der LibNoDave helfen?

FileUpload_SINUMERIK_Operate.jpg

FileUpload_Error.jpg

FileUpload_NoError.jpg

Anhang anzeigen File_upload_20171020.rar
 
Hm, hatte ich die Funktion nicht schon mal irgendwo programmiert? Weil wenn Wireshark das dekodieren kann, dann muss ich mir das ja schon einmal angesehen haben.
Sind die ganzen NC-Funktionen denn schon bei Jochen in der libnodave Version, oder gibt es dazu eine andere Quelle?
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Achso jetzt sehe ich es.
In FileUpload_Error und FileUpload_NoError wird die Upload Funktion verwendet mit der auch ein Baustein in die SPS hochgeladen werden.
FileUpload_SINUMERIK_Operate enthält hingegen die NC spezifischen Befehle, die hatten wir glaube ich schon einmal in eine libnodave kompatible Funktion gegossen.
 
Fasst. Ich hab schon mal mit dir zusammen die Funktion für die NC in die Lib vom Jochen integriert. (Nach dem späteren überarbeiten der SPS Funktion sah diese auch aus, wie die von uns erstellte für die NC)

Nun habe ich aber das Problem, dass wenn das Besagte Bedienpanel läuft die Funktion einen Fehler von der Steuerung bekommt. Daher hab ich mir den Upload des Operate angesehen und dieser funktioniert anders.

Anhang anzeigen libnodave_20171020.rar
 
Die Funktion davePutNCProgram() sollte soweit ich das sehe so funktionieren wie in FileUpload_SINUMERIK_Operate.
Hast du mal eine Aufzeichnung bei der du diese Funktion verwendest und sie nicht funktioniert?
Denn in FileUpload_Error wird diese Funktion nicht verwendet, sondern der Standard-Upload der auch für S7-Programmbausteine verwendet wird.
 
Zurück
Oben