Skip to content

Commit

Permalink
Add instance number and element number support in symbol path. This c…
Browse files Browse the repository at this point in the history
…reate smaller path and allows more read to be packet in a single MultiRead requests. Numeric path elements are encoded as byte or short with proper segment type.
  • Loading branch information
spelletier committed Apr 10, 2018
1 parent 574a62d commit af8338a
Showing 1 changed file with 115 additions and 45 deletions.
160 changes: 115 additions & 45 deletions src/etherip/types/CNSymbolPath.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,20 @@

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Control Net Path for element path
* <p>
* Example (with suitable static import):
* Control Net Path for element path. Support symbolic and numeric address elements in any part.
* <p>
* Examples (with suitable static import):
* </p><p>
* <code>CNPath path = Symbol.name("my_tag")</code>
*
* <code>CNPath path = Symbol.name("my_tag[1].Name")</code>
* <code>CNPath path = Symbol.name("123.1.Name")</code>
* </p>
* @author Kay Kasemir
*/
@SuppressWarnings("nls")
Expand All @@ -30,15 +33,24 @@ public class CNSymbolPath extends CNPath
* <p>
* Contains a string path and an optional array index
*/
class PathAndIndex
public static class PathElement
{
private final String path;
private static final int MAX_BYTE_VALUE = 255;
private final String path;
private final Integer index;
private boolean firstElement;

public PathAndIndex(final String path, final Integer index)
public PathElement(final boolean firstElement, final String path, final Integer index)
{
this.path = path;
this.index = index;
this.firstElement = firstElement;
if (path.matches("^[0-9]+$")) {
this.path = null;
this.index = Integer.parseInt(path);
}
else {
this.path = path;
this.index = index;
}
}

public String getPath()
Expand All @@ -50,6 +62,85 @@ public Integer getIndex()
{
return this.index;
}

public int getEncodedSize() {
int size = 0;
if (firstElement && path == null) {
size += 2; // Add space for Symbol Class ID
}

if (path != null) {
size += path.length() + 2;
if (needPad()) {
size += 1;
}
}
if (index != null) {
size += 2;
if (index > MAX_BYTE_VALUE) {
size += 2;
}
}
return size;
}

public void encode(final ByteBuffer buf) {
if (firstElement && path == null) {
encodeInstanceId(buf);
}
else {
encodeElementName(buf);
encodeElementId(buf);
}
}

private void encodeInstanceId(final ByteBuffer buf) {
buf.put(new byte[] {0x20, 0x6B}); // Logical Segment for Symbol Class ID
if (index > MAX_BYTE_VALUE) {
buf.put((byte) 0x25);
buf.put((byte) 0);
buf.putShort(index.shortValue());
}
else {
buf.put((byte) 0x24);
buf.put(index.byteValue());
}
}

private void encodeElementName(final ByteBuffer buf) {
if (path != null) {
// spec 4 p.21: "ANSI extended symbol segment"
buf.put((byte) 0x91);
buf.put((byte) path.length());
buf.put(path.getBytes());
if (this.needPad())
{
buf.put((byte) 0);
}
}
}

private void encodeElementId(final ByteBuffer buf) {
if (index != null)
{
if (index > MAX_BYTE_VALUE) {
buf.put((byte) 0x29);
buf.put((byte) 0);
buf.putShort(index.shortValue());
}
else {
buf.put((byte) 0x28);
buf.put(index.byteValue());
}
}
}

/** @return Is path of odd length, requiring a pad byte? */
private boolean needPad()
{
return (path.length() % 2) != 0;
}


@Override
public String toString()
Expand All @@ -64,16 +155,17 @@ public String toString()

private final Pattern PATTERN_BRACKETS = Pattern.compile("\\[(\\d+)\\]");

private final List<PathAndIndex> paths = new ArrayList<>();
private final List<PathElement> elements = new ArrayList<>();

/**
/**
* Initialize
*
* @param symbol
* Name of symbol
*/
protected CNSymbolPath(final String symbol)
{
boolean firstElement = true;
for (final String s : symbol.split("\\."))
{
final Matcher m = this.PATTERN_BRACKETS.matcher(s);
Expand All @@ -86,23 +178,23 @@ protected CNSymbolPath(final String symbol)
index = Integer.parseInt(match);
path = path.replace("[" + match + "]", "");
}
this.paths.add(new PathAndIndex(path, index));
this.elements.add(new PathElement(firstElement, path, index));
firstElement = false;
}
}

public List<PathElement> getElements() {
return Collections.unmodifiableList(elements);
}

/** {@inheritDoc} */
@Override
public int getRequestSize()
{ // End of string is padded if length is odd
int count = 0;
for (final PathAndIndex s : this.paths)
for (final PathElement s : this.elements)
{
count += 2 + s.getPath().length()
+ (this.needPad(s.getPath()) ? 1 : 0);
if (s.getIndex() != null)
{
count += 2;
}
count += s.getEncodedSize();
}
return count;
}
Expand All @@ -111,48 +203,26 @@ public int getRequestSize()
@Override
public void encode(final ByteBuffer buf, final StringBuilder log)
{
// spec 4 p.21: "ANSI extended symbol segment"
buf.put((byte) (this.getRequestSize() / 2));
for (final PathAndIndex pi : this.paths)
for (final PathElement pi : this.elements)
{
final String s = pi.getPath();
buf.put((byte) 0x91);
buf.put((byte) s.length());
buf.put(s.getBytes());
if (this.needPad(s))
{
buf.put((byte) 0);
}
final Integer index = pi.getIndex();
if (index != null)
{
// Path Segment 28, from wireshark
buf.put((byte) 0x28);
buf.put(index.byteValue());
}
pi.encode(buf);
}
}

/** @return Is path of odd length, requiring a pad byte? */
private boolean needPad(final String s)
{
// Findbugs: x%2==1 fails for negative numbers
return (s.length() % 2) != 0;
}

@Override
public String toString()
{
final StringBuilder buf = new StringBuilder();
buf.append("Path Symbol(0x91) ");
for (final PathAndIndex pi : this.paths)
for (final PathElement pi : this.elements)
{
if (buf.length() > 18)
{
buf.append(", ");
}
buf.append('\'').append(pi).append('\'');
if (this.needPad(pi.getPath()))
if (pi.needPad())
{
buf.append(", 0x00");
}
Expand Down

0 comments on commit af8338a

Please sign in to comment.