From 462fd21cf4309f8e28794114022d2df16b831477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luiz=20Coutinho?= Date: Fri, 3 Nov 2017 10:39:12 -0200 Subject: [PATCH] SGTIN-96 and SGTIN-198 implementation --- .../option/SGTIN/SGTINExtensionDigit.java | 42 ++ .../option/SGTIN/SGTINFilterValue.java | 38 ++ .../epctagcoder/option/SGTIN/SGTINHeader.java | 42 ++ .../option/SGTIN/SGTINTagSize.java | 62 +++ .../SGTINPartitionTableList.java | 50 +++ .../epctagcoder/parse/SGTIN/ParseSGTIN.java | 425 ++++++++++++++++++ 6 files changed, 659 insertions(+) create mode 100644 epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINExtensionDigit.java create mode 100644 epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINFilterValue.java create mode 100644 epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINHeader.java create mode 100644 epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINTagSize.java create mode 100644 epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/partitionTable/SGTINPartitionTableList.java create mode 100644 epctagcoder/src/main/java/org/epctagcoder/parse/SGTIN/ParseSGTIN.java diff --git a/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINExtensionDigit.java b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINExtensionDigit.java new file mode 100644 index 0000000..7f52d38 --- /dev/null +++ b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINExtensionDigit.java @@ -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 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); + } + + + + +} diff --git a/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINFilterValue.java b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINFilterValue.java new file mode 100644 index 0000000..3d7b174 --- /dev/null +++ b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINFilterValue.java @@ -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 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); + } + + +} diff --git a/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINHeader.java b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINHeader.java new file mode 100644 index 0000000..6871e9a --- /dev/null +++ b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINHeader.java @@ -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 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); + } + + +} diff --git a/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINTagSize.java b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINTagSize.java new file mode 100644 index 0000000..c811fac --- /dev/null +++ b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/SGTINTagSize.java @@ -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 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); + } + + +} diff --git a/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/partitionTable/SGTINPartitionTableList.java b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/partitionTable/SGTINPartitionTableList.java new file mode 100644 index 0000000..a6a3607 --- /dev/null +++ b/epctagcoder/src/main/java/org/epctagcoder/option/SGTIN/partitionTable/SGTINPartitionTableList.java @@ -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 list = new ArrayList(); + + 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; + } + + +} diff --git a/epctagcoder/src/main/java/org/epctagcoder/parse/SGTIN/ParseSGTIN.java b/epctagcoder/src/main/java/org/epctagcoder/parse/SGTIN/ParseSGTIN.java new file mode 100644 index 0000000..64b05ed --- /dev/null +++ b/epctagcoder/src/main/java/org/epctagcoder/parse/SGTIN/ParseSGTIN.java @@ -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 optionalCompanyPrefix = Optional.ofNullable(extensionDigit); + Optional optionalRfidTag = Optional.ofNullable(rfidTag); + Optional optionalEpcTagURI = Optional.ofNullable(epcTagURI); + Optional 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 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"); \ No newline at end of file