Blog
Følgende programeksempel giver dig mulighed for at overvåge og styre en Web-IO 2x Digital og indeholder følgende funktioner til dette:
Skift af udgange
Manuel eller automatisk aflæsning af indgange, udgange og tællere
Aflæsning og sletning af enkelte eller alle tællere
Kom i gang - bestil en prøveversion i 30 dage.
Prøv vores produkter fra Wiesemann & Theis gratis i 30 dage ved at skrive det i ordrebekræftelsen: Ønsker at teste produktet.
Hvis du ikke ønsker at gøre brug af din returret inden for 30 dage, skal du blot betale den medfølgende faktura. Gratis forsendelse i Danmark.
Forberedelser
Du har allerede angivet din Web-IO Digital
- med magt,
- konfigurerede ind- og udgange,
- tilsluttet den til dit netværk,
- tildelt den en IP-adresse – hvilket med WuTility ikke er noget problem.
Kombination af de forskellige betjenings- og displayelementer
I den følgende illustration er de forskellige betjenings- og displayelementer betegnet med det navn, der bruges i programkoden. Opstillingen er tænkt som en reference, der gør det lettere at forstå det følgende eksempel.

Når du navngiver de enkelte objekter, er det en god idé at bruge logiske navne. I dette eksempel beskriver den første del af navnet objekttypen, og den anden del funktionen.
1. Import af de nødvendige kilder
I starten af et program skal alle nødvendige kilder importeres.
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.Timer;
import javax.swing.border.BevelBorder;2. Udvidelse af klassen
Klassen “Client” arver alle egenskaber fra klassen “Jframe” til visning af programvinduet og forbinder grænsefladerne “ActionListener” (registrering af knaphændelser), “KeyListener” (registrering af ændringer i tekstfelter) og “Runnable” (aktivering af tråde) ved at importere deres metoder.
public class Client extends JFrame implements ActionListener, KeyListener, Runnable {
...
}3. Installation af de grafiske elementer
For de grafiske elementer (knapper, CheckBoxes, labels, paneler, TextFields) oprettes globale instanser, så de er tilgængelige i alle metoder i programmet.
private JButton jbConnect = new JButton("Connect");
private JButton jbDisconnect = new JButton("Disconnect");
private JButton jbOutputsRead = new JButton("Read all");
private JButton jbInputsRead = new JButton("Read all");
private JButton jbCounterRead0 = new JButton("Read");
private JButton jbCounterRead1 = new JButton("Read");
private JButton jbCounterReadAll = new JButton("Read");
private JButton jbCounterClear0 = new JButton("Clear");
private JButton jbCounterClear1 = new JButton("Clear");
private JButton jbCounterClearAll = new JButton("Clear");
private JCheckBox jcbCounterPolling = new JCheckBox("Polling");
private JCheckBox jcbOutput0 = new JCheckBox("Output 0");
private JCheckBox jcbOutput1 = new JCheckBox("Output 1");
private JCheckBox jcbOutputPolling = new JCheckBox("Polling");
private JCheckBox jcbInput0 = new JCheckBox("Input 0");
private JCheckBox jcbInput1 = new JCheckBox("Input 1");
private JCheckBox jcbInputPolling = new JCheckBox("Polling");
private JLabel jlStatusBar = new JLabel("No connection");
private JPanel jpIO = new JPanel();
private JTextField jtfInterval = new JTextField("250");
private JTextField jtfCounter0 = new JTextField("0");
private JTextField jtfCounter1 = new JTextField("0");
private JTextField jtfIP = new JTextField();
private JTextField jtfPort = new JTextField("80");
private JTextField jtfPassword = new JTextField();4. Elementer til dataoverførsel og tidsadfærd
For at sende data over et socket-interface bruges der i dette eksempel instanser fra klasserne “InputStream”, “OutputStream” og “Socket”.
private InputStream isInStream;
private OutputStream osOutStream;
private Socket soClientSocket;Timeren, som styrer polling, er afledt af klassen “Timer” (javax.swing.Timer).
private Timer tiPoll;5. “Main”-metoden
Metoden “main” vælges automatisk, når programmet startes. Den påkalder klassens konstruktør.
public static void main(String[] args) {
new Client();
}6. Konstruktøren
Konstruktøren bruges til at definere størrelse, position og adfærd for displayelementer i programvinduet. Konstruktøren påkaldes uden at overføre parametre.
public Client() {
...
}Derefter oprettes JPanel “jplO”. Til dette formål dimensioneres eller deaktiveres displayelementerne for at generere outputtilstanden og tildeles en lytter, så der kan reageres på brugerinput. Derefter tilføjes elementerne til Jpanel.
jcbOutput0.setBounds(15, 25, 80, 20);
jcbOutput0.setEnabled(false);
jcbOutput0.addActionListener(this);
jcbOutput1.setBounds(15, 50, 80, 20);
jcbOutput1.setEnabled(false);
jcbOutput1.addActionListener(this);
jbOutputsRead.setBounds(15, 75, 80, 25);
jbOutputsRead.setEnabled(false);
jbOutputsRead.addActionListener(this);
jcbOutputPolling.setBounds(15, 105, 80, 20);
jcbOutputPolling.setEnabled(false);
jcbInput0.setBounds(105, 25, 80, 20);
jcbInput0.setEnabled(false);
jcbInput0.addActionListener(this);
jcbInput1.setBounds(105, 50, 80, 20);
jcbInput1.setEnabled(false);
jcbInput1.addActionListener(this);
jbInputsRead.setBounds(105, 75, 80, 25);
jbInputsRead.setEnabled(false);
jbInputsRead.addActionListener(this);
jcbInputPolling.setBounds(105, 105, 80, 20);
jcbInputPolling.setEnabled(false);
JLabel jlCounter0 = new JLabel("Counter 0");
jlCounter0.setBounds(190, 25, 55, 20);
jlCounter0.setEnabled(false);
JLabel jlCounter1 = new JLabel("Counter 1");
jlCounter1.setBounds(190, 50, 55, 20);
jlCounter1.setEnabled(false);
JLabel jlCounterAll = new JLabel("Counter All");
jlCounterAll.setBounds(190, 77, 70, 20);
jlCounterAll.setEnabled(false);
JLabel jlInterval = new JLabel("Interval");
jlInterval.setBounds(190, 105, 50, 20);
jlInterval.setEnabled(false);
tfCounter0.setBounds(250, 23, 40, 25);
jtfCounter0.setEnabled(false);
jtfCounter0.setHorizontalAlignment(JTextField.CENTER);
jtfCounter0.setEditable(false);
jtfCounter1.setBounds(250, 48, 40, 25);
jtfCounter1.setEnabled(false);
jtfCounter1.setHorizontalAlignment(JTextField.CENTER);
jtfCounter1.setEditable(false);
jtfInterval.setBounds(250, 102, 40, 25);
jtfInterval.setEnabled(false);
jtfInterval.setHorizontalAlignment(JTextField.CENTER);
jtfInterval.addKeyListener(this);
jbCounterRead0.setBounds(295, 23, 65, 25);
jbCounterRead0.setEnabled(false);
jbCounterRead0.addActionListener(this);
jbCounterRead1.setBounds(295, 48, 65, 25);
jbCounterRead1.setEnabled(false);
jbCounterRead1.addActionListener(this);
jbCounterReadAll.setBounds(295, 75, 65, 25);
jbCounterReadAll.setEnabled(false);
jbCounterReadAll.addActionListener(this);
jcbCounterPolling.setBounds(295, 105, 80, 20);
jcbCounterPolling.setEnabled(false);
jbCounterClear0.setBounds(360, 23, 65, 25);
jbCounterClear0.setEnabled(false);
jbCounterClear0.addActionListener(this);
jbCounterClear1.setBounds(360, 48, 65, 25);
jbCounterClear1.setEnabled(false);
jbCounterClear1.addActionListener(this);
jbCounterClearAll.setBounds(360, 75, 65, 25);
jbCounterClearAll.setEnabled(false);
jbCounterClearAll.addActionListener(this);
jpIO.setLayout(null);
jpIO.setBounds(5, 5, 440, 135);
jpIO.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), "Input/ Output Control"));
jpIO.add(jcbOutput0);
jpIO.add(jcbOutput1);
jpIO.add(jbOutputsRead);
jpIO.add(jcbOutputPolling);
jpIO.add(jcbInput0);
jpIO.add(jcbInput1);
jpIO.add(jbInputsRead);
jpIO.add(jcbInputPolling);
jpIO.add(jlCounter0);
jpIO.add(jlCounter1);
jpIO.add(jlCounterAll);
jpIO.add(jlInterval);
jpIO.add(jtfCounter0);
jpIO.add(jtfCounter1);
jpIO.add(jtfInterval);
jpIO.add(jbCounterRead0);
jpIO.add(jbCounterRead1);
jpIO.add(jbCounterReadAll);
jpIO.add(jcbCounterPolling);
jpIO.add(jbCounterClear0);
jpIO.add(jbCounterClear1);
jpIO.add(jbCounterClearAll);Det følgende programfragment opretter de betjenings- og visningselementer, der er samlet på overfladen under overskriften “Connection Control”. Det JPanel, som disse elementer tilføjes til, instantieres lokalt, da der ikke længere er adgang uden for konstruktøren.
JLabel jlIP = new JLabel("IPAddress");
jlIP.setBounds(20, 40, 120, 20);
jtfIP.setBounds(20, 60, 120, 26);
jtfIP.setHorizontalAlignment(JTextField.CENTER);
JLabel jlPort = new JLabel("Port");
jlPort.setBounds(145, 40, 70, 20);
jtfPort.setBounds(145, 60, 70, 26);
jtfPort.setHorizontalAlignment(JTextField.CENTER);
JLabel jlPassword = new JLabel("Password");
jlPassword.setBounds(220, 40, 95, 20);
jtfPassword.setBounds(220, 60, 95, 26);
jtfPassword.setHorizontalAlignment(JTextField.CENTER);
jbConnect.setBounds(330, 25, 100, 25);
jbConnect.addActionListener(this);
jbDisconnect.setBounds(330, 60, 100, 25);
jbDisconnect.setEnabled(false);
jbDisconnect.addActionListener(this);
JPanel jpConnect = new JPanel();
jpConnect.setLayout(null);
jpConnect.setBounds(5, 140, 440, 95);
jpConnect.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), "Connection Control"));
jpConnect.add(jlIP);
jpConnect.add(jlPort);
jpConnect.add(jlPassword);
jpConnect.add(jtfIP);
jpConnect.add(jtfPort);
jpConnect.add(jtfPassword);
jpConnect.add(jbConnect);
jpConnect.add(jbDisconnect);Statuslinjen giver information om status for socket-forbindelsen til en Web-IO, mens programmet kører. Standardværdien “Ingen forbindelse” tildeles den allerede under instantiering. I konstruktøren defineres layoutet med hensyn til størrelse, placering og kanttype.
jlStatusBar.setLayout(null);
jlStatusBar.setBounds(1, 240, 450, 20);
jlStatusBar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));Til sidst indstilles vinduets egenskaber, og de tidligere oprettede elementer tilføjes. I det sidste trin gøres programvinduet synligt. Dette afslutter initialiseringsfasen, og programmet er nu klar til at køre.
setTitle("Web-IO ASCII Client");
setLocation(400, 300);
setLayout(null);
setSize(460, 290);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(jpIO);
add(jpConnect);
add(jlStatusBar);
setVisible(true);7. Behandl knap- og timerhændelser
Behandling af hændelser (generelt): Hvis der er registreret en ActionListener for visnings- og betjeningselementer (f.eks. knapper og tekstfelter), aktiveres metoden “actionPerformed”, når der udløses hændelser. Den overførte parameter indeholder bl.a. oplysninger om, hvilken komponent der udløste hændelsen.
public void actionPerformed(ActionEvent arg0) {
...
}Åbning af en forbindelse: Ved at klikke på knappen “Opret forbindelse” åbnes en forbindelse til den angivne Web-IO. Displayet og betjeningselementerne aktiveres eller deaktiveres i overensstemmelse med tilstanden, og statuslinjen viser, hvor langt forbindelsen er nået. Hvis socket’en blev åbnet med succes, og strømmene til data-input og data-output blev genereret med succes, startes en tråd, som behandler de ankomne data. En timer, der anmoder om data i henhold til den fastlagte polling-adfærd, startes. Hvis der opstår en fejl, mens forbindelsen åbnes, bruges en undtagelse til at afslutte.
if (arg0.getSource() == jbConnect)
{
jbConnect.setEnabled(false);
jlStatusBar.setText("Trying to connect to " + jtfIP.getText());
try
{
soClientSocket = new Socket(jtfIP.getText(),Integer.parseInt(jtfPort.getText()));
isInStream = soClientSocket.getInputStream();
osOutStream = soClientSocket.getOutputStream();
new Thread(this).start();
tiPoll = new Timer(Integer.parseInt(jtfInterval.getText()),this);
tiPoll.start();
for (int i = 0; i < jpIO.getComponentCount();i++)
{
((JComponent) jpIO.getComponent(i)).setEnabled(true);
}
jbDisconnect.setEnabled(true);
jlStatusBar.setText("Connected to " + jtfIP.getText());
return;
}
catch (NumberFormatException e)
{
e.printStackTrace();
}
catch (UnknownHostException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
jbConnect.setEnabled(true);
jlStatusBar.setText("Error - No Connection");
}Forsætlig afbrydelse af forbindelsen: Ved at klikke på knappen “Afbryd forbindelsen” aktiveres metoden “Afbryd forbindelsen”. Den indeholder instruktioner, der lukker forbindelsen på en kontrolleret måde og forbereder programmet til at acceptere en ny forbindelse. Da afbrydelse også kan introduceres ved hjælp af en undtagelse, finder afbrydelsen sted i en separat metode og ikke i “actionPerformed”-metoden.
else if (arg0.getSource() == jbDisconnect) {
disconnect();
}Skift udgange: Det respektive output kan skiftes ved at manipulere afkrydsningsfelterne “jcbOutput0” og “jcbOutput1”. Hvis et af afkrydsningsfelterne udløser en hændelse, sendes en streng til Web-IO’en, som, afhængigt af den indstillede status, implementerer valget.
else if (arg0.getSource() == jcbOutput0)
{
if (jcbOutput0.isSelected())
{
write("GET /outputaccess0?PW=" + jtfPassword.getText() + "&State=ON&");
}
else
{
write("GET /outputaccess0?PW=" + jtfPassword.getText() + "&State=OFF&");
}
}
else if (arg0.getSource() == jcbOutput1)
{
if (jcbOutput1.isSelected())
{
write("GET /outputaccess1?PW=" + jtfPassword.getText() + "&State=ON&");
}
else
{
write("GET /outputaccess1?PW=" + jtfPassword.getText() + "&State=OFF&");
}
}Aflæs ind- og udgange, aflæs og ryd tællere: Ind- og udgange kan aflæses fuldstændigt ved hjælp af en knap. Til dette formål sendes en tilsvarende kommandostreng til Web-IO. Det fungerer på samme måde med tællerne. Hver knap har en tilknyttet kommandostreng, som udløser det tilsvarende resultat.
else if (arg0.getSource() == jbOutputsRead) {
write("GET /output?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbInputsRead) {
write("GET /input?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbCounterRead0) {
write("GET /counter0?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbCounterRead1) {
write("GET /counter1?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbCounterReadAll) {
write("GET /counter?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbCounterClear0) {
write("GET /counterclear0?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbCounterClear1) {
write("GET /counterclear1?PW=" + jtfPassword.getText() + "&");
}
else if (arg0.getSource() == jbCounterClearAll) {
write("GET /counterclear?PW=" + jtfPassword.getText() + "&");
}Polling: Timeren, som blev startet, da forbindelsen blev åbnet, udløser også en hændelse (cyklisk). Afhængigt af status for de tre afkrydsningsfelter for polling polles udgangene, indgangene og tællerne ved hver cyklus.
else if (arg0.getSource() == tiPoll) {
if (jcbOutputPolling.isSelected()) {
write("GET /output?PW=" + jtfPassword.getText() + "&");
}
if (jcbInputPolling.isSelected()) {
write("GET /input?PW=" + jtfPassword.getText() + "&");
}
if (jcbCounterPolling.isSelected()) {
write("GET /counter?PW=" + jtfPassword.getText() + "&");
}
}8. Behandl ændringer i TextFields
Ændring af polling-intervallet: Ændringer i polling-intervallet er aktive uden at blive bekræftet. En ændring registreres ved hjælp af en KeyListener, som føjes til tekstfeltet “tfInterval”. Dette kræver implementering af interfacemetoderne (“keyPressed”, “KeyReleased” og “keyTyped”). Når indholdet af TextField ændres, konverteres den opdaterede værdi til Int-format og sendes til timeren. Hvis indholdet af TextField ikke kan konverteres til et heltal, bruges undtagelsen til at afslutte. Polling-hastigheden ændres ikke i dette tilfælde.
public void keyPressed(KeyEvent arg0) {
}
public void keyReleased(KeyEvent arg0) {
try {
tiPoll.setDelay(Integer.parseInt(jtfInterval.getText()));
} catch (NumberFormatException e) {
}
}
public void keyTyped(KeyEvent arg0) {
}9. Modtagelse og behandling af data
Metoden “run” startes, når en socket-forbindelse åbnes, og kører som en tråd næsten parallelt med det aktuelle program. I denne metode tjekkes socket’en løbende i en while-løkke for indkommende data. Når forbindelsen afbrydes, aflæses værdien -1, som er løkkens afbrydelseskriterium. Hvis der modtages en værdi >0, konverteres den i henhold til ASCII-tabellen til et tegn og føjes til den modtagne streng. Hvis der modtages et 0, er strengen modtaget i sin helhed, og behandlingen begynder. Starten på en streng (“output;”, “input;”, “counter” og “counter”) angiver tildelingen af data. Når informationen er identificeret, analyseres den og vises.
public void run() {
int iInput, iState;
String sIn = "";
StringTokenizer stToken;
try {
while
((iInput = isInStream.read()) != -1) {
if
(iInput > 0) {
sIn += (char) iInput;
}
else {
if (sIn.startsWith("input")) {
iState = Integer.parseInt(sIn.replaceFirst("input;", ""));
jcbInput0.setSelected(((iState & 1) > 0) ? true : false);
jcbInput1.setSelected(((iState & 2) > 0) ? true : false);
}
else if (sIn.startsWith("output")) {
iState = Integer.parseInt(sIn.replaceFirst("output;",""));
jcbOutput0.setSelected(((iState & 1) > 0) ? true : false);
jcbOutput1.setSelected(((iState & 2) > 0) ? true : false);
}
else if (sIn.startsWith("counter")) {
if (sIn.startsWith("counter;")) {
sIn = sIn.replaceFirst("counter;", "");
stToken = new StringTokenizer(sIn, ";");
jtfCounter0.setText(stToken.nextToken());
jtfCounter1.setText(stToken.nextToken());
}
else {
stToken = new StringTokenizer(sIn, ";");
if (stToken.nextToken().equals("counter0")) {
jtfCounter0.setText(stToken.nextToken());
}
else {
jtfCounter1.setText(stToken.nextToken());
}
sIn = sIn.replaceFirst("counter", "");
}
}
sIn = "";
}
}
disconnect();
} catch
(IOException e) {
}
}10. Send kommandostrenge
Metoden “write” bruges til at skrive de overførte kommandostrenge til stikket. skrivningen sker i to trin. “Write”-instruktionen overfører strengen til socket’en som en bytekæde. “Flush”-instruktionen sender bytes til peer’en. Hvis der opstår en fejl under denne procedure, bruges undtagelsen til at afslutte.
private void write(String sOutput) {
try {
osOutStream.write(sOutput.getBytes());
osOutStream.flush();
}
catch (IOException e) {
}
}11. Frakobling af
Instruktionerne til korrekt åbning af en forbindelse findes i en separat metode, som påkaldes ved at klikke på knappen “Afbryd forbindelse” og ved at påkalde en afbrydelse. Først deaktiveres knappen “Disconnect”, meddelelsen om afbrydelsen vises i statuslinjen, og polling-timeren stoppes. Derefter lukkes stikkontakten. Hvis denne procedure lykkes, bliver alle input- og displayelementer sat til deres oprindelige tilstand. Hvis lukningen ikke lykkes, indstilles grænsefladen igen til en eksisterende forbindelse.
private void disconnect() {
jbDisconnect.setEnabled(false);
jlStatusBar.setText("Trying to disconnect from " + jtfIP.getText());
try {
tiPoll.stop();
soClientSocket.close();
for (int i = 0; i < jpIO.getComponentCount(); i++) {
((JComponent)jpIO.getComponent(i)).setEnabled(false);
}
jbConnect.setEnabled(true);
jlStatusBar.setText("No connection");
return;
} catch (IOException e) {
e.printStackTrace();
}
jbDisconnect.setEnabled(true);
jlStatusBar.setText("Error - Connected to " + jtfIP.getText());
}Wiesemann & Theis GmbH blev grundlagt i 1979 af Reinhard Wiesemann og Rüdiger Theis. Med 50 ansatte producerer virksomheden mikrocomputer- og netværksteknologi i Wuppertal. I 2001 introducerede Wiesemann & Theis den første industrielle temperatursensor med et netværksinterface, Web-Thermometer, og har næsten 20 års erfaring inden for områderne Industri 4.0 og Internet of Things.
Active Communication har været distributør for W&T siden 1992 i Danmark og siden 2002 også i Sverige, Norge og Island. W&T’s produkter er ekstremt brugervenlige og pålidelige til en konkurrencedygtig pris.