Skip to content

Commit

Permalink
SGTIN-96 and SGTIN-198 implementation
Browse files Browse the repository at this point in the history
jlcout committed Nov 3, 2017
1 parent b662dfe commit 462fd21
Showing 6 changed files with 659 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.epctagcoder.option.SGTIN;

import java.util.LinkedHashMap;
import java.util.Map;

public enum SGTINExtensionDigit {
EXTENSION_0(0),
EXTENSION_1(1),
EXTENSION_2(2),
EXTENSION_3(3),
EXTENSION_4(4),
EXTENSION_5(5),
EXTENSION_6(6),
EXTENSION_7(7),
EXTENSION_8(8),
EXTENSION_9(9);

private int value;

private SGTINExtensionDigit(int value) {
this.value = value;
}

public int getValue() {
return value;
}

private static final Map<Integer, SGTINExtensionDigit> BY_CODE_MAP = new LinkedHashMap<>();
static {
for (SGTINExtensionDigit rae : SGTINExtensionDigit.values()) {
BY_CODE_MAP.put(rae.value, rae);
}
}

public static SGTINExtensionDigit forCode(int code) {
return BY_CODE_MAP.get(code);
}




}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.epctagcoder.option.SGTIN;

import java.util.LinkedHashMap;
import java.util.Map;

