diff --git a/pom.xml b/pom.xml index 82485c4..ba251d8 100644 --- a/pom.xml +++ b/pom.xml @@ -193,6 +193,11 @@ asn1bean 1.14.0 + + org.openmuc + j60870 + 1.7.2 + diff --git a/src/main/java/com/xydl/cac/iec104/Iec104Client.java b/src/main/java/com/xydl/cac/iec104/Iec104Client.java new file mode 100644 index 0000000..ba14f0b --- /dev/null +++ b/src/main/java/com/xydl/cac/iec104/Iec104Client.java @@ -0,0 +1,53 @@ +package com.xydl.cac.iec104; + +import lombok.extern.slf4j.Slf4j; +import org.openmuc.j60870.*; +import org.openmuc.j60870.ie.IeQualifierOfInterrogation; + +import java.io.IOException; +import java.net.InetAddress; + +@Slf4j +public class Iec104Client implements ConnectionEventListener { + public String ip; + public Integer port; + Connection connection; + + public void connect(String _ip, Integer _port) throws Exception { + ip = _ip; + port = _port; + InetAddress address = InetAddress.getByName(ip); + ClientConnectionBuilder clientConnectionBuilder = new ClientConnectionBuilder(address) + .setMessageFragmentTimeout(5_000) + .setConnectionTimeout(20_000) + .setPort(port) + .setConnectionEventListener(this); + connection = clientConnectionBuilder.build(); + } + + public void disconnect() { + if (connection != null) { + connection.close(); + } + } + + public void interrogation() throws Exception { + connection.interrogation(1, CauseOfTransmission.ACTIVATION, + new IeQualifierOfInterrogation(20)); + } + + @Override + public void newASdu(Connection connection, ASdu aSdu) { + log.debug("Got new ASdu:" + aSdu.toString()); + } + + @Override + public void connectionClosed(Connection connection, IOException e) { + + } + + @Override + public void dataTransferStateChanged(Connection connection, boolean b) { + + } +} diff --git a/src/main/java/com/xydl/cac/iec104/Iec104Server.java b/src/main/java/com/xydl/cac/iec104/Iec104Server.java new file mode 100644 index 0000000..c8dce10 --- /dev/null +++ b/src/main/java/com/xydl/cac/iec104/Iec104Server.java @@ -0,0 +1,56 @@ +package com.xydl.cac.iec104; + +import lombok.extern.slf4j.Slf4j; +import org.openmuc.j60870.*; + +import java.io.IOException; + +@Slf4j +public class Iec104Server implements ServerEventListener { + Server _server; + int idCounter = 1; + public boolean started = false; + public Integer port = null; + + public Iec104Server() { + } + + public void start(int port) throws Exception { + if (!started) { + _server = Server.builder() + .setPort(port) + .setIoaFieldLength(3) + .setCommonAddressFieldLength(2) + .setCotFieldLength(2) + .build(); + _server.start(this); + started = true; + this.port = port; + log.info("已启动IEC104服务端在" + port + "端口"); + } + } + + public void stop() { + if (_server != null) { + _server.stop(); + log.info("已停止IEC104服务端于" + port + "端口"); + } + started = false; + } + + + @Override + public ConnectionEventListener connectionIndication(Connection connection) { + return new ServerConnectionEventListener(connection, idCounter++); + } + + @Override + public void serverStoppedListeningIndication(IOException e) { + + } + + @Override + public void connectionAttemptFailed(IOException e) { + + } +} diff --git a/src/main/java/com/xydl/cac/iec104/ServerConnectionEventListener.java b/src/main/java/com/xydl/cac/iec104/ServerConnectionEventListener.java new file mode 100644 index 0000000..7f80b5d --- /dev/null +++ b/src/main/java/com/xydl/cac/iec104/ServerConnectionEventListener.java @@ -0,0 +1,94 @@ +package com.xydl.cac.iec104; + +import lombok.extern.slf4j.Slf4j; +import org.openmuc.j60870.*; +import org.openmuc.j60870.ie.*; + +import java.io.EOFException; +import java.io.IOException; + +@Slf4j +public class ServerConnectionEventListener implements ConnectionEventListener { + Connection connection; + int connectionId; + boolean selected = false; + + public ServerConnectionEventListener(Connection connection, int connectionId) { + this.connection = connection; + this.connectionId = connectionId; + } + + @Override + public void newASdu(Connection connection, ASdu aSdu) { + log.debug("Got new ASdu:" + aSdu.toString()); + InformationObject informationObject = null; + try { + switch (aSdu.getTypeIdentification()) { + // interrogation command + case C_IC_NA_1: + log.debug("Got interrogation command (100). Will send scaled measured values."); + connection.sendConfirmation(aSdu); + // example GI response values + connection.send(new ASdu(ASduType.M_ME_NB_1, true, CauseOfTransmission.INTERROGATED_BY_STATION, + false, false, 0, aSdu.getCommonAddress(), + new InformationObject(1, new InformationElement[][]{ + {new IeScaledValue(-32768), new IeQuality(false, false, false, false, false)}, + {new IeScaledValue(10), new IeQuality(false, false, false, false, false)}, + {new IeScaledValue(-5), + new IeQuality(false, false, false, false, false)}}))); + connection.sendActivationTermination(aSdu); + break; + case C_SC_NA_1: + informationObject = aSdu.getInformationObjects()[0]; + IeSingleCommand singleCommand = (IeSingleCommand) informationObject + .getInformationElements()[0][0]; + + if (informationObject.getInformationObjectAddress() != 5000) { + break; + } + if (singleCommand.isSelect()) { + log.debug("Got single command (45) with select true. Select command."); + selected = true; + connection.sendConfirmation(aSdu); + } else if (!singleCommand.isSelect() && selected) { + log.debug("Got single command (45) with select false. Execute selected command."); + selected = false; + connection.sendConfirmation(aSdu); + } else { + log.debug("Got single command (45) with select false. But no command is selected, no execution."); + } + break; + case C_CS_NA_1: + IeTime56 ieTime56 = new IeTime56(System.currentTimeMillis()); + log.debug("Got Clock synchronization command (103). Send current time: " + ieTime56.toString()); + connection.synchronizeClocks(aSdu.getCommonAddress(), ieTime56); + break; + case C_SE_NB_1: + log.debug("Got Set point command, scaled value (49)"); + break; + default: + log.debug("Got unknown request: " + aSdu.toString(), + ". Send negative confirm with CoT UNKNOWN_TYPE_ID(44)"); + connection.sendConfirmation(aSdu, aSdu.getCommonAddress(), true, + CauseOfTransmission.UNKNOWN_TYPE_ID); + } + + } catch (EOFException e) { + log.debug("Will quit listening for commands on connection (" + connectionId, + ") because socket was closed."); + } catch (IOException e) { + log.debug("Will quit listening for commands on connection (" + connectionId, ") because of error: ", + e.getMessage()); + } + } + + @Override + public void connectionClosed(Connection connection, IOException e) { + + } + + @Override + public void dataTransferStateChanged(Connection connection, boolean b) { + + } +}