25. VISION-ANWEISUNGEN

25.1.  Übersicht

Diese Seite enthält die Hilfe für die Vision-Anweisungssets. Drücken Sie beim Bearbeiten des Teach Pendant-Programms die Taste F1,[INST], um eine Liste der Teach Pendant-Anweisungen anzuzeigen. Die iRVision-Anweisungen befinden sich unter dem Gruppentitel VISION (nicht zu verwechseln mit PC VISION, das mit einem PC-basierten Vision-System verwendet wird).

Nach Auswahl der VISION-Gruppe werden die Vision-Anweisungen angezeigt. Wählen Sie eine der gewünschten Anweisungen:

  • RUN_FIND

  • GET_OFFSET

  • SET_REFERENCE

  • CAMERA_CALIB

  • VR[].OFFSET

  • VR[].MODELID

25.2.  RUN_FIND

VISION RUN_FIND '...' CAMERA_VIEW[...]

Diese Anweisung beginnt mit einer Aufforderung zur Erfassung des Bilds. Nach dem Erfassen des Bilds wechselt die Steuerung zurück zum Roboter, und der Roboter kann sich bewegen. Der Vision-Prozess wird weiter verarbeitet.

Parameter

Vision Prozess Name: Gibt den Namen des Vision-Prozesses an. Der Name kann in der Liste der gültigen Vision-Prozesse ausgewählt werden.

Nummer der Kameraansicht: Eine Option, die mit einem Vision-Programm mit mehr als einer Kameraaufnahme verwendet wird, und die Bilder können nicht gleichzeitig erfasst werden. Angenommen, es gibt beispielsweise eine Deckenkamera und der Roboter hält ein Werkstück mit zwei Zielöffnungen. Die Zielöffnungen sind zu weit auseinander, als dass sie sich im selben Sichtfeld befinden können. Öffnung 1 wird erfasst, dann bewegt sich der Roboter, und Öffnung 2 wird erfasst. In diesem Fall gäbe es zwei RUN_FIND-Anweisungen, eine für jede Öffnung.

25.3. GET_OFFSET

VISION GET_OFFSET 'VP1' VR[2] JMP LBL[99]

Dieser Befehl stellt ein Ziel-Vision-Register für den Vision-Prozess zur Verfügung. Wenn der Vision-Prozess abgeschlossen ist und die Daten an das entsprechende Vision-Register übertragen wurden, wechselt die Steuerung zurück zum Roboter.

Parameter

Vision Prozess Name: Geben Sie den Namen des Vision-Prozesses ein.

Vision-Registerindex: Geben Sie den Index des Vision-Registers ein, das die Daten empfangen soll.

Sprung zu Kennsatz: Geben Sie den Kennsatz einer Jump-Anweisung ein. Das Programm wechselt zu diesem Kennsatz, wenn das Vision-Programm fehlschlägt oder keine Werkstücke gefunden werden.

25.4. GET_PASSFAIL

Syntax

VISION GET_PASSFAIL 'VP1' R[99]

Mit dieser Anweisung wird das Erf./Fehlg.-Ergebnis eines Vision-Prozesses an ein allgemeines Register übertragen.

Parameter

Vision Prozess Name: Der Name des Vision-Prozesses. Der Name kann in der Liste der überprüften Vision-Prozesse ausgewählt werden.

Pass Fail-Register: Dieses Register enthält das Erf./Fehlg.-Ergebnis.

  • 0 zeigt an, dass der Test fehlgeschlagen ist.

  • 1 zeigt an, dass der Test erfolgreich war.

  • 2 gibt an, dass ein übergeordnetes Tool fehlgeschlagen ist und ein untergeordnetes Tool nicht getestet werden konnte.

Beispiel

VISION GET_PASSFAIL 'INSPECTVP' R[5] ;

25.5. GET_NFOUND

Syntax

VISION GET_NFOUND 'VP1' R[...] [CAMERA_VIEW[...] ]

Diese Anweisung gibt die Anzahl der in der angegebenen Ansicht gefundenen Ziele zurück. Wenn die Kameraansicht nicht angegeben wird, wird standardmäßig Ansicht 1 angezeigt.

Parameter

Vision Prozess Name: Der Name des Vision-Prozesses. Der Name kann in der Liste der gültigen Vision-Prozesse ausgewählt werden.

