strolch-plc/plc-core/src/main/java/li/strolch/plc/core/hw/connections/DataLogicScannerConnection....

253 lines
6.8 KiB
Java

package li.strolch.plc.core.hw.connections;
import static li.strolch.plc.model.PlcConstants.PARAM_SIMULATED;
import static li.strolch.utils.helper.ExceptionHelper.getExceptionMessageWithCauses;
import java.io.IOException;
import java.io.InputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import li.strolch.plc.core.hw.Plc;
import li.strolch.utils.helper.AsciiHelper;
public class DataLogicScannerConnection extends SimplePlcConnection {
public static final String NO_CONNECTION = "*NoConnection*";
public static final String NO_READ = "*NoRead*";
private static final String ADDR_TRIGGER = ".trigger";
private static final String ADDR_BARCODE = ".barcode";
private InetAddress address;
private int port;
private int readTimeout;
private Socket socket;
private boolean triggered;
private boolean read;
private Future<?> readTask;
private HashSet<String> addresses;
private final String addressTrigger;
private final String addressBarcode;
public DataLogicScannerConnection(Plc plc, String id) {
super(plc, id);
this.addressTrigger = id + ADDR_TRIGGER;
this.addressBarcode = id + ADDR_BARCODE;
}
@Override
public boolean isAutoConnect() {
return false;
}
@Override
public void initialize(Map<String, Object> parameters) throws Exception {
this.simulated = parameters.containsKey(PARAM_SIMULATED) && (boolean) parameters.get(PARAM_SIMULATED);
String address = (String) parameters.get("address");
String[] parts = address.split(":");
this.address = Inet4Address.getByName(parts[0]);
this.port = Integer.parseInt(parts[1]);
this.readTimeout = (int) parameters.get("readTimeout");
this.addresses = new HashSet<>();
this.addresses.add(this.addressTrigger);
this.addresses.add(this.addressBarcode);
logger.info("Configured DataLogic Scanner connection to " + this.address + ":" + this.port);
}
@Override
public boolean connect() {
if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
return super.connect();
}
if (isConnected())
return true;
try {
this.socket = new Socket(this.address, this.port);
this.socket.setSoTimeout((int) TimeUnit.SECONDS.toMillis(this.readTimeout));
logger.info("Connected DataLogic Scanner connection to " + this.address + ":" + this.port);
this.read = true;
this.readTask = this.plc.getExecutorPool().getSingleThreadExecutor(this.id).submit(this::read);
return super.connect();
} catch (IOException e) {
handleBrokenConnection(
"Failed to connect to " + this.address + ":" + this.port + ": " + getExceptionMessageWithCauses(e),
e);
return false;
}
}
@Override
public void disconnect() {
if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
super.disconnect();
return;
}
internalDisconnect();
super.disconnect();
}
private void internalDisconnect() {
this.read = false;
if (this.readTask != null) {
this.readTask.cancel(true);
}
if (this.socket != null) {
logger.warn("Closing socket to " + this.address + ":" + this.port);
try {
this.socket.shutdownInput();
this.socket.shutdownOutput();
this.socket.close();
} catch (IOException e) {
logger.error("Failed to close socket", e);
}
this.socket = null;
}
}
@Override
public Set<String> getAddresses() {
return this.addresses;
}
private void sendStartTrigger() throws IOException {
this.triggered = true;
this.socket.getOutputStream().write('T');
this.socket.getOutputStream().flush();
logger.info("Triggered DataLogicScanner");
}
private void sendStopTrigger() throws IOException {
this.triggered = false;
this.socket.getOutputStream().write('S');
logger.info("Stopped DataLogicScanner");
}
@Override
public void send(String address, Object value) {
if (!this.addressTrigger.equals(address))
throw new IllegalStateException("Illegal Address " + address);
if (this.simulated) {
logger.warn(this.id + ": Running SIMULATED, NOT CONNECTING!");
return;
}
boolean trigger = (boolean) value;
try {
if (trigger) {
if (!connect())
throw new IllegalStateException("Could not connect to " + this.address + ":" + this.port);
sendStartTrigger();
} else {
if (isConnected()) {
sendStopTrigger();
disconnect();
}
}
} catch (IOException e) {
handleBrokenConnection(
"Failed to handle address " + address + " for " + this.address + ":" + this.port + ": "
+ getExceptionMessageWithCauses(e), e);
throw new IllegalStateException(
"Failed to handle address " + address + " for " + this.address + ":" + this.port, e);
}
}
private void read() {
logger.info("Reading from DataLogic Scanner at " + this.address + ":" + this.port + "...");
while (this.read) {
try {
InputStream inputStream = this.socket.getInputStream();
int read;
while ((read = inputStream.read()) != AsciiHelper.STX) {
if (read == -1) {
if (this.read) {
throw new IllegalStateException("No data read from socket!");
} else {
logger.warn("Disconnect requested while waiting for data.");
break;
}
}
}
if (this.read) {
StringBuilder sb = new StringBuilder();
while ((read = inputStream.read()) != AsciiHelper.ETX) {
if (read == -1) {
if (this.read) {
throw new IllegalStateException("No data read from socket!");
} else {
logger.warn("Disconnected requested while waiting for data.");
break;
}
}
sb.append((char) read);
}
String barcode = sb.toString();
logger.info("Received barcode " + barcode);
notify(this.addressBarcode, barcode);
}
} catch (Exception e) {
if (e instanceof SocketTimeoutException) {
if (this.triggered) {
notify(this.addressBarcode, NO_READ);
try {
sendStopTrigger();
} catch (IOException ex) {
logger.error("Failed to send stop during timeout exception: " + ex.getMessage());
}
internalDisconnect();
handleBrokenConnection(
"Timeout while reading from scanner at " + this.address + ":" + this.port + ": "
+ getExceptionMessageWithCauses(e), e);
} else {
logger.warn("Timeout while reading from scanner at " + this.address + ":" + this.port
+ ". Disconnected.");
notify(this.addressBarcode, NO_CONNECTION);
disconnect();
}
} else {
notify(this.addressBarcode, NO_CONNECTION);
internalDisconnect();
handleBrokenConnection("Failed to connect to " + this.address + ":" + this.port + ": "
+ getExceptionMessageWithCauses(e), e);
}
}
}
logger.info("Stopped reading from " + this.address + ":" + this.port);
}
}