Technische Anleitung: Erweiterung des VisionPro ToolBlockLoad
Eine umfassende Schritt-für-Schritt-Anleitung zur Integration von Digitalausgangssteuerung und TCP/IP-Kommunikation in Ihre Cognex VisionPro-Anwendung.
Teil 1: Ansteuerung eines Digitalausgangs
1
Zielsetzung
Das Standard-Cognex-Beispielprojekt ToolBlockLoad wird so erweitert, dass es nach jeder Ausführung des ToolBlocks prüft, ob die Inspektion erfolgreich war (InspectionPassed == true). Bei einem positiven Ergebnis wird ein Impuls an einen digitalen Ausgang einer CC24 I/O-Karte gesendet.
2
Voraussetzungen
  • Visual Studio mit C# ist installiert
  • Cognex VisionPro ist installiert
  • Eine Cognex CC24 I/O-Karte ist physikalisch installiert und die Treiber sind eingerichtet
  • Das ToolBlockLoad-Beispielprojekt ist geöffnet
  • Die verwendete .vpp-Datei verfügt über einen booleschen Ausgangsterminal mit dem Namen InspectionPassed
Schritt 1: Hinzufügen der using-Anweisungen
Um auf die I/O-Hardware zugreifen zu können, müssen die entsprechenden VisionPro-Bibliotheken eingebunden werden. Die Datei Form1.cs wird geöffnet und die folgenden Zeilen werden zu den bestehenden using-Anweisungen hinzugefügt:
using Cognex.VisionPro.Comm; // Stellt Basisfunktionen für die Kommunikation bereit using Cognex.VisionPro.IO; // Stellt Klassen für den direkten I/O-Zugriff bereit
Diese Namespaces ermöglichen den Zugriff auf die CC24-Kommunikationskarte und ihre I/O-Funktionen, was essentiell für die Ansteuerung der Digitalausgänge ist. Die Comm-Bibliothek bietet Basisklassen für die Kommunikation, während die IO-Bibliothek spezifische Klassen für die Steuerung der Ein- und Ausgänge bereitstellt.
Schritt 2: Deklarieren der Klassenvariablen
Innerhalb der Form1-Klasse werden bei den anderen Variablendeklarationen die Variablen für die Kommunikationskarte und die Precision I/O-Schnittstelle hinzugefügt.
public partial class Form1 : Form { CogImageFileTool mIFTool; CogAcqFifoTool mAcqTool; long numPass = 0; long numFail = 0; // NEU: Variablen für die I/O-Karte private CogCommCard mCard; private CogPrio mPrio; // Precision I/O Schnittstelle //... restlicher Code... }
Die CogCommCard-Variable repräsentiert die physische CC24-Karte im System, während CogPrio die Precision I/O-Schnittstelle bereitstellt, die eine präzise Steuerung der digitalen Ein- und Ausgänge ermöglicht. Diese Variablen müssen auf Klassenebene deklariert werden, damit sie in verschiedenen Methoden der Form zugänglich sind.
Schritt 3: Initialisieren der I/O-Hardware
Die Initialisierung der Hardware erfolgt im Konstruktor der Form. Hier wird die Verbindung zur Karte hergestellt und das Verhalten des Ausgangs (der Impuls) definiert. Der Code enthält eine Fehlerbehandlung für den Fall, dass keine Karte gefunden wird.
try { // Sucht nach allen installierten Cognex-Kommunikationskarten. CogCommCards cards = new CogCommCards(); if (cards.Count > 0) { // Greift auf die ERSTE gefundene Karte in der Sammlung zu. mCard = cards[0]; // Holt die Schnittstelle für das Precision I/O-System. mPrio = mCard.DiscreteIOAccess.CreatePrecisionIO(); if (mPrio != null) { // Deaktiviert zunächst alle alten Ereignisse mPrio.DisableEvents(); mPrio.Events.Clear();
Die Initialisierung umfasst mehrere wichtige Schritte: Zuerst werden alle verfügbaren Kommunikationskarten ermittelt, dann wird die erste verfügbare Karte ausgewählt und die Precision I/O-Schnittstelle erstellt. Nach der Bereinigung alter Ereignisse wird ein neues Ereignis für den Impulsausgang konfiguriert.
Fortsetzung: Initialisieren der I/O-Hardware
// Erstellt ein neues Precision I/O-Ereignis für den "PASS"-Fall. CogPrioEvent passPulseEvent = new CogPrioEvent(); passPulseEvent.Name = "PassSignalPulse"; // Eindeutiger Name für das Ereignis // Definiert die Aktion: Ein Impuls auf Ausgang 0. CogPrioEventResponseLine response = new CogPrioEventResponseLine( CogPrioBankConstants.OutputBank0, // Bank der Ausgänge 0, // Ausgangsnummer 0 CogPrioOutputLineValueConstants.PulseHigh, // Aktion: einen High-Impuls senden 50, // Dauer des Impulses in Millisekunden CogPrioDelayTypeConstants.None, // Keine Verzögerung 0); // Wert der Verzögerung // Fügt die definierte Reaktion zum Ereignis hinzu. passPulseEvent.ResponsesLine.Add(response); // Fügt das Ereignis zur Ereignissammlung der Karte hinzu. mPrio.Events.Add(passPulseEvent); // Aktiviert das Precision I/O-System. Die Karte ist nun bereit. mPrio.EnableEvents(); } } else { MessageBox.Show("Keine Cognex CC24 Kommunikationskarte gefunden.", "Hardware-Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning); mCard = null; mPrio = null; } } catch (Exception ex) { MessageBox.Show("Fehler bei der Initialisierung der E/A-Karte: " + ex.Message, "Hardware-Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error); mCard = null; mPrio = null; }
Schritt 4: Auslösen des Ausgangssignals
Die Methode Subject_Ran wird nach jeder Ausführung des ToolBlocks aufgerufen. Hier wird die Logik eingefügt, um das Ergebnis auszulesen und den Impuls auszulösen:
void Subject_Ran(object sender, EventArgs e) { // ... (bestehender Code zur Zählung von Pass/Fail) ... // NEUE LOGIK ZUR STEUERUNG DES AUSGANGS // Prüfen, ob das Prio-System erfolgreich initialisiert wurde. if (mPrio != null) { // Den öffentlichen Ausgang "InspectionPassed" aus dem ToolBlock auslesen. bool inspectionPassed = (bool)(cogToolBlockEditV21.Subject.Outputs["InspectionPassed"].Value); if (inspectionPassed) { // Wenn die Inspektion erfolgreich war, löse das vordefinierte "PassSignalPulse"-Ereignis aus. mPrio.Events["PassSignalPulse"].Schedule(); } } // ... (restlicher Code zur Anzeige der Ergebnisse) ... }
Schritt 5: Bereinigen der Ressourcen
Es ist wichtig, die Verbindung zur Hardware sauber zu beenden, wenn die Anwendung geschlossen wird. Der Code zum Deaktivieren und Freigeben der I/O-Ressourcen wird in der Dispose-Methode hinzugefügt.
protected override void Dispose(bool disposing) { // NEUEN CODE ZUR BEREINIGUNG HINZUFÜGEN if (mPrio != null) { // Deaktiviert das Precision I/O-System beim Schließen der Anwendung. mPrio.DisableEvents(); } if (mCard != null) { // Gibt die Ressourcen der Karte frei. mCard.Dispose(); mCard = null; } // ... (vorhandener Code zum Trennen der Event-Handler und Dispose) ... base.Dispose(disposing); }

Ein unsauberes Beenden der I/O-Ressourcen kann zu Speicherlecks führen oder die Karte in einem inkonsistenten Zustand hinterlassen, was Probleme bei nachfolgenden Programmstarts verursachen kann.
Teil 2: Implementierung eines TCP/IP-Servers
Zielsetzung
Aufbauend auf Teil 1 wird die Anwendung um einen TCP/IP-Server erweitert. Dieser Server sendet nach jeder Inspektion das Ergebnis (z.B. den Wert des "Klasse"-Ausgangs aus dem ToolBlock) an einen verbundenen Client (z.B. eine SPS oder eine andere Anwendung).
Schritt 1: Hinzufügen der using-Anweisungen
Die Form1.cs wird um die Namespaces für die Netzwerkkommunikation erweitert:
using System.Net; using System.Net.Sockets; using System.IO; using System.Threading;
Die TCP/IP-Kommunikation ermöglicht die nahtlose Integration des Bildverarbeitungssystems in bestehende Automatisierungsnetzwerke. Die Inspektionsergebnisse können direkt an übergeordnete Steuerungen oder Datenbanksysteme übermittelt werden.
Schritt 2: Deklarieren der Server-Variablen
Zusätzlich zu den I/O-Variablen werden die Variablen für den TCP-Server deklariert.
public partial class Form1 : Form { // ... (bestehende Variablen) ... private CogPrio mPrio; // NEU: Variablen für den TCP/IP Server private TcpListener tcpServer; private TcpClient connectedClient; private Thread serverThread; // ... }
TcpListener
Hört auf eingehende TCP-Verbindungen auf einem bestimmten Port. Diese Klasse ist die Basis für unseren TCP-Server, der auf eingehende Client-Verbindungen wartet.
TcpClient
Repräsentiert einen verbundenen Client. Diese Variable speichert die Referenz auf den aktuell verbundenen Client, an den die Inspektionsergebnisse gesendet werden.
Thread
Ein separater Ausführungsthread für den Server. Dieser ermöglicht es dem Server, im Hintergrund zu laufen, ohne die Hauptanwendung zu blockieren.
Schritt 3: Starten des Servers
Der TCP-Server wird beim Laden der Anwendung gestartet. Dazu wird die Form1_Load-Methode modifiziert.
private void Form1_Load(object sender, EventArgs e) { // NEU: Den TCP-Server beim Start der Anwendung starten. StartTcpServer(); }
Durch den Aufruf von StartTcpServer() im Form1_Load-Ereignis wird der Server automatisch gestartet, sobald die Anwendung geladen ist. Dies stellt sicher, dass der Server bereit ist, Verbindungen anzunehmen, bevor die ersten Inspektionen durchgeführt werden.
Die Methode Form1_Load ist ein geeigneter Ort für die Initialisierung, da sie nach dem Konstruktor, aber vor der Anzeige des Formulars ausgeführt wird. Zu diesem Zeitpunkt sind alle Steuerelemente bereits erstellt, aber das Formular ist noch nicht sichtbar für den Benutzer.
Schritt 4: Senden der Inspektionsergebnisse
In der Subject_Ran-Methode wird nach der Auswertung ein Aufruf zum Senden der Daten über TCP/IP hinzugefügt.
void Subject_Ran(object sender, EventArgs e) { // ... (gesamter Code aus Teil 1 für Pass/Fail-Zählung und I/O-Triggerung) ... // NEU: Senden der Ergebnisse über TCP/IP nach jeder Auswertung SendResultsViaTcp(); // ... (restlicher Code zur Anzeige der Ergebnisse) ... }
Nach jeder Ausführung des ToolBlocks und der Auswertung des Ergebnisses wird die Methode SendResultsViaTcp() aufgerufen. Diese Methode formatiert die Inspektionsergebnisse und sendet sie an den verbundenen Client. Die Integration in die bestehende Subject_Ran-Methode stellt sicher, dass die Daten immer aktuell sind und unmittelbar nach der Verarbeitung übertragen werden.
Schritt 5: Implementieren der Server-Logik
Die Methoden zur Steuerung des TCP-Servers werden als neue Methoden zur Form1-Klasse hinzugefügt.
///
/// Startet den Server-Thread, der auf Client-Verbindungen lauscht. ///
private void StartTcpServer() { serverThread = new Thread(new ThreadStart(ListenForClients)); serverThread.IsBackground = true; // Sorgt dafür, dass der Thread mit der Anwendung beendet wird. serverThread.Start(); } ///
/// Diese Methode läuft in einem eigenen Thread und wartet auf eingehende Client-Verbindungen. ///
private void ListenForClients() { try { // Lausche auf allen Netzwerkkarten auf Port 2000 tcpServer = new TcpListener(IPAddress.Any, 2000); tcpServer.Start(); while (true) { // Blockiert, bis ein Client eine Verbindung herstellt. TcpClient client = tcpServer.AcceptTcpClient(); // Alten Client trennen, falls vorhanden, und neuen speichern. connectedClient?.Close(); connectedClient = client; } } catch (SocketException) { // Diese Ausnahme wird erwartet, wenn tcpServer.Stop() aufgerufen wird. } }
SendResultsViaTcp-Methode - Teil 1
///
/// Formatiert die Ergebnisse und sendet sie an den aktuell verbundenen Client. ///
private void SendResultsViaTcp() { if (connectedClient == null || !connectedClient.Connected) return;
Diese Methode beginnt mit einer Prüfung, ob überhaupt ein Client verbunden ist. Wenn kein Client verbunden ist oder die Verbindung unterbrochen wurde, kehrt die Methode sofort zurück, ohne zu versuchen, Daten zu senden.
Der Überprüfungsschritt ist wichtig, um unnötige Ausnahmen zu vermeiden, die auftreten könnten, wenn versucht wird, Daten an einen nicht vorhandenen oder nicht verbundenen Client zu senden.
Verbindungsprüfung
Überprüft, ob ein Client verbunden ist
Datenvorbereitung
Formatiert die Inspektionsergebnisse für die Übertragung
Datenübertragung
Sendet die formatierten Daten an den Client
Fehlerbehandlung
Behandelt mögliche Verbindungsprobleme
SendResultsViaTcp-Methode - Teil 2 & Ressourcenbereinigung
try { // Ergebnis aus dem ToolBlock holen (z.B. "Klasse"). string messageToSend = cogToolBlockEditV21.Subject.Outputs["Klasse"].Value.ToString(); // Nachricht senden (CR+LF wird von vielen Geräten als Zeilenende erwartet). NetworkStream stream = connectedClient.GetStream(); byte[] data = Encoding.ASCII.GetBytes(messageToSend + "\r\n"); stream.Write(data, 0, data.Length); } catch (IOException) { // Verbindungsabbruch behandeln connectedClient.Close(); connectedClient = null; } catch (Exception) { // Andere Sendefehler behandeln }
Bereinigen der Netzwerkressourcen
protected override void Dispose(bool disposing) { // NEU: TCP-Server und Client sauber beenden tcpServer?.Stop(); connectedClient?.Close(); // ... (bestehender Code zur Bereinigung der I/O-Ressourcen) ... base.Dispose(disposing); }

Bei der Bereinigung der Ressourcen ist die Reihenfolge wichtig: Zuerst wird der Server gestoppt, dann werden die Client-Verbindungen geschlossen, und schließlich werden die I/O-Ressourcen freigegeben. Der Nulloperator (?.) stellt sicher, dass keine NullReferenceException auftritt, falls die Objekte nicht initialisiert wurden.