Nummer gefunden Registernummer:

Nummer der Kameraansicht: (1-4)

Beispiel

VISION GET_NFOUND 'FINDVP' R[6] CAMERA_VIEW[2] ;

25.6.  SET_REFERENCE

VISION SET_REFERENCE 'VP1'

Verwenden Sie diese Anweisung, um die Referenzposition von einem Programm festzulegen. Die Schritte für das Training eines Vision-Prozesses dienen zum Trainieren und Finden eines Vision-Ziels. Legen Sie diese Vision-Position unter "Referenz festlegen" auf Null oder auf die Referenzposition. Vision gibt ein Null-Ergebnis für das Offset zurück. Zeichen Sie die Roboterposition zum Greifen und Ablegen ohne Offset auf. Die Vision-Position und die Roboterposition wurden verknüpft.

Parameter

Vision Prozess Name: Verwenden Sie die Taste [WAHL], um den Vision-Prozess in der Liste der Vision-Prozesse auszuwählen.

25.7. OVERRIDE

Syntax

VISION OVERRIDE 'AST' Wert

Diese Anweisung ändert einen Wert im Vision-Prozess, wie vom Anwendungs-Setup-Tool definiert. Das Anwendungs-Setup-Tool gibt den Namen eines Vision-Prozesses, ein Tool im Vision-Prozess sowie einen Feldwert im Tool an. Der Wert wird nur einmal verwendet. Der Benutzer muss den Override-Wert zurücksetzen, der mit der nächsten VISION RUN_FIND-Anweisung verwendet werden soll, anderenfalls wird der ursprüngliche Vision-Prozess-Wert verwendet.

Parameter

VISION OVERRIDE 'AST' Wert

AST: Name des Anwendungs-Setup-Tool

Wert: An den Vision-Prozess übergebene Eingabewerte

Beispiel

VISION OVERRIDE 'EXPOSURE' 45.0 ; 

In diesem Beispiel wurde ein Anwendungs-Setup-Tool (EXPOSURE) erstellt, um die Belichtungszeit im Vision-Prozess auf .045 Sekunden festzulegen.

25.8.  CAMERA_CALIB

VISION CAMERA_CALIB 'TWOPTCAL' Request=1

Mit dieser Anweisung können Sie das System von einem Teach Pendant-Programm kaibrieren. Dies ist besonders hilfreich für eine robotermontierte Kamera. Checks können regelmäßig ausgeführt werden. Wenn eine Kalibrierung erforderlich ist, kann das Kalibrierungsprogramm ausgeführt werden, um Vision neu zu kalibrieren. Dies ist hilfreich, wenn die Kamera gestoßen oder ersetzt wurde.

Parameter

Kamera Name : Geben Sie den Namen der Kamerakalibrierung ein.

Anforderungsnummer: Geben Sie die Ebenennummer für das zu kalibrierende Raster für die Rasterkalibrierung ein.

25.9.  VR[]. MODELID

R[33]=VR[2].MODELID

Diese Anweisung überträgt die Vision-Prozess-Modell-ID aus dem Vision-Datenregister an ein Register, sodass sie verwendet werden kann, um zu ermitteln, welches Modell gefunden wurde.

Parameter

Registerindex Geben Sie eine Ganzzahl an, um den Index des Zielregisters anzugeben.

Vision-Registerindex: Geben Sie eine Ganzzahl an, um das Quell-Vision-Register anzugeben.

25.10. VR[].MES[]

Syntax

R[RN]=VR[VRN].MES[MN] 

Diese Anweisung ruft einen Messwert aus einem angegebenen Vision-Register ab. Ein Vision-Register enthält zehn Messergebnisse. Der Messwert ist standardmäßig 0.0.

Parameter

Registernummer: Zielregister

Vision-Registernummer: (1-10)

Messnummer: (1-10)

Beispiel

R[5]=VR[1].MES[2] ; 

Dies setzt R[5] mit dem Inhalt des zweiten Messwerts in Vision-Register 1.

25.11. VR[].FOUND_POS[]

Syntax

PR[PRN]=VR[VRN].FOUND_POS[VN] ; 

Diese Anweisung gibt das gefundene Positionsergebnis einer angegebenen Nummer eine Vision-Registers zurück. Wenn eine Ansicht nicht verwendet wird, ist FOUND_POS gleich $NILPOS.