public enum SGTINFilterValue {
ALL_OTHERS_0(0),
POS_ITEM_1(1),
CASE_2(2),
RESERVED_3(3),
INNER_PACK_4(4),
RESERVED_5(5),
UNIT_LOAD_6(6),
COMPONENT_7(7);

private int value;

private SGTINFilterValue(int value) {
this.value = value;
}

public int getValue() {
return value;
}

private static final Map<Integer, SGTINFilterValue> BY_CODE_MAP = new LinkedHashMap<>();
static {
for (SGTINFilterValue rae : SGTINFilterValue.values()) {
BY_CODE_MAP.put(rae.value, rae);
}
}

public static SGTINFilterValue forCode(int code) {
return BY_CODE_MAP.get(code);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.epctagcoder.option.SGTIN;

import java.util.LinkedHashMap;
import java.util.Map;

public enum SGTINHeader {
HEADER_00110000("00110000") {
public Integer getTagSize() {
return 96;
}
},
HEADER_00110110("00110110") {
public Integer getTagSize() {
return 198;
}
};

private String value;
public abstract Integer getTagSize();


private SGTINHeader(String value) {
this.value = value;
}

public String getValue() {
return value;
}

private static final Map<String, SGTINHeader> BY_CODE_MAP = new LinkedHashMap<>();
static {
for (SGTINHeader rae : SGTINHeader.values()) {
BY_CODE_MAP.put(rae.value, rae);
}
}

public static SGTINHeader forCode(String code) {
return BY_CODE_MAP.get(code);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package org.epctagcoder.option.SGTIN;

import java.util.LinkedHashMap;
import java.util.Map;

public enum SGTINTagSize {
BITS_96(96) {
public Integer getHeader() {
return 48;
}
public Integer getSerialBitCount() {
return 38;
}
public Integer getSerialMaxLenght() {
return 11;
}
public Long getSerialMaxValue() {
return 274_877_906_943L;
}
},
BITS_198(198) {
public Integer getHeader() {
return 54;
}
public Integer getSerialBitCount() {
return 140;
}
public Integer getSerialMaxLenght() {
return 20;
}
public Long getSerialMaxValue() {
return null; // not used
}
};

private int value;
public abstract Integer getHeader();
public abstract Integer getSerialBitCount();
public abstract Integer getSerialMaxLenght();
public abstract Long getSerialMaxValue();

private SGTINTagSize(int value) {
this.value = value;
}

public int getValue() {
return value;
}

private static final Map<Integer, SGTINTagSize> BY_CODE_MAP = new LinkedHashMap<>();
static {
for (SGTINTagSize rae : SGTINTagSize.values()) {
BY_CODE_MAP.put(rae.value, rae);
}
}

public static SGTINTagSize forCode(int code) {
return BY_CODE_MAP.get(code);
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.epctagcoder.option.SGTIN.partitionTable;

import java.util.ArrayList;
import java.util.List;

import org.epctagcoder.option.TableItem;


public class SGTINPartitionTableList {
static final private List<TableItem> list = new ArrayList<TableItem>();

static {
list.add( new TableItem(0, 40, 12, 4, 1) );
list.add( new TableItem(1, 37, 11, 7, 2) );
list.add( new TableItem(2, 34, 10, 10, 3) );
list.add( new TableItem(3, 30, 9, 14, 4) );
list.add( new TableItem(4, 27, 8, 17, 5) );
list.add( new TableItem(5, 24, 7, 20, 6) );
list.add( new TableItem(6, 20, 6, 24, 7) );
}

public SGTINPartitionTableList() {

}


public TableItem getPartitionByL(Integer index) {
TableItem tableItem = null;
for (TableItem item : list) {
if (item.getL()==index) {
tableItem = item;
break;
}
}
return tableItem;
}

public TableItem getPartitionByValue(Integer index) {
TableItem tableItem = null;
for (TableItem item : list) {
if (item.getPartitionValue()==index) {
tableItem = item;
break;
}
}
return tableItem;
}


}
425 changes: 425 additions & 0 deletions epctagcoder/src/main/java/org/epctagcoder/parse/SGTIN/ParseSGTIN.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,425 @@
package org.epctagcoder.parse.SGTIN;

import java.util.Optional;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.epctagcoder.option.PrefixLength;
import org.epctagcoder.option.TableItem;
import org.epctagcoder.option.SGTIN.SGTINExtensionDigit;
import org.epctagcoder.option.SGTIN.SGTINFilterValue;
import org.epctagcoder.option.SGTIN.SGTINHeader;
import org.epctagcoder.option.SGTIN.SGTINTagSize;
import org.epctagcoder.option.SGTIN.partitionTable.SGTINPartitionTableList;
import org.epctagcoder.result.SGTIN;
import org.epctagcoder.util.Converter;


public class ParseSGTIN {
private SGTIN sgtin = new SGTIN();
private SGTINExtensionDigit extensionDigit;
private String companyPrefix;
private PrefixLength prefixLength;
private SGTINTagSize tagSize;
private SGTINFilterValue filterValue;
private String itemReference;
private String serial;
private String rfidTag;
private String epcTagURI;
private String epcPureIdentityURI;
private TableItem tableItem;
private int remainder;

public static ChoiceStep Builder() {
return new Steps();
}

private ParseSGTIN(Steps steps) {
this.extensionDigit = steps.extensionDigit;
this.companyPrefix = steps.companyPrefix;
this.tagSize = steps.tagSize;
this.filterValue = steps.filterValue;
this.itemReference = steps.itemReference;
this.serial = steps.serial;
this.rfidTag = steps.rfidTag;
this.epcTagURI = steps.epcTagURI;
this.epcPureIdentityURI = steps.epcPureIdentityURI;
parse();
}



private void parse() {
Optional<SGTINExtensionDigit> optionalCompanyPrefix = Optional.ofNullable(extensionDigit);
Optional<String> optionalRfidTag = Optional.ofNullable(rfidTag);
Optional<String> optionalEpcTagURI = Optional.ofNullable(epcTagURI);
Optional<String> optionalEpcPureIdentityURI = Optional.ofNullable(epcPureIdentityURI);

if ( optionalRfidTag.isPresent() ) {
String inputBin = Converter.hexToBin(rfidTag);
String headerBin = inputBin.substring(0, 8);
String filterBin = inputBin.substring(8,11);
String partitionBin = inputBin.substring(11,14);
SGTINPartitionTableList sgtinPartitionTableList = new SGTINPartitionTableList();

tagSize = SGTINTagSize.forCode(SGTINHeader.forCode(headerBin).getTagSize());
tableItem = sgtinPartitionTableList.getPartitionByValue( Integer.parseInt(partitionBin, 2) );

String filterDec = Long.toString( Long.parseLong(filterBin, 2) );
String companyPrefixBin = inputBin.substring(14,14+tableItem.getM());
String itemReferenceWithExtensionBin = inputBin.substring(14+tableItem.getM(),14+tableItem.getM()+tableItem.getN());
String serialBin = inputBin.substring(14+tableItem.getM()+tableItem.getN() );
String companyPrefixDec = Converter.binToDec(companyPrefixBin);
String itemReferenceWithExtensionDec = Converter.binToDec(itemReferenceWithExtensionBin);
String extensionDec = itemReferenceWithExtensionDec.substring(0,1);

itemReference = itemReferenceWithExtensionDec.substring(1);

if (tagSize.getSerialBitCount()==140) {
serialBin = Converter.convertBinToBit(serialBin, 7, 8);
serial = Converter.binToString(serialBin);
} else if (tagSize.getSerialBitCount()==38) {
serial = Converter.binToDec(serialBin);
}

companyPrefix = Converter.strZero(companyPrefixDec, tableItem.getL());
extensionDigit = SGTINExtensionDigit.forCode( Integer.parseInt(extensionDec) );
filterValue = SGTINFilterValue.forCode( Integer.parseInt(filterDec) );
prefixLength = PrefixLength.forCode(tableItem.getL());

} else {

if ( optionalCompanyPrefix.isPresent() ) {
SGTINPartitionTableList sgtinPartitionTableList = new SGTINPartitionTableList();
prefixLength = PrefixLength.forCode( companyPrefix.length() );

validateCompanyPrefix();

tableItem = sgtinPartitionTableList.getPartitionByL( prefixLength.getValue() );

validateExtensionDigitAndItemReference();
validateSerial();

} else {

if ( optionalEpcTagURI.isPresent() ) {
Pattern pattern = Pattern.compile("(urn:epc:tag:sgtin-)(96|198)\\:([0-7])\\.(\\d+)\\.([0-8])(\\d+)\\.(\\w+)");
Matcher matcher = pattern.matcher(epcTagURI);

if ( matcher.matches() ) {
tagSize = SGTINTagSize.forCode( Integer.parseInt(matcher.group(2)) );
filterValue = SGTINFilterValue.forCode( Integer.parseInt(matcher.group(3)) );
companyPrefix = matcher.group(4);
prefixLength = PrefixLength.forCode( matcher.group(4).length() );
extensionDigit = SGTINExtensionDigit.forCode( Integer.parseInt(matcher.group(5)) );
itemReference = matcher.group(6);
serial = matcher.group(7);
} else {
throw new IllegalArgumentException("EPC Tag URI is invalid");
}

} else if ( optionalEpcPureIdentityURI.isPresent() ) {
Pattern pattern = Pattern.compile("(urn:epc:id:sgtin)\\:(\\d+)\\.([0-8])(\\d+)\\.(\\w+)");
Matcher matcher = pattern.matcher(epcPureIdentityURI);

if ( matcher.matches() ) {
companyPrefix = matcher.group(2);
prefixLength = PrefixLength.forCode( matcher.group(2).length() );
extensionDigit = SGTINExtensionDigit.forCode( Integer.parseInt(matcher.group(3)) );
itemReference = matcher.group(4);;
serial = matcher.group(5);
} else {
throw new IllegalArgumentException("EPC Pure Identity is invalid");
}


}

SGTINPartitionTableList sgtinPartitionTableList = new SGTINPartitionTableList();
tableItem = sgtinPartitionTableList.getPartitionByL( prefixLength.getValue() );

}

}

String outputBin = getBinary();
String outputHex = Converter.binToHex( outputBin );

sgtin.setEpcScheme("sgtin");
sgtin.setApplicationIdentifier("AI 414 + AI 254");
sgtin.setTagSize(Integer.toString(tagSize.getValue()));
sgtin.setFilterValue(Integer.toString(filterValue.getValue()));
sgtin.setPartitionValue(Integer.toString(tableItem.getPartitionValue()));
sgtin.setPrefixLength(Integer.toString(prefixLength.getValue()));
sgtin.setCompanyPrefix(companyPrefix);
sgtin.setItemReference(itemReference);
sgtin.setExtensionDigit(Integer.toString(extensionDigit.getValue()));
sgtin.setSerial(serial);
sgtin.setCheckDigit(Integer.toString(getCheckDigit()));
sgtin.setEpcPureIdentityURI(String.format("urn:epc:id:sgtin:%s.%s%s.%s", companyPrefix, extensionDigit.getValue(), itemReference, serial));
sgtin.setEpcTagURI(String.format("urn:epc:tag:sgtin-%s:%s.%s.%s%s.%s", tagSize.getValue(), filterValue.getValue(), companyPrefix, extensionDigit.getValue(), itemReference, serial));
sgtin.setEpcRawURI(String.format("urn:epc:raw:%s.x%s", tagSize.getValue()+remainder, outputHex ));
sgtin.setBinary(outputBin);
sgtin.setRfidTag(outputHex);

}


private String getBinary() {
StringBuilder bin = new StringBuilder();

remainder = (int) (Math.ceil((tagSize.getValue()/16.0))*16)-tagSize.getValue();

bin.append( Converter.decToBin(tagSize.getHeader(), 8) );
bin.append( Converter.decToBin(filterValue.getValue(), 3) );
bin.append( Converter.decToBin(tableItem.getPartitionValue(), 3) );
bin.append( Converter.decToBin(Integer.parseInt(companyPrefix), tableItem.getM()) );
bin.append( Converter.decToBin(Integer.parseInt(Integer.toString(extensionDigit.getValue())+itemReference), tableItem.getN()) );

if (tagSize.getValue()==198) {
bin.append( Converter.fill(Converter.StringtoBinary(serial, 7), tagSize.getSerialBitCount()+remainder ) );
} else if (tagSize.getValue()==96) {
bin.append( Converter.decToBin(serial, tagSize.getSerialBitCount()+remainder ) );
}

return bin.toString();
}


private Integer getCheckDigit() {
String value = new StringBuilder()
.append(extensionDigit.getValue())
.append(companyPrefix)
.append(itemReference)
.toString();

Integer d14 = (10 - ((3
* (Character.getNumericValue(value.charAt(0)) + Character.getNumericValue(value.charAt(2))
+ Character.getNumericValue(value.charAt(4))
+ Character.getNumericValue(value.charAt(6)) + Character.getNumericValue(value.charAt(8))
+ Character.getNumericValue(value.charAt(10)) + Character.getNumericValue(value.charAt(12)))
+ (Character.getNumericValue(value.charAt(1)) + Character.getNumericValue(value.charAt(3))
+ Character.getNumericValue(value.charAt(5)) + Character.getNumericValue(value.charAt(7))
+ Character.getNumericValue(value.charAt(9)) + Character.getNumericValue(value.charAt(11))))
% 10)) % 10;

return d14;
}


public SGTIN getSGTIN() {
return sgtin;
}

public String getRfidTag() {
return Converter.binToHex( getBinary() );
}




private void validateExtensionDigitAndItemReference() {
StringBuilder value = new StringBuilder()
.append(extensionDigit.getValue())
.append(itemReference);

if ( value.length()!=tableItem.getDigits() ) {
throw new IllegalArgumentException(String.format("Concatenation between Extension Digit \"%d\" and Item Reference \"%s\" has %d length and should have %d length",
extensionDigit.getValue(), itemReference, value.length(), tableItem.getDigits()));
}
}

private void validateCompanyPrefix() {
Optional<PrefixLength> optionalPefixLength = Optional.ofNullable(prefixLength);
if ( !optionalPefixLength.isPresent() ) {
throw new IllegalArgumentException("Company Prefix is invalid. Length not found in the partition table");
}

}

private void validateSerial() {
if (tagSize.getValue()==198 ) {
if ( serial.length()>tagSize.getSerialMaxLenght() ) {
throw new IllegalArgumentException("Serial value is out of range. Should be up to 20 alphanumeric characters");
}
} else if (tagSize.getValue()==96 ) {
if ( Long.parseLong(serial) >tagSize.getSerialMaxValue() ) {
throw new IllegalArgumentException("Serial value is out of range. Should be less than or equal 274,877,906,943");
}
if ( serial.startsWith("0") ) {
throw new IllegalArgumentException("Serial with leading zeros is not allowed");
}
}

}




public static interface ChoiceStep {
ExtensionDigiStep withCompanyPrefix(String companyPrefix);
BuildStep withRFIDTag(String rfidTag);
BuildStep withEPCTagURI(String epcTagURI);
TagSizeStep withEPCPureIdentityURI(String epcPureIdentityURI);
}

public static interface ExtensionDigiStep {
ItemReferenceStep withExtensionDigit(SGTINExtensionDigit extensionDigit);
}

public static interface ItemReferenceStep {
SerialStep withItemReference(String itemReference);
}

public static interface SerialStep {
TagSizeStep withSerial(String serial);
}

public static interface TagSizeStep {
FilterValueStep withTagSize( SGTINTagSize tagSize );
}

public static interface FilterValueStep {
BuildStep withFilterValue( SGTINFilterValue filterValue );
}

public static interface BuildStep {
ParseSGTIN build();
}


private static class Steps implements ChoiceStep, ExtensionDigiStep, ItemReferenceStep, SerialStep, TagSizeStep, FilterValueStep, BuildStep {
private SGTINExtensionDigit extensionDigit;
private String companyPrefix;
private SGTINTagSize tagSize;
private SGTINFilterValue filterValue;
private String itemReference;
private String serial;
private String rfidTag;
private String epcTagURI;
private String epcPureIdentityURI;

@Override
public ParseSGTIN build() {
return new ParseSGTIN(this);
}

@Override
public ItemReferenceStep withExtensionDigit(SGTINExtensionDigit extensionDigit) {
this.extensionDigit = extensionDigit;
return this;
}

@Override
public SerialStep withItemReference(String itemReference) {
this.itemReference = itemReference;
return this;
}

@Override
public TagSizeStep withSerial(String serial) {
this.serial = serial;
return this;
}

@Override
public FilterValueStep withTagSize(SGTINTagSize tagSize) {
this.tagSize = tagSize;
return this;
}

@Override
public BuildStep withFilterValue(SGTINFilterValue filterValue) {
this.filterValue = filterValue;
return this;
}

@Override
public BuildStep withRFIDTag(String rfidTag) {
this.rfidTag = rfidTag;
return this;
}

@Override
public BuildStep withEPCTagURI(String epcTagURI) {
this.epcTagURI = epcTagURI;
return this;
}

@Override
public TagSizeStep withEPCPureIdentityURI(String epcPureIdentityURI) {
this.epcPureIdentityURI = epcPureIdentityURI;
return this;
}

@Override
public ExtensionDigiStep withCompanyPrefix(String companyPrefix) {
this.companyPrefix = companyPrefix;
return this;
}


}


}



//http://stackoverflow.com/questions/473282/how-can-i-pad-an-integers-with-zeros-on-the-left?noredirect=1&lq=1
//public static String lPadZero(int in, int fill){

//public Result toSGTIN() {
//return result;
//}

/*
private String getRFIDTag() {
//return new BigInteger(sscc.getBinary(), 2).toString(16).toUpperCase();
//return Converter.convertBinaryToHex(sgtin.getBinary().toString()).toUpperCase();
return Converter.convertBinaryToHex(getBinary().toString()).toUpperCase();
}
*/

/*
private String getEPCPureIdentityURI() {
return String.format("urn:epc:id:sgtin:%s.%s%s.%s",
companyPrefix,
extensionDigit.getValue(),
itemReference,
serial);
//return new StringBuilder()
// .append("urn:epc:id:sgtin:")
// .append(companyPrefix).append(".")
// .append(extensionDigit.getValue()).append(itemReference).append(".")
// .append(serial)
// .toString();
}
*/

/*
private String getEPCTagURI() {
return String.format("urn:epc:tag:sgtin-%s:%s.%s.%s%s.%s",
tagSize.getValue(),
filterValue.getValue(),
companyPrefix,
extensionDigit.getValue(),
itemReference,
serial);
//return new StringBuilder()
// .append("urn:epc:tag:sgtin-")
// .append(tagSize.getValue()).append(":")
// .append(filterValue.getValue()).append(".")
// .append(companyPrefix).append(".")
// .append(extensionDigit.getValue())
// .append(itemReference).append(".")
// .append(serial)
// .toString();
}
*/


/// https://github.com/taimos/common-gs1/blob/master/src/main/java/de/taimos/common/gs1/SSCCHelper.java
// throw new IllegalArgumentException("Invalid gln");

0 comments on commit 462fd21

Please sign in to comment.