WinCC ScreenItems indiziert durchlaufen

Medium

Level-1
Beiträge
66
Reaktionspunkte
1
Zuviel Werbung?
-> Hier kostenlos registrieren
Vorab: WinCC 7.3, Server/Client Projekt, Clients sind PCs

Hallo liebe Helfer,

ich schreibe gerade ein Faceplate, bei dem ich in einem VBScript durch alle Bild-Objekte des Faceplates iterieren muss.
Hintergrund ist, dass die normalen Windows-Radio-Boxen zu klein sind, und auch nicht mehrspaltig so darstellbar sind, dass nachher immer nur ein Item selektiert sein kann. Ich möchte das Verhalten daher mit Buttons nachbauen, die entweder keinen Text haben, oder ein "X". Jeder Button ist über seinen Namen eindeutig identifiziert: BLM001, BLM002, ... ...

Wird nun ein Button gedrückt, wird die Zahl im Namen (Right(Item.ObjectName, 3)) in eine Faceplate-Variable geschrieben (32 Bit Ganzzahl). Das klappt auch, ich lasse mir den Wert probehalbar in einem E/A-Feld anzeigen. Bei Änderung dieses Wertes möchte ich nun durch alle Objekte gehen, schauen ob der Name mit "BLM" anfängt, und wenn ja abhängig von dem Wert meiner Variablen ein "X" oder ein Leerzeichen als Text setzen.

Obwohl ich bei meiner bisherigen Recherche immer wieder was davon gelesen habe, dass man ScreenItems() in einem Faceplate gar nicht nutzen kann, und schon gar nicht Properties setzen, klappt folgendes jedoch wunderbar:
Code:
Sub OutputValue_OnPropertyChanged(Byval Item, Byval value) 
    ScreenItems("BLM001").Text = "H"
End Sub
Der Code wird bei Änderung des Ausgabewertes in dem E/A-Feld ausgeführt, welches die Nummer meines zuletzt gedrücken Buttons beinhaltet. BLM001 hat tatsächlich zur Runtime dann den Text "H", während er im Editor noch den Text "X" hat. Also scheint man ScreenItems wohl DOCH in einem Faceplate nutzen zu können, und Properties schreiben geht dann scheinbar auch.

Die Siemens VBS Referenz suggeriert, dass man ScreenItems() nicht nur mit Namen, sondern auch mit einem numerischen Index nutzen können soll. Aber genau da hapert es jetzt:
Code:
Sub OutputValue_OnPropertyChanged(Byval Item, Byval value)        
    Dim obj
    Dim i
    For i = 1 To ScreenItems.Count
        obj = ScreenItems(i)
        If Left(obj.ObjectName, 3) = "BLM" Then
            If Right(obj.ObjectName, 3) = SmartTags("SelectedLM").Value Then
                obj.Text = "X"
            Else
                obj.Text = " "
            End If
        End If
    Next
End Sub
Code, den ich vor die Schleife setze, wird noch ausgeführt. Danach leider nichts mehr, und ich bekomme auch keine Fehlermeldungen irgendwelcher Art. Es passiert einfach nix. (Es werden auch keine Leerzeichen gesetzt, die Buttons haben alle vorab ein "-" als Text um das zu prüfen.)

Wie muss ich das richtig machen?
Danke euch schon mal!

EDIT: ScreenItems.Count ist 0! Lustigerweise habe ich das so ermittelt:
Code:
	ScreenItems("Testfeld").Text = ScreenItems.Count
"Testfeld" ist ein Statischer Text der auf dem Faceplate liegt. Wenn ich also per Name dran komme, scheint in der Liste "ScreenItems" ja durchaus etwas drin zu sein. Warum ist dann aber Count 0?? Da komme ich jetzt nicht mehr mit.
 
Zuletzt bearbeitet:
Zuviel Werbung?
-> Hier kostenlos registrieren
Dann müsste ich mich für jeden Button durch die Klickorgiendialoge quälen. Nein, das tut man sonst schon genug. Ich wollte eigentlich eine elegante Lösung, bei der man Buttons zufügen kann, indem man einfach einen bestehenden kopiert und dessen Namen ändert. Und es sollen nachher auch noch andere Sachen passieren an dieser Stelle, die definitiv Scripting erfordern. (Strings aus der SPS lesen, konkatenieren und an das Parent-Fenster zurück liefern etc.)

