Skip to content

Commit

Permalink
feat(plc4j/iec-60870): Continued implementing the portocol.
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisdutz committed Aug 28, 2023
1 parent 7045669 commit 20246c7
Show file tree
Hide file tree
Showing 10 changed files with 412 additions and 10 deletions.
14 changes: 5 additions & 9 deletions plc4j/drivers/iec-60870/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,28 +123,24 @@
<version>0.11.0-SNAPSHOT</version>
</dependency>

<!--dependency>
<dependency>
<groupId>org.apache.plc4x</groupId>
<artifactId>plc4j-transport-tcp</artifactId>
<version>0.11.0-SNAPSHOT</version>
</dependency-->
</dependency>

<!--dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-buffer</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.pcap4j</groupId>
<artifactId>pcap4j-core</artifactId>
</dependency-->

<dependency>
<groupId>org.apache.plc4x</groupId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.plc4x.java.iec608705104.readwrite;

import io.netty.buffer.ByteBuf;
import org.apache.plc4x.java.api.metadata.PlcDriverMetadata;
import org.apache.plc4x.java.iec608705104.readwrite.configuration.Iec608705014Configuration;
import org.apache.plc4x.java.iec608705104.readwrite.protocol.Iec608705104Protocol;
import org.apache.plc4x.java.iec608705104.readwrite.tag.Iec608705104TagHandler;
import org.apache.plc4x.java.spi.configuration.Configuration;
import org.apache.plc4x.java.spi.connection.GeneratedDriverBase;
import org.apache.plc4x.java.spi.connection.ProtocolStackConfigurer;
import org.apache.plc4x.java.spi.connection.SingleProtocolStackConfigurer;
import org.apache.plc4x.java.spi.values.PlcValueHandler;

import java.util.function.Consumer;
import java.util.function.ToIntFunction;