Parameter

PRN: Die Nummer des Zielpositionsregisters.

VRN: Vision-Register-Nummer (1–10).

VN: Ansichtsnummer (1-4)

Beispiel

PR[3]=VR[2].FOUND_POS[1] ; 

PR[3] mit Ansicht 1 gefundene Position von VisionRegister 2 wird geladen.

25.12.  VR[].OFFSET

PR[1]=VR[2].OFFSET

Diese Anweisung überträgt das Vision-Offset aus dem Vision-Datenregister an ein Positionsregister, sodass es mit einer Bewegungsanweisung mit einer Offset-Klausel verwendet werden kann.

Parameter

Positionsregister-Index: Geben Sie eine Ganzzahl an, um den Index des Zielpositionsregisters anzugeben.

Vision-Registerindex: Geben Sie eine Ganzzahl an, um das Quell-Vision-Register anzugeben.

25.13. VR.[].ENC

Syntax

R[RN]=VR[VRN].ENC ; 

Diese Anweisung überträgt den Encoder-Wert des Vision-Registers an ein Zielregister. Wird mit Visual Tracking verwendet. Der Encoder-Wert wird zum Zeitpunkt der Erfassung der Kameraaufnahme aufgezeichnet.

Parameter

RN: Registernummer

VRN: Vision-Registernummer: (1-10)

Beispiel

R[4]=VR[2].ENC ; 

R[4] wird mit dem Encoder-Wert von VR[2] festgelegt.

25.14. RUN_FIND SR[]

Syntax

VISION RUN_FIND SR[SRN] {CAMERA_VIEW[CVN] }

Diese Anweisung fordert die Erfassung einer Vision-Ansichtsaufnahme an. Der Name des Vision-Prozesses wird von einem Zeichenfolgenregister übergeben. Wenn die Kameraansicht ausgelassen wird, ist der Standardwert 1. Siehe auch RUN_FIND.

Parameter

SRN: Die Zeichenfolgenregisternummer (1-4) des Namens des Vision-Prozesses.

CVN: Wenn die Kameraansichtsnummer (1-4) ausgelassen wird, ist der Standardwert 1.

Beispiel

VISION RUN_FIND SR[1] CAMERA_VIEW[2]

25.15. GET_OFFSET SR[]

Syntax

VISION GET_OFFSET SR[SRN] VR[VRN] JMP LBL[JL] ; 

Diese Anweisung überträgt die Ergebnisdaten des Vision-Prozesses an ein Vision-Register. Das Zeichenfolgenregister enthält den Namen des Vision-Prozesses. Siehe auch GET_OFFSET-Anweisung.

Parameter

SRN: Die Zeichenfolgenregisternummer (1-4) enthält den Namen des Vision-Prozesses.

VRN: Nummer des Ziel-Vision-Registers (1-10)

JL: SprungLabel-Nummer für kein Werkstück oder Fehler.

Beispiel

VISION GET_OFFSET SR[1] VR[2] JMP LBL[998] 

25.16. SET_REF SR[]

Syntax

VISION SET_REFERENCE SR[SRN] 

Mit dieser Anweisung wird die Sollposition in einem Bilderkennungsprozess festgelegt. Der Name des Vision-Prozesses wird von einem Zeichenfolgenregister übergeben. Siehe auch SET_REFERENCE-Anweisung.

Parameter

Zeichenfolgenregisternummer: Das Zeichenfolgenregister (1-4) enthält den Namen des Vision-Prozesses.

Beispiel

VISION SET_REFERENCE SR[2] 

25.17. CAMERA_CAL SR[]

Syntax

VISION CAMERA_CALIB SR[SRN] Request=RN  

Diese Anweisung findet die Ebenendaten für ein Kamerakalibrierungs-Tool. Sie ermöglicht dem Benutzer, Vision über ein TP-Programm erneut zu kalibrieren. Der Name der Kamerakalibrierung wird von einem Zeichenfolgenregister übergeben. Siehe auch CAMERA_CALIB-Anweisung.

Parameter

SRN: Die Zeichenfolgenregisternummer (1-4) enthält den Namen der Kamerakalibrierung.

RN: Anforderungsnummer

Beispiel

VISION CAMERA_CAL SR[2] REQUEST=1

