BarcodeRule.java 15.8 KB
package com.myproject.util;

import com.google.common.base.Strings;
import com.myproject.bean.CodeBean;
import com.myproject.bean.update.Barcode;
import com.myproject.bean.update.Storage;
import com.myproject.bean.update.StoragePos;
import com.myproject.webapp.controller.webService.DataCache;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 *
 * 条码规则,可用字段有:
 * PN为物料编号即 PartNumber
 * RI 为唯一码即ReelId
 * QTY 为数量
 * PRODATE为生产日期xxPRODATEyyyyMMdd
 * EXPDATE为过期日期xxxEXPDATEyyyyMMdd
 * SP 为供应商,
 * BATCH 为批次,
 *
 * 其中必须含有PN和 RI, QTY为空时使用产品档案的封装数量,如果字段前或者后有无用字符,可使用 x(一个字符) 替代
 *
 * 1@2@3@PN@5@6@7@8@9@10@11@12@13@14@15@16@RI@18@19@20@21@22@23@24
 * [)>@06@12S0002@P5292001000@1P1690215@31P1690215@12V527973628@10VCHN-YANTAI@2P@20P@6D20170626@14D20171223@30PY@ZN@K0@16K0@V815@3SB370000000EZZ@Q500GRM000@20T1@1TMT72543954@2T@1Z@@
 *
 * [@xx@xx@partNO.@xx@xx@xx@xx@xx@生产日期@过期日期@xx@xx@xx@xx@xx@psckid唯一码@重量@xx@xx@xx@xc@@' 
 *
 * 零包
 * [)>@06@12S0002@P8638516895@1P08EFP2285A@31P8638516895@12V315754465@10VCHN-KUNSHAN@2P@20P1-K-PR77-XD7179#QX0#@6D20180216@14D20180816@30PY@ZN@K0@16K0@V97219759@3SS000200062918@Q20NAR000@20T1@1T8021___0718@2T@1Z08EFP2285A@@
 * 整包
 * [)>@06@12S0002@P8638516895@1P08EFP2285A@31P8638516895@12V315754465@10VCHN-KUNSHAN@2P@20P1-K-PR77-XD7179#QX0#@6D20180216@14D20180816@30PY@ZN@K0@16K0@V97219759@3SS000200062909@Q15NAR000@20T1@1T@2T@1Z08EFP2285A@@
 *
 * Created by sunke on 2017/3/25.
 */
public class BarcodeRule {

    protected final static Logger log = LogManager.getLogger(BarcodeRule.class);

    private BarcodeRule() {

    }

    public static BarcodeRule newRule(String ruleStr){
        BarcodeRule newRule = new BarcodeRule();
        newRule.ruleStr = ruleStr;
        log.info("开始解析二维码规则:" + ruleStr);
        if(!Strings.isNullOrEmpty(ruleStr)){
            //先找分割符,分割出来数组数量最多的放第一个
            String trySeparator = "";
            //找最多的字符
            String[] tempArr = new String[]{};
            for (int i = 0; i < ruleStr.length(); i++) {
                char c = ruleStr.charAt(i);
                if(!Character.isLetterOrDigit(c)){
                    //不是字母和数字才可以作为分割符
                    String separator = toRegexStr(c + "");
                    if(trySeparator.indexOf(c) == -1){
                        String[] ss = ruleStr.split(separator);
                        if(ss.length > tempArr.length){
                            trySeparator = c + trySeparator;

                            newRule.separator = separator;
                            tempArr = ss;
                        }else{
                            trySeparator =  trySeparator + c;
                        }
                    };
                }
            }

            log.info("分割符为:"+newRule.separator+"长度为:"+tempArr.length);
            for (int i=0; i<tempArr.length; i++) {
                String fieldValue = tempArr[i];
                if(newRule.partNumber_item.matchRule(fieldValue, i)){
                    log.info("partNumber: 为" +  newRule.partNumber_item.toString());
                }else if(newRule.reelId_item.matchRule(fieldValue, i)){
                    log.info("reelId: 为" +  newRule.reelId_item.toString());
                }else if(newRule.batch_item.matchRule(fieldValue, i)){
                    log.info("batch: 为" +  newRule.batch_item.toString());

                }else if(newRule.quantity_item.matchRule(fieldValue, i)){
                    log.info("quantity: 为" +  newRule.quantity_item.toString());

                }else if(newRule.supplier_item.matchRule(fieldValue, i)){
                    log.info("supplier: 为" +  newRule.supplier_item.toString());

                }else if(newRule.produceDate_item.matchRule(fieldValue,i)){
                    log.info("produceDate: 为" +  newRule.produceDate_item.toString());
                }else if(newRule.expireDate_item.matchRule(fieldValue,i)){
                    log.info("expireDate: 为" +  newRule.expireDate_item.toString());
                }
            }
            if(newRule.reelId_item.hasThisField()  && newRule.partNumber_item.hasThisField()){
                newRule.length = tempArr.length;
            }else{
                if(DataCache.isProductionFor(DataCache.CUSTOMER.NANRUI)){
                    //南瑞的没有RI
                    if(newRule.partNumber_item.hasThisField()){
                        newRule.length = tempArr.length;
                    }
                }else{
                    log.warn("编码规则中必须包含 RI 和 PN");
                }
            }
        }
        return newRule;
    }

    private String ruleStr;
    private String separator;
    private int length = 0;
    //private String[] ruleArr = new String[]{};

    private RuleItem partNumber_item = new RuleItem("PN");
    private RuleItem reelId_item  = new RuleItem("RI");
    private RuleItem quantity_item = new RuleItem("QTY");
    private RuleItem produceDate_item = new RuleItem("PRODATE");
    private RuleItem expireDate_item = new RuleItem("EXPDATE");
    private RuleItem supplier_item = new RuleItem("SP");
    private RuleItem batch_item  = new RuleItem("BATCH");

    private class RuleItem{
        private RuleItem(String ruleName) {
            this.name = ruleName;
        }

        //名称
        private String name;
        //位置
        private int index = -1;
        //前缀
        private String prefix;
        //后缀
        private String suffix;

        @Override
        public String toString() {
            return "{" +
                    "name='" + name + '\'' +
                    ", index=" + index +
                    ", prefix='" + prefix + '\'' +
                    ", suffix='" + suffix + '\'' +
                    '}';
        }

        /**
         * 解析规则中是否包含本字段
         */
        private boolean hasThisField(){
            return index != -1;
        }

        private boolean matchRule(String ruleValue, int ruleIndex){
            int nameIndex = ruleValue.indexOf(name);
            if(nameIndex >= 0){
                suffix = ruleValue.substring(nameIndex + name.length());
                prefix = ruleValue.substring(0,nameIndex);
                index = ruleIndex;
                return true;
            }
            return false;
        }

        private String getStrValue(String[] codeArr){
            try{

                if(index != -1){
                    String codeValue = codeArr[index];
                    //如果有前缀和后缀
                    codeValue = codeValue.substring(prefix.length());

                    //是否是日期
                    if(!name.contains("DATE")){
                        if(codeValue.length() < suffix.length()){
                            return null;
                        }
                        codeValue = codeValue.substring(0,codeValue.length() - suffix.length());
                    }
                    return codeValue;
                }

            }catch (Exception e){
                log.error("解析出错:",e);
            }
            return null;
        }

        private int getIntValue(String[] codeArr){
            String value = getStrValue(codeArr);
            if(!Strings.isNullOrEmpty(value)){
                try{
                    return Integer.valueOf(value);
                }catch (Exception ex){
                    log.warn(value + " is not a validate int");
                }
            }
            return -1;
        }

        private Date getDateValue(String[] codeArr){
            String value = getStrValue(codeArr);
            if(!Strings.isNullOrEmpty(value)){
                try{
                    return DateUtil.toDate(value,suffix);
                }catch (Exception ex){
                    log.warn(value + " is not a validate date");
                }
            }
            return null;
        }
    }

    public CodeBean toCodeBean(String codeStr){
        log.info("开始解析条码["+codeStr+"]");
        String fullCodeStr = codeStr;
        CodeBean codeBean = new CodeBean();
        codeBean.setCodeStr(codeStr);
        //是否带有位置信息
        float locationX = -1f;
        float locationY = -1f;
        int reelWidth = -1;
        int reelHeight = -1;
        //=摄像头编号+条码X坐标x条码Y坐标-料盘尺寸=CODE
        //=1+123.4x100.5-7x12=CODE
        if(codeStr.startsWith("=") && codeStr.contains("x") && codeStr.contains("+")){
            codeStr = codeStr.substring(1);
            String[] arr = codeStr.split("=", 2);
            String location = arr[0];
            String[] locationInfoArr = location.split("\\+");
            String cameraIndexStr = locationInfoArr[0];
            //摄像头编号信息
            int cameraIndex = Integer.valueOf(cameraIndexStr);

            String[] locationSizeArr = locationInfoArr[1].split("-",2);

            String locationStr = locationSizeArr[0];

            //带有位置信息
            String[] locationArr = locationStr.split("x");
            locationX = Float.valueOf(locationArr[0].trim());
            locationY = Float.valueOf(locationArr[1].trim());

            if(locationSizeArr.length > 1){
                String sizeStr = locationSizeArr[1];
                //带有料盘宽高信息
                if(!Strings.isNullOrEmpty(sizeStr)){
                    String[] sizeInfoArr = sizeStr.split("x",2);
                    reelWidth = Integer.parseInt(sizeInfoArr[0].trim());
                    reelHeight = Integer.parseInt(sizeInfoArr[1].trim());
                }
            }

            codeBean.setCameraIndex(cameraIndex);
            codeBean.setLocationX(locationX);
            codeBean.setLocationY(locationY);
            codeBean.setReelWidth(reelWidth);
            codeBean.setReelHeight(reelHeight);
            codeStr = arr[1];

        }
        codeBean.setCodeStr(codeStr);
        if(!isValidRule()){
            codeBean.setError("解析规则未定义");
            return codeBean;
        }
        String[] codeArr = codeStr.split(separator);
        //条码与规则长度对应不上
        if(codeArr.length != length){
            log.info("条码["+codeStr+"]与规则【"+ruleStr+"】长度不同");
            codeBean.setError("条码["+codeStr+"]长度错误");
            return codeBean;
        }else{
            codeArr = codeStr.split(separator,length);
        }

        Barcode b = new Barcode();
        b.setFullCodeStr(fullCodeStr);

        String reelId = reelId_item.getStrValue(codeArr);
        if(!DataCache.isProductionFor(DataCache.CUSTOMER.NANRUI)){
            if(Strings.isNullOrEmpty(reelId)){
                log.info("条码解析失败,未找到RI字段");
                codeBean.setError("条码解析失败,未找到RI字段");
                return codeBean;
            }
        }

        String partNumber = partNumber_item.getStrValue(codeArr);

        if(Strings.isNullOrEmpty(partNumber)){
            log.info("条码解析失败,未找到PN字段");
            codeBean.setError("条码解析失败,未找到PN字段");
            return codeBean;
        }
        if(batch_item.hasThisField()){
            String batch = batch_item.getStrValue(codeArr);
            b.setBatch(batch);
        }
        int quantity = 1;
        if(quantity_item.hasThisField()){
            quantity = quantity_item.getIntValue(codeArr);
            if(quantity == -1){
                log.info("条码解析失败,未找到QTY 字段");
                codeBean.setError("条码解析失败,未找到QTY 字段");
                return codeBean;
            }
        }

        Date produceDate= produceDate_item.getDateValue(codeArr);
        Date expireDate= expireDate_item.getDateValue(codeArr);
        String supplier = supplier_item.getStrValue(codeArr);

        //南瑞使用整个条码作为唯一码
        if(DataCache.isProductionFor(DataCache.CUSTOMER.NANRUI)){
            b.setBarcode(codeStr);
            codeBean.setCodeStr(codeStr);
        }else{
            b.setBarcode(reelId);
            codeBean.setCodeStr(reelId);
        }
        b.setPartNumber(partNumber);
        b.setAmount(quantity);
        b.setProduceDate(produceDate);
        b.setExpireDate(expireDate);
        b.setProvider(supplier);
        b.setPlateSize(reelWidth);
        b.setHeight(reelHeight);

        codeBean.setBarcode(b);
        return codeBean;
    }

    /**
     * 转义正则特殊字符 ($()*+.[]?\^{}
     * @return
     */
    private static String toRegexStr(String separator){
        //转义正则特殊字符 ($()*+.[]?\^{}
        return separator.replace("\\", "\\\\").replace("*", "\\*")
                         .replace("+", "\\+").replace("|", "\\|")
                         .replace("{", "\\{").replace("}", "\\}")
                         .replace("(", "\\(").replace(")", "\\)")
                         .replace("^", "\\^").replace("$", "\\$")
                         .replace("[", "\\[").replace("]", "\\]")
                         .replace("?", "\\?").replace(",", "\\,")
                         .replace(".", "\\.").replace("&", "\\&");
    }

    /**
     * 是否是有效的规则
     */
    public boolean isValidRule(){
        return length > 0;
    }

    public static void main(String args[]) throws Exception{
//
//        String rule = "1@2@3@PN@5@6@7@8@9@10@xxPRODATEyyyyMMdd@xxxEXPDATEyyyyMMdd@13@14@15@16@17@RI@xQTYxxxxxx@20@21@22@23";
//        rule = "1@2@3@PPN@5@6@7@8@9@10@xxPRODATEyyyyMMdd@xxxEXPDATEyyyyMMdd@13@14@15@16@17@RI@xQTYxxxxxx@20@21@22@23";
//        rule = "1@2@3@PPN@5@6@7@8@9@10@11@12@13@14@15@16@17@xxRI@xQTYxxxxxx@20@21@22@23";
//        rule = "1@2@3@PPN@5@6@7@8@9@10@11@12@13@14@15@16@xxRI@xxQTYxxxxxx@19@20@21@22@23@@";
//        rule = "1;2;3;PN;QTY;6";
//        rule = "PN|RI|3|4|5|6|7|8|9|10|11|12|13|14|15|QTYxxx|17";
//        rule="1@2@3@PPN@5@6@7@8@9@10@xxPRODATEyyyyMMdd@xxxEXPDATEyyyyMMdd@13@14@15@16@VSP@3SRI@xQTYxxxxxx@20@21@22@23";
//        BarcodeRule br = BarcodeRule.newRule(rule);
//
//        //String codeStr = "[)>@06@12S0002@P5292001000@1P1690215@31P1690215@12V527973628@10VCHN-YANTAI@2P@20P@6D20170626@14D20171223@30PY@ZN@K0@16K0@V815@3SB370000000EZZ@Q500GRM000@20T1@1TMT72543954@2T@1Z@@";
//
//        String codeStr = "[)>@06@12S001@P8909000244@1P@31P@10V@2P@20P@6D@14D@30PY@Z @K@16K@V000000@3SA20180913-01554@Q03000NAR000@20T1@1TH000000000B172621166@Q03000@2T@1Z@@\n" +
//                "\n" +
//                "1@2@3@PPN@5@6@7@8@9@10@11@12@13@14@15@16@17@xxRI@xQTYxxxxxx@20@21@22@23@@";
//        //codeStr = "[)>@06@12S0002@P8638516894@1PB025008SS5107-04@31P8638516894@12V865379333@10VCHN-WUXI@2PV01@20P#QX0#PROBIMER77MA-1@6D20180214@14D20180805@30PY@ZN@K55158066@16K0@V97198306@3SS500000570451@Q20NAR000@20T1@1T35H-0618@2T@1ZTRIPOD@@";
//        codeStr = "[)>@06@12S001@P8909000244@1P@31P@10V@2P@20P@6D@14D@30PY@Z @K@16K@V000000@3SA20180913-01554@Q03000NAR000@20T1@1TH000000000B172621166@Q03000@2T@1Z@@";
//        codeStr = ")》@06@120S002@P8908018028@1PBLM18PG121SH1D@31PBLM18PG121SH1D@12V690588124@10VJPN-AM@2P@20P@6D20190805@14D20210205@30PY@Z1@K0@16K0@V97294419@3SS191908055X1Z@Q4000NAR000@20T1@1TAM961458E@2T@1Z@@";
//        codeStr = "[)>@06@12S0002@P1267360241@1P@31P@12V71862569@10VPHL-MADUYA@2P@20P@6D20201026@14D20221105@30PY@ZN@K@16K@V0000002262@3SS000002493953@Q5000NAR000@20T1@1T204413664H@2T@1Z@@";
////        codeStr = "=1+0x0-0x0=A0002";
//        Barcode b = br.toCodeBean(codeStr).getBarcode();
//        System.out.println("SP:"+b.getProvider());
//        String str = "3SS191908055X1Z";
//        System.out.println(str.substring(str.length() - 13));

        String memo = "2=2=yellow;1=1=green;statusLight=10=30";
    }

}