Bitte auch mein Edit im 1. Beitrag beachten. Seltsame Dinge.
 
Da würde ich mir eher eine Konstante hinterlegen, wie viele Elemente vorhanden sind, und Elemente dann passend benennen (BLM001...BLM099). Oder du hinterlegst die Konstante als Wert in einem unsichtbaren EA-Feld. Zyklisch alle Elemente durchzugehen ist sicher auch nicht die schnellste Lösung.

Warum das mit dem Durchgehen der Elemente nicht funktioniert, weiß ich allerdings auch nicht aus dem stehgreif. Manche Funktionen sind auch nicht für die Runtime gedacht, sondern nur für Konfiguration (CS).
 
Probier mal so in der Art:
Code:
Dim obj
Dim objScreen
Dim i
Dim name

Set objScreen = HMIRuntime.Screens("DeinBildname")

For i = 1 To objScreen.ScreenItems.Count
   name = objScreen.ScreenItems.Item(i).ObjectName
   HMIRuntime.Trace "ObjectName[" & i & "] = " & name & vbNewLine
Next
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Gut, das könnte ich machen. Es gibt zwar Lücken in der Nummerierung (die Zahlen korrespondieren direkt mit Tanknummern), aber das sollte dennoch flott genug sein. Dann bastel ich mir die Objektnamen zur Indizierung eben als Strings zusammen. (Da rollen sich mir, als eigentlich Software-Engineer zwar die Fußnägel auf, aber wenn Siemens drauf steht erwartet vermutlich eh niemand viel Eleganz oder Performanz :D )

Danke für die Anregung! Das sollte, nach aktuellem Kenntnisstand, eigentlich klappen.

Edit: Den Umweg über das "Elternbild" probiere ich auch! Allerdings erst morgen. Spät genug geworden für heute. Danke nochmals!
 
Zuletzt bearbeitet:
Wenn das dynamisch sein soll, würde ich vielleicht anstelle von selbstgestalteten Radiobuttons ein Listenfeld verwenden. Das lässt sich dann auch elegant per Skript füllen und abfragen, bekommt Scollbalken wenn es nicht mehr in dein Bild hineinpasst usw.
 
Wenn das dynamisch sein soll, würde ich vielleicht anstelle von selbstgestalteten Radiobuttons ein Listenfeld verwenden. Das lässt sich dann auch elegant per Skript füllen und abfragen, bekommt Scollbalken wenn es nicht mehr in dein Bild hineinpasst usw.
Das hatte ich auch anfangs überlegt. Leider kann ich dort den einzelnen Einträgen keine "non-visuellen Daten" mit anhängen, sodass ich unsere internen Tanknummern (die für die Bediener keine Bedeutung haben) mit in den Text aufnehmen müsste. (Leider ist es nicht Tank 1-x, sondern 3, 4, 5, 8, 11, usw, so dass ich den Selektierten Index nicht durch eine generelle Formel in die zugehörige Tanknummer überführen kann.) Die interne Tanknummer brauche ich aber, da die Schnittstelle zu unserem Dosier-Baustein diese benötigt. Und das ganze hier wird meine Quell-Auswahl.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
Na geil. So geht es nicht, weil man es wohl für eine tolle Idee gehalten hat statt einfach Null zurückzugeben, das Script einfach wortlos abzubrechen wenn es kein Objekt mit Namen "Foo" gibt, und man Set obj = ScreenItems("Foo") aufruft. Dadurch kann ich dann nicht mehr mit meinem "Text-Index" über alle Buttons von x bis y iterieren, weil es Lücken darin gibt, wo einfach keine Buttons zu der Nummer existieren. Hab den Kaffee schon wieder auf für heute mit diesem ganz tollen Scripting, das Siemens da eingebaut hat...
Lasst mich raten: WinCC VBS kennt auch keine try...except Konstrukte hm?

Edit: Sorry, zu früh gemotzt. Siemens kann ja nichts für das seltsame Error-Handling in VBS. Mit einem "On Error Resume Next" und Abfrage der gobalen "Err" Variablen kann ich was zaubern. Scusi!
 
Zuletzt bearbeitet:
HI,

ich kenne deinen Schmerz ;)

Du kannst folgendes Versuchen:

Code:
'Starting error routine - Starten der Fehlerroutine
On Error Resume Next

'do your stuff


'Check if any errors happend 
If Err.Number <> 0 Then
	ShowSystemAlarm "Error #" & CStr(Err.Number) & " " & Err.Description
	Err.Clear
	Exit Sub