25.18. GET_PASSFAIL SR[]

Syntax

VISION GET_PASSFAIL SR[SRN] R[PFN] ; 

Mit dieser Anweisung wird das Erf./Fehlg.-Ergebnis eines Vision-Prozesses an ein allgemeines Register übergeben. Siehe auch GET_PASSFAIL-Anweisung.

Parameter

SRN: Die Zeichenfolgenregisternummer (1-4) enthält den Namen des Vision-Prozesses.

PFN: Die Nummer des Erf./Fehlg.-Registers enthält das Vision-Ergebnis.

  • 0 zeigt an, dass der Test fehlgeschlagen ist

  • 1 zeigt an, dass der Test erfolgreich war

  • 2 gibt an, dass ein übergeordnetes Tool fehlgeschlagen ist und ein untergeordnetes Tool nicht getestet werden konnte.

Beispiel

GET_PASSFAIL SR[2] R[5] ;

25.19. GET_READING

Syntax

VISION GET_READING 'BARCODEVP' SR[1] R[2] JMP LBL[997] 

Diese Anweisung gibt den Barcode-Wert in einem Zeichenfolgenregister zurück. Die Anzahl der Zeichen wird im angezeigten Register zurückgegeben. Beachten Sie, dass im Fall eines Überlaufs kein Fehler angezeigt wird und die Überlaufzeichen verloren gehen. Die maximale Größe des Zeichenfolgenregisters ist 128. Der Benutzer muss das Register auf die Anzahl der gelesenen Zeichen hin überprüfen.

Parameter

Parameter

Beispiel

VISION GET_READING 'BARCODEVP' SR[1] R[2] JMP LBL[997]

25.20.  BEISPIELPROGRAMM

Das folgende Beispiel erläutert, wie die Vision-Anweisungen zusammen verwendet werden:

Zeile 2 findet den Vision-Prozess VP1

Die Ergebnisse aus Zeile 3 werden in Vision-Register 5 gespeichert. Wenn keine Werkstücke gefunden wurden, springt das Programm zu LBL 999.

Zeile 4, das Offset, wird an PR[1] übergeben

Zeile 5, die Modell-ID, wird in R[4] gespeichert

Zeile 7 ist der Annäherungspunkt

Zeile 8 ist der Greifpunkt

Zeile 9 löst den Greifer aus

Zeile 10 Bewegung durch die Annäherung.

 1:  LBL[1]
 2:  VISION RUN_FIND 'VP1'
 3:  VISION GET_OFFSET 'VP1' VR[5]
  :  JMP LBL[999]
 4:  PR[1]=VR[5].OFFSET
 5:  R[4]=VR[1].MODELID
 6:
 7:L @P[1] 500MM/SEC CNT75 Offset,PR[1]
 8:L @P[2] 100MM/SEC FINE Offset,PR[1]:
 9:  CALL PICK
10:L @P[1] 300mm/sec CNT75 Offset,PR[1]
  :  Offset,PR[1]
 11: ENDE
 12:
 13: LBL[999:NO PART]
 14: UALM[1]
[End]

25.21. Beispielprogramm für die Neukalibrierung und Verifizierung

In diesem Beispiel kalibriert der Benutzer neu und verifiziert die Genauigkeit (.05 mm). Die Kamera ist auf dem Roboter montiert. Das Kalibrierungsraster ist dasselbe Koordinatensystem wie das Anwendungskoordinatensystem (uframe 1).