/**
* Implementation of the ADS protocol, based on:
* - ADS Protocol
* - TCP
* - Serial
*/
public class Iec60870514PlcDriver extends GeneratedDriverBase<APDU> {

@Override
public String getProtocolCode() {
return "iec-60870-5-104";
}

@Override
public String getProtocolName() {
return "IEC 60870-5-104";
}

@Override
protected boolean canRead() {
return true;
}

@Override
protected boolean canWrite() {
return true;
}

@Override
protected boolean canSubscribe() {
return true;
}

@Override
protected Class<? extends Configuration> getConfigurationType() {
return Iec608705014Configuration.class;
}

@Override
protected String getDefaultTransport() {
return "tcp";
}

@Override
protected Iec608705104TagHandler getTagHandler() {
return new Iec608705104TagHandler();
}

@Override
protected org.apache.plc4x.java.api.value.PlcValueHandler getValueHandler() {
return new PlcValueHandler();
}

@Override
public PlcDriverMetadata getMetadata() {
return () -> true;
}

/**
* This protocol doesn't have a disconnect procedure, so there is no need to wait for a login to finish.
* @return false
*/
@Override
protected boolean awaitDisconnectComplete() {
return false;
}

@Override
protected ProtocolStackConfigurer<APDU> getStackConfigurer() {
return SingleProtocolStackConfigurer.builder(APDU.class, APDU::staticParse)
.withPacketSizeEstimator(ByteLengthEstimator.class)
.withCorruptPacketRemover(CorruptPackageCleaner.class)
.withProtocol(Iec608705104Protocol.class)
.littleEndian()
.build();
}

/** Estimate the Length of a Packet */
public static class ByteLengthEstimator implements ToIntFunction<ByteBuf> {
@Override
public int applyAsInt(ByteBuf byteBuf) {
if (byteBuf.readableBytes() >= 2) {
return byteBuf.getUnsignedByte( 1) + 2;
}
return -1;
}
}

/** Consumes all Bytes till another Magic Byte is found */
public static class CorruptPackageCleaner implements Consumer<ByteBuf> {
@Override
public void accept(ByteBuf byteBuf) {
while (byteBuf.getUnsignedByte(0) != APDU.STARTBYTE) {
// Just consume the bytes till the next possible start position.
byteBuf.readByte();
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.plc4x.java.iec608705104.readwrite.configuration;

import org.apache.plc4x.java.iec608705104.readwrite.IEC608705104Constants;
import org.apache.plc4x.java.spi.configuration.Configuration;
import org.apache.plc4x.java.spi.configuration.annotations.ConfigurationParameter;
import org.apache.plc4x.java.spi.configuration.annotations.defaults.IntDefaultValue;
import org.apache.plc4x.java.transport.tcp.TcpTransportConfiguration;

public class Iec608705014Configuration implements Configuration, TcpTransportConfiguration {

@ConfigurationParameter("timeout-request")
@IntDefaultValue(4000)
protected int timeoutRequest;

@Override
public int getDefaultPort() {
return IEC608705104Constants.DEFAULTPORT;
}

public int getTimeoutRequest() {
return timeoutRequest;
}

public void setTimeoutRequest(int timeoutRequest) {
this.timeoutRequest = timeoutRequest;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.plc4x.java.iec608705104.readwrite.protocol;

import org.apache.plc4x.java.api.messages.PlcSubscriptionEvent;
import org.apache.plc4x.java.api.model.PlcConsumerRegistration;
import org.apache.plc4x.java.api.model.PlcSubscriptionHandle;
import org.apache.plc4x.java.iec608705104.readwrite.*;
import org.apache.plc4x.java.iec608705104.readwrite.configuration.Iec608705014Configuration;
import org.apache.plc4x.java.spi.ConversationContext;
import org.apache.plc4x.java.spi.Plc4xProtocolBase;
import org.apache.plc4x.java.spi.configuration.HasConfiguration;
import org.apache.plc4x.java.spi.messages.PlcBrowser;
import org.apache.plc4x.java.spi.messages.PlcSubscriber;
import org.apache.plc4x.java.spi.transaction.RequestTransactionManager;

import java.time.Duration;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;

public class Iec608705104Protocol extends Plc4xProtocolBase<APDU> implements HasConfiguration<Iec608705014Configuration>, PlcSubscriber, PlcBrowser {

private Iec608705014Configuration configuration;
private final RequestTransactionManager tm;

public Iec608705104Protocol() {
// We're starting with allowing only one message in-flight.
this.tm = new RequestTransactionManager(1);
}

@Override
public void setConfiguration(Iec608705014Configuration configuration) {
this.configuration = configuration;
}

@Override
public void onConnect(ConversationContext<APDU> context) {
// First we exchange a test-frame
APDUUFormatTestFrameActivation testFrameActivation = new APDUUFormatTestFrameActivation(0x43);
RequestTransactionManager.RequestTransaction testFrameTx = tm.startRequest();
testFrameTx.submit(() -> context.sendRequest(testFrameActivation)
.expectResponse(APDU.class, Duration.ofMillis(configuration.getTimeoutRequest()))
.onTimeout(e -> context.getChannel().pipeline().fireExceptionCaught(e))
.onError((p, e) -> context.getChannel().pipeline().fireExceptionCaught(e))
.check(apdu -> apdu instanceof APDUUFormatTestFrameConfirmation)
.unwrap(apdu -> (APDUUFormatTestFrameConfirmation) apdu)
.handle(testFrameResponse -> {
testFrameTx.endRequest();

// Next send the start-data-transfer packet.
APDUUFormatStartDataTransferActivation startDataTransferActivation = new APDUUFormatStartDataTransferActivation(0x07);
RequestTransactionManager.RequestTransaction startDataTransferTx = tm.startRequest();
startDataTransferTx.submit(() -> context.sendRequest(startDataTransferActivation)
.expectResponse(APDU.class, Duration.ofMillis(configuration.getTimeoutRequest()))
.onTimeout(e -> context.getChannel().pipeline().fireExceptionCaught(e))
.onError((p, e) -> context.getChannel().pipeline().fireExceptionCaught(e))
.check(apdu -> apdu instanceof APDUUFormatStartDataTransferConfirmation)
.unwrap(apdu -> (APDUUFormatStartDataTransferConfirmation) apdu)
.handle(startDataTransferResponse -> {
startDataTransferTx.endRequest();
context.fireConnected();
}));
}));
}

@Override
public void close(ConversationContext<APDU> context) {

}


@Override
protected void decode(ConversationContext<APDU> context, APDU msg) throws Exception {
if (msg instanceof APDUUFormatTestFrameActivation) {
APDUUFormatTestFrameConfirmation testFrameConfirmation = new APDUUFormatTestFrameConfirmation(0x83);
context.sendToWire(testFrameConfirmation);
}
}

@Override
public PlcConsumerRegistration register(Consumer<PlcSubscriptionEvent> consumer, Collection<PlcSubscriptionHandle> handles) {
return null;
}

@Override
public void unregister(PlcConsumerRegistration registration) {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package org.apache.plc4x.java.iec608705104.readwrite.tag;

import org.apache.plc4x.java.api.model.PlcQuery;
import org.apache.plc4x.java.api.model.PlcTag;
import org.apache.plc4x.java.spi.connection.PlcTagHandler;

public class Iec608705104TagHandler implements PlcTagHandler {

@Override
public PlcTag parseTag(String tagAddress) {
return null;
}

@Override
public PlcQuery parseQuery(String query) {
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@
# specific language governing permissions and limitations
# under the License.
#
org.apache.plc4x.java.iec608705104.readwrite.Iec60870514PlcDriver
Loading

0 comments on commit 20246c7

Please sign in to comment.