End If

Gruß Thomas
 
Hallo

Ich löse das so

Code:
[FONT=Courier New]' Alle Dialog-Elemente (benannt "DisconWarnDialog_1_...") ausblenden
For Each ScreenItem In HmiRuntime.Screens(strBildName).ScreenItems
    ObjName=ScreenItem.ObjectName
    If InStr(ObjName, "DisconWarnDialog_1_")=1 Then
        ScreenItem.Visible=False
    End If
Next[/FONT]

For Each durchläuft alle Elemente
 
Zuviel Werbung?
-> Hier kostenlos registrieren
@IckeSI: So ähnlich hatte ich es jetzt auch versucht. Das Clearen ist aber eine gute Idee, gleich eingebaut. So schaut meine Sub jetzt aus:
Code:
Sub OutputValue_OnPropertyChanged(Byval Item, Byval value)                        
    On Error Resume Next
    Dim obj
    Dim i
    Dim n
    For i = ScreenItems("TankMin").Text To ScreenItems("TankMax").Text
        If i < 10 Then
            n = "BLM00" & i
        Else
            If i < 100 Then
                n = "BLM0" & i
            Else
                n = "BLM" & i
            End If
        End If
        Set obj = ScreenItems(n)
        If Err.Number = 0 Then
            If i = SmartTags("SelectedLM").Value Then
                obj.Text = "X"
            Else
                obj.Text = " "
            End If
        Else
            Err.Clear
        End If
    Next
End Sub
Klappt jetzt genau wie ich mir es vorgestellt habe. Danke nochmals an euch!

Edit: @S7_PN: Das hatte ich ganz am Anfang auch so, allerdings wird die For Each Schleife niemals betreten. Das wird damit zusammen hängen, dass ScreenItems.Count auch 0 ergibt. Ich vermute, dass sich das etwas seltsam verhält, weil ich es in einem Faceplate, nicht einem Bild mache.

Edit2: Die Benamsung werde ich aber noch ändern, so dass ich mir die blöde Fallunterscheidung wegen der führenden Nullen sparen kann.
 
Zuletzt bearbeitet:
Hallo Medium,
For Each ScreenItem In HmiRuntime.Screens(strBildName).ScreenItems

durchläuft alle Elemente eines Bildes, ein "ScreenItems.Count" ist garnicht notwendig,

ObjName=ScreenItem.ObjectName

lädt dann den Namen des jeweiligen Objekt
es, mann muss dann nur nur filtern ob die for each Schleife das objekt hat, welches man ändern will


Code:
    For Each ScreenItem In HmiRuntime.Screens(strBildName).ScreenItems
        If ScreenItem.Type = "HMITextField" Then 
            ObjName=ScreenItem.ObjectName
            If Left(ObjName, COMMON_TB_NAME_LEN)=COMMON_TB_NAME_PART Then
                ScreenItem.Text=""
                ScreenItem.BackColor=ColorOfBg
            End If
            If InStr(1, ObjName, "tb_Diag_PnMeld_") Then
                ScreenItem.Visible=False
            ElseIf ((InStr(1, ObjName, "tb_Diag_LastErr_")) Or (InStr(1, ObjName, "tb_Diag_ErrCnt_"))) Then
                ScreenItem.Visible=True
            End If
        End If
    Next


Mit ScreenItem.Type = "HMITextField" kann man auch gezielt z.B. nur nach Text-Feldern filtern
 
Schon klar, dass man .Count für eine ForEach Schleife nicht braucht. Aber probiere es mal aus: Versuche damit mal in einem Faceplate-Typen über alle Bildelemente zu iterieren. Ich wette, dass die Schleife nichtmals betreten wird.
 
Zuviel Werbung?
-> Hier kostenlos registrieren
hi,

TIA v13:

außerhalb Faceplate (Bildbaustein)
Code:
For Each ScreenItem In HmiRuntime.ActiveScreen.ScreenItems
        SmartTags("HMI_ItemCount") = SmartTags("HMI_ItemCount") + 1
Next

innerhalb Faceplate (Bildbaustein)
Code:
For Each ScreenItem In Me.HmiRuntime.ActiveScreen.ScreenItems
        SmartTags("HMI_ItemCount") = SmartTags("HMI_ItemCount") + 1     
Next

ggf. Hilft es

Gruß Thomas
 
Zurück
Oben