Zeilen 1-6 bewegen die Kamera zur Kalibrierungsebene 1
Zeilen 7-9 bewegen die Kamera zur Kalibrierungsebene 2
Zeilen 11-12 bringen den Roboter zurück zu Ebene 1 und bereitet das Suchen des großen Kreises eines Kalibrierungsrasters vor. Der Benutzer muss das Multi-Fenster-Tool mit einem untergeordneten GPM für den großen Kreis verwenden. Im Multi-Fenster-Tool kann der Benutzer ein Fenster für jeden großen Kreis definieren und eine Modell-ID als 0 und den Suchfenster-Index oder (1-4) zurückgeben.
Zeilen 15-16 testen die Anzahl der Kreise (muss 4 sein)
Zeilen 17-22 laden R[9] und R[10] mit X und Y
Zeilen 23–52 überprüfen, ob die Kreismitten korrekt sind.
   1:  !Re-Calib Robot Mounted Cam ;
   2:  UFRAME_NUM=1 ;
   3:  UTOOL_NUM=1 ;
   4:L P[1:Plane 1] 2000mm/sec FINE    ;
   5:  WAIT    .25(sec) ;
   6:  VISION CAMERA_CALIB 'WTICALB' Request=1 ;
   7:L P[2:Plane 2] 2000mm/sec FINE    ;
   8:  WAIT    .25(sec) ;
   9:  VISION CAMERA_CALIB 'WTICALB' Request=2 ;
  10:  !Test for valid calibration ;
  11:L P[1:Plane 1] 2000mm/sec FINE    ;
  12:  R[6:Window index]=0    ;
  13:  LBL[20] ;
  14:  VISION RUN_FIND 'BIGCIRCLE'    ;
  15:  VISION GET_NFOUND 'BIGCIRCLE' R[7]    ;
  16:  IF R[7:N Big Circles]<>4,JMP LBL[999] ;
  17:  LBL[50:Loop] ;
  18:  VISION GET_OFFSET 'BIGCIRCLE' VR[1] JMP LBL[200] ;
  19:  R[8]=VR[1].MODELID ;
  20:  PR[6]=VR[1].FOUND_POS[1] ;
  21:  R[9:X]=PR[6,1:Found Pos]    ;
  22:  R[10:Y]=PR[6,2:Found Pos]    ;
  23:  SELECT R[8:MODEL ID]=1,JMP LBL[110] ;
  24:         =2,JMP LBL[120] ;
  25:         =3,JMP LBL[130] ;
  26:         =4,JMP LBL[140] ;
  27:         ELSE,JMP LBL[999] ;
  28:  LBL[110:Origin Circle] ;
  29:  IF R[9:X]>.05,JMP LBL[999] ;
  30:  IF R[9:X]<-.05,JMP LBL[999] ;
  31:  IF R[10:Y]>.05,JMP LBL[999] ;
  32:  IF R[10:Y]<-.05,JMP LBL[999] ;
  33:  JMP LBL[50] ;
  34:  LBL[120:Inner X  Circle] ;
  35:  IF R[9:X]>15.05,JMP LBL[999] ;
  36:  IF R[9:X]<14.95,JMP LBL[999] ;
  37:  IF R[10:Y]>.05,JMP LBL[999] ;
  38:  IF R[10:Y]<-.05,JMP LBL[999] ;
  39:  JMP LBL[50] ;
  40:  LBL[130:Outer X Circle] ;
  41:  IF R[9:X]>30.05,JMP LBL[999] ;
  42:  IF R[9:X]<29.95,JMP LBL[999] ;
  43:  IF R[10:Y]>.05,JMP LBL[999] ;
  44:  IF R[10:Y]<-.05,JMP LBL[999] ;
  45:  JMP LBL[50] ;
  46:  LBL[140:Y Circle] ;
  47:  IF R[9:X]>.05,JMP LBL[999] ;
  48:  IF R[9:X]<-.05,JMP LBL[999] ;
  49:  IF R[10:Y]>-14.95,JMP LBL[999] ;
  50:  IF R[10:Y]<-15.05,JMP LBL[999] ;
  51:  JMP LBL[50] ;
  52:  LBL[200:Success] ;
  53:  END
  54:  LBL[999:Error]
  55:  UALM[1]

25.22. Beispielprogramm für das Barcode-Lesen

Dieses Programm liest den Barcode mit "Barcode" VP . Der Wert des Barcode-Labels wird in SR[1] gespeichert. Register [1] wurde programmiert, um das Ergebnis anzuzeigen.

   1:  VISION RUN_FIND 'BARCODE'    ;
   2:  VISION GET_READING 'BARCODE' SR[1] R[12] JMP LBL[999] ;
   3:  IF R[12]>128,JMP LBL[20] ;
   4:  LBL[10:Success] ;
   5:  R[1]=0    ;
   6:  END
   7:   ;
   8:  LBL[20:SR Overflow] ;
   9:  R[1]=2    ;
  10:  END
  11:   ;
  12:  LBL[999:Error] ;
  13:  R[1]=(-1)    ;

25.23. Inspektions-Beispielprogramm

R[1] gibt das Ergebnis des InspectPart-Vision-Prozesses an. Wenn R[1] eine 1 enthält, war er erfolgreich. Wenn R[1] 0 ist, ist er fehlgeschlagen. Wenn R[1] 2 ist, konnte der Test nicht ausgeführt werden, weil das übergeordnete Tool den Testbereich nicht finden konnte.

   1:  VISION RUN_FIND 'INSPECTPART'    ;
   2:  VISION GET_PASSFAIL 'INSPECTPART' R[1] ;

25.24. 2D Multi-Ansicht-Vision-Prozess

Verwenden Sie Multi-Ansicht 1 "Kamera Roboter", um ein Werkstück zu greifen. Öffnung 1 darf nicht größer als 1 Zoll sein. Zeilen 1-6 bewegen den Roboter zu Ansicht 1, um ein Ziel zu finden. Wenn kein Ziel gefunden wird, zu Ansicht 2 wechseln.

Zeilen 7-10, Der Roboter wird zu Ansicht 2 verfahren und findet das zweite Ziel.
Zeilen 12-14 bieten eine praktische Methode zum Festlegen der Referenzposition.
Der Benutzer setzt R[4](Referenz-Flag setzen) für eine einmalig festgelegte Referenz auf 1.
Zeilen 16-18 finden das Werkstück und speichern die Messung [1] in R[15] (Lochdurchmesser).
Zeile 19 überspringt das Greifen, wenn das Loch zu groß ist.
Zeile 20 Das Werkstück wird mit dem Vision-Register als Koordinatensystem-Offset gegriffen.
   1:  UFRAME_NUM=1 ;
   2:  UTOOL_NUM=1 ;
   3:L P[1] 2000mm/sec FINE    ;
   4:  VISION RUN_FIND 'FINDPART' CAMERA_VIEW[1] ;
   5:  VISION GET_NFOUND 'FINDPART' R[11]    ;
   6:  IF R[11:N found]<>1,JMP LBL[996] ;
   7:L P[2] 2000mm/sec FINE    ;
   8:  VISION RUN_FIND 'FINDPART' CAMERA_VIEW[2] ;
   9:  VISION GET_NFOUND 'FINDPART' R[11]    ;
  10:  IF R[11:N found]<>1,JMP LBL[997] ;
  11:   ;
  12:  IF R[4]<>1,JMP LBL[20] ;
  13:  VISION SET_REFERENCE 'FINDPART' ;
  14:  R[4]=0    ;
  15:   ;
  16:  LBL[20] ;
  17:  VISION GET_OFFSET 'FINDPART' VR[1] JMP LBL[998] ;
  18:  R[15]=VR[1].MES[1] ;
  19:  IF R[15:Diameter]>25.4,JMP LBL[999] ;
  20:L P[3] 2000mm/sec FINE VOFFSET,VR[1]    ;

25.25. Beispielprogramm für das Aufrufen von Vision durch das Zeichenfolgenregister

Das erste Programm bietet eine einfache Methode zum Laden eines Zeichenfolgenwerts in ein Zeichenfolgenregister.

PROGRAMM GetPartName
   1:  !Set SR[1] to passed str ;
   2:  SR[1]=AR[1]    ;
Zeilen 1-5 laden den entsprechenden Namen des Vision-Prozesses in SR[1], und zwar mit der Routine
GetpartName.
Zeilen 6-7 rufen Vision nach dem Inhalt von SR[1] auf.
Zeilen 8-9 verfahren zur Annäherungsposition (unter Verwendung eines Tool-Offsets)
Angenommen, PR[3] ist (0,0,50,0,0,0) und dann die Greifposition
   1:  SELECT R[20:VP Name Index]=1,CALL GETPARTNAME('FindPart1') ;
   2:         =2,CALL GETPARTNAME('FindPart2') ;
   3:         =3,CALL GETPARTNAME('FindPart3') ;
   4:         =4,CALL GETPARTNAME('FindPart4') ;
   5:         ELSE,JMP LBL[999] ;
   6:  VISION RUN_FIND SR[1]    ;
   7:  VISION GET_OFFSET SR[1] VR[1] JMP LBL[998] ;
   8:L P[1] 2000mm/sec FINE VOFFSET,VR[1] Tool_Offset,PR[3]    ;
   9:L P[1] 2000mm/sec FINE VOFFSET,VR[1]    ;