Commit 489fd36e sunke

条码规则改回原来的

增加Debug状态为18 offlile
1 个父辈 2883e5aa
......@@ -186,6 +186,9 @@ public class ServiceHandle {
statusCode = "310218";
statusText = "Emergency";
}else if(status== StorageConstants.BOX_STATUS.DEBUG){
statusCode = "18";
statusText = "Offline";
}else if(status== StorageConstants.BOX_STATUS.PROBLEM || statusBean.getStatus() == StorageConstants.STATUS.PROBLEM){
statusCode = "310218";
statusText = "Problem";
......
......@@ -21,54 +21,10 @@ import java.util.Date;
* PRODATE为生产日期xxPRODATEyyyyMMdd
* EXPDATE为过期日期xxxEXPDATEyyyyMMdd
* SP 为供应商,
* BATCH 为批次
* MSL 为MSL等级
* MEMO 备注信息
*
*
*
*条码规则,可用字段有:
* PN为物料编号即 PartNumber
* RI 为唯一码即ReelId,[RI]为所有字符串作为一个唯一码
* QTY 为数量
* PRODATE为生产日期xxPRODATEyyyyMMdd
* EXPDATE为过期日期xxxEXPDATEyyyyMMdd
* SP 为供应商,
* BATCH 为批次
* MSL 为MSL等级
* 其中必须含有PN和 RI, QTY为空时使用产品档案的封装数量
*
* 前面一位数字大于0表示去除前面第n位, -1表示不去除,并且对字段长数不做限制, 等于0表示不去除,但对字段长度做限制
* 中间一位数字0表示为变长,正值表示从前面截取,负值表示从后面开始截取,前缀和后缀及长度都有效时,需要验证字串总长度
* 后面一位数字大于0表示去除后面第n位, -1表示不去除,并且对字段长数不做限制, 等于0表示不去除,但对字段长度做限制
*
* 例一: QTY[-1:5:-1]取前5位作为数量
* 例二: QTY[-1:-5:-1]取后5位作为数量
* 例三: QTY[1:5:-1]去除前面第1位后,取前5位作为数量
* 例四: QTY[-1:-5:1]去除后面第1位后,取后5位作为数量
* 例五: QTY[1:0:-1]去除前面1位后,剩余的作为数量
* 例六: QTY[0:5:3]去除前面0位和后面3位,剩余的5位作为数量,也就是说只能为8位
*
* 示例:
* 规则为: [RI]_PN_PRODATEyyMMdd_QTY[0:5:4]
* 条码: 4500065747_CS000069_180101_030000041
* 解析后: RI=4500065747_CS000069_180101_030000041
* PN=CS000069
* 生产日期为: 2018年1月1日
* 数量为:去掉前面0位,去掉后面4位,剩下03000正好为5位,所以数量是3000,
* 如果条码变为4500065747_CS000069_180101_0300000410则会提示不合规则,因为去掉前面0位,去掉后面4位,剩下的030004是6位,不是5位
*
* 示例:
* 规则为: BATCH;PRODATEyyyyMMdd[1:8:-1];PN[1:12:-1]SP[13:5:-1]QTY[-1:-5:-1];RI
* 条码为: L00002019090199951797;E20190901 0365;B8C.R2003.V81506072019090103000;R506072019102200356
* 解析后: PN=8C.R2003.V81
* RI=R506072019102200356
* QTY=3000
* BATCH=L00002019090199951797
* PRODATE=2019年9月1日
* 供应商=50607
* 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@@
......@@ -94,23 +50,18 @@ public class BarcodeRule {
BarcodeRule newRule = new BarcodeRule();
newRule.ruleStr = ruleStr;
log.info("开始解析二维码规则:" + ruleStr);
if(!Strings.isNullOrEmpty(ruleStr)){
//先去除[prefix,length,suffix]这样的配置字段
String canSplitStr =ruleStr.replaceAll("DATE.*?\\]","");
canSplitStr = canSplitStr.replaceAll("\\[[^\\]]*?\\]","");
//先找分割符,分割出来数组数量最多的放第一个
String trySeparator = "";
//找最多的字符
String[] tempArr = new String[]{};
for (int i = 0; i < canSplitStr.length(); i++) {
char c = canSplitStr.charAt(i);
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,-1);
String[] ss = ruleStr.split(separator);
if(ss.length > tempArr.length){
trySeparator = c + trySeparator;
......@@ -124,76 +75,27 @@ public class BarcodeRule {
}
log.info("分割符为:"+newRule.separator+"长度为:"+tempArr.length);
if(tempArr.length == 0){
tempArr = new String[]{ruleStr};
}
for (int i=0; i<tempArr.length; i++) {
String fieldValue = tempArr[i];
if(!newRule.partNumber_item.hasThisField()){
if(newRule.partNumber_item.matchRule(fieldValue, i)){
log.info("partNumber: 为" + newRule.partNumber_item.toString());
}
}
if(!newRule.whole_reelId_item.hasThisField() && !newRule.reelId_item.hasThisField()){
if(newRule.whole_reelId_item.matchRule(fieldValue, i)){
log.info("whole reelId: 为" + newRule.whole_reelId_item.toString());
}else if(newRule.reelId_item.matchRule(fieldValue, i)){
log.info("reelId: 为" + newRule.reelId_item.toString());
}
}
if(!newRule.batch_item.hasThisField()){
if(newRule.batch_item.matchRule(fieldValue, i)){
log.info("batch: 为" + newRule.batch_item.toString());
}
}
if(!newRule.quantity_item.hasThisField()){
if(newRule.quantity_item.matchRule(fieldValue, i)){
log.info("quantity: 为" + newRule.quantity_item.toString());
}
}
if(!newRule.supplier_item.hasThisField()){
if(newRule.supplier_item.matchRule(fieldValue, i)){
log.info("supplier: 为" + newRule.supplier_item.toString());
}
}
if(!newRule.msl_item.hasThisField()){
if(newRule.msl_item.matchRule(fieldValue, i)){
log.info("MSL: 为" + newRule.msl_item.toString());
}
}
if(!newRule.memo_item.hasThisField()){
if(newRule.memo_item.matchRule(fieldValue, i)){
log.info("MEMO: 为" + newRule.memo_item.toString());
}
}
if(!newRule.produceDate_item.hasThisField()){
if(newRule.produceDate_item.matchRule(fieldValue,i)){
log.info("produceDate: 为" + newRule.produceDate_item.toString());
}
}
if(!newRule.expireDate_item.matchRule(fieldValue,i)){
if(newRule.expireDate_item.matchRule(fieldValue,i)){
log.info("expireDate: 为" + newRule.expireDate_item.toString());
}
}
if(!newRule.expireYear_item.hasThisField() && !newRule.expireMonth_item.hasThisField() && !newRule.expireDay_item.hasThisField()){
if(newRule.expireYear_item.matchRule(fieldValue,i)){
log.info("expireYear: 为" + newRule.expireYear_item.toString());
}else if(newRule.expireMonth_item.matchRule(fieldValue,i)){
log.info("expireMonth: 为" + newRule.expireMonth_item.toString());
}else if(newRule.expireDay_item.matchRule(fieldValue,i)){
log.info("expireDay: 为" + newRule.expireDay_item.toString());
}
if(newRule.partNumber_item.matchRule(fieldValue, i)){
log.info("partNumber: 为" + newRule.partNumber_item.toString());
}else if(newRule.whole_reelId_item.matchRule(fieldValue, i)){
log.info("whole reelId: 为" + newRule.whole_reelId_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());
}
}
boolean validRule = false;
......@@ -236,13 +138,8 @@ public class BarcodeRule {
private RuleItem quantity_item = new RuleItem("QTY");
private RuleItem produceDate_item = new RuleItem("PRODATE");
private RuleItem expireDate_item = new RuleItem("EXPDATE");
private RuleItem expireYear_item = new RuleItem("EXPYEAR");
private RuleItem expireMonth_item = new RuleItem("EXPMONTH");
private RuleItem expireDay_item = new RuleItem("EXPDAY");
private RuleItem supplier_item = new RuleItem("SP");
private RuleItem batch_item = new RuleItem("BATCH");
private RuleItem msl_item = new RuleItem("MSL");
private RuleItem memo_item = new RuleItem("MEMO");
private class RuleItem{
private RuleItem(String ruleName) {
......@@ -253,14 +150,10 @@ public class BarcodeRule {
private String name;
//位置
private int index = -1;
//前缀(-1时表示没有前缀)如果要验证总长度,可设置为0
private int prefix = -1;
private int length = 0;
//后缀(-1时表示没有前缀)如果要验证总长度,可设置为0
private int suffix = -1;
//日期格式
private String formatStr = "";
//前缀
private String prefix;
//后缀
private String suffix;
@Override
public String toString() {
......@@ -268,7 +161,6 @@ public class BarcodeRule {
"name='" + name + '\'' +
", index=" + index +
", prefix='" + prefix + '\'' +
", length='" + length + '\'' +
", suffix='" + suffix + '\'' +
'}';
}
......@@ -283,87 +175,29 @@ public class BarcodeRule {
private boolean matchRule(String ruleValue, int ruleIndex){
int nameIndex = ruleValue.indexOf(name);
if(nameIndex >= 0){
int startIndex = ruleValue.indexOf("[",nameIndex + 1 );
if(startIndex > 0){
int endIndex = ruleValue.indexOf("]",nameIndex);
if(endIndex > startIndex){
index = ruleIndex;
String limitStr = ruleValue.substring(startIndex+1,endIndex);
String[] limitInfo = limitStr.split(":");
prefix = Integer.valueOf(limitInfo[0]);
length = Integer.valueOf(limitInfo[1]);
suffix = Integer.valueOf(limitInfo[2]);
if(isDateField()){
//取日期格式
formatStr = ruleValue.substring(nameIndex+name.length(),startIndex);
}
}
}else{
prefix = ruleValue.substring(0,nameIndex).length();
if(isDateField()){
suffix = 0;
formatStr = ruleValue.substring(nameIndex + name.length());
}else{
suffix = ruleValue.substring(nameIndex + name.length()).length();
}
if(prefix == 0){
prefix = -1;
}
if(suffix == 0){
suffix = -1;
}
index = ruleIndex;
}
suffix = ruleValue.substring(nameIndex + name.length());
prefix = ruleValue.substring(0,nameIndex);
index = ruleIndex;
return true;
}
return false;
}
private boolean isDateField(){
return name.contains("DATE");
}
private String getStrValue(String[] codeArr){
try{
if(index != -1){
String codeValue = codeArr[index];
if(codeValue.length() < prefix + suffix){
return null;
}
//如果有前缀和后缀
if(prefix > 0){
codeValue = codeValue.substring(prefix);
}
if(suffix > 0){
codeValue = codeValue.substring(0, codeValue.length() - suffix);
}
codeValue = codeValue.substring(prefix.length());
if(length > 0){
if(prefix >=0 && suffix >=0){
if(codeValue.length() != length){
log.info(codeValue + "的长度不是" + length);
return null;
}
}
//从前面取length位
if (codeValue.length() >= length){
return codeValue.substring(0, length);
//是否是日期
if(!name.contains("DATE")){
if(codeValue.length() < suffix.length()){
return null;
}
}else if(length < 0){
return codeValue.substring(codeValue.length()+length, codeValue.length());
codeValue = codeValue.substring(0,codeValue.length() - suffix.length());
}
//是否是日期
// if(!isDateField()){
// if(codeValue.length() < suffix){
// return null;
// }
// codeValue = codeValue.substring(0,codeValue.length() - suffix);
// }
return codeValue;
}
......@@ -377,7 +211,7 @@ public class BarcodeRule {
String value = getStrValue(codeArr);
if(!Strings.isNullOrEmpty(value)){
try{
return Float.valueOf(value).intValue();
return Integer.valueOf(value);
}catch (Exception ex){
log.warn(value + " is not a validate int");
}
......@@ -399,7 +233,7 @@ public class BarcodeRule {
c.set(Calendar.WEEK_OF_YEAR,weekOfYear);
return c.getTime();
}
return DateUtil.toDate(value,formatStr);
return DateUtil.toDate(value,suffix);
}catch (Exception ex){
log.warn(value + " is not a validate date");
}
......@@ -462,20 +296,16 @@ public class BarcodeRule {
codeBean.setError("error.barcode.noRule","解析规则未定义");
return codeBean;
}
String[] codeArr = new String[]{codeStr};
if(!Strings.isNullOrEmpty(separator)){
codeArr = codeStr.split(separator,-1);
//条码与规则长度对应不上
if(codeArr.length != length){
log.info("条码["+codeStr+"]与规则【"+ruleStr+"】长度不同");
codeBean.setError("error.barcode.wrongLength",new String[]{}, "条码["+codeStr+"]长度错误");
return codeBean;
}else{
codeArr = codeStr.split(separator,length);
}
String[] codeArr = codeStr.split(separator);
//条码与规则长度对应不上
if(codeArr.length != length){
log.info("条码["+codeStr+"]与规则【"+ruleStr+"】长度不同");
codeBean.setError("error.barcode.wrongLength",new String[]{}, "条码["+codeStr+"]长度错误");
return codeBean;
}else{
codeArr = codeStr.split(separator,length);
}
Barcode b = new Barcode();
String reelId = "";
......@@ -516,42 +346,8 @@ public class BarcodeRule {
}
Date produceDate= produceDate_item.getDateValue(codeArr);
if(produceDate_item.hasThisField() && produceDate == null){
log.info("条码解析失败,PRODATE字段不合规则");
codeBean.setError("error.barcode.noField",new String[]{"PRODATE"},"条码解析失败,未找到PRODATE字段");
return codeBean;
}
Date expireDate= expireDate_item.getDateValue(codeArr);
if(produceDate != null && expireDate == null){
Calendar c = Calendar.getInstance();
c.setTime(produceDate);
int expYear = expireYear_item.getIntValue(codeArr);
int expMonth = expireMonth_item.getIntValue(codeArr);
int expDay = expireDay_item.getIntValue(codeArr);
boolean hasExpire = false;
if(expYear != -1){
c.add(Calendar.YEAR, expYear);
hasExpire = true;
}else if(expMonth != -1){
c.add(Calendar.MONTH, expMonth);
hasExpire = true;
}else if(expDay != -1){
c.add(Calendar.DAY_OF_YEAR, expDay);
hasExpire = true;
}
if(hasExpire){
expireDate = c.getTime();
}
}
String supplier = supplier_item.getStrValue(codeArr);
String msl = msl_item.getStrValue(codeArr);
if(memo_item.hasThisField()){
String memo = memo_item.getStrValue(codeArr);
b.setMemo(memo);
}
//南瑞使用整个条码作为唯一码
if(DataCache.isProductionFor(DataCache.CUSTOMER.NANRUI)){
......@@ -561,6 +357,17 @@ public class BarcodeRule {
b.setBarcode(reelId);
codeBean.setCodeStr(reelId);
}
//昆山纬创资通的PN 中带有-解析出 ProviderNum
//if(DataCache.isProductionFor(DataCache.CUSTOMER.WEICHUANG)){
if(partNumber.contains("-")){
String[] pnInfo = partNumber.split("-");
String providerNumber = pnInfo[pnInfo.length-1];
String pn = partNumber.replace("-"+providerNumber, "");
partNumber = pn;
b.setProviderNumber(providerNumber);
}
//}
b.setPartNumber(partNumber);
b.setAmount(quantity);
b.setProduceDate(produceDate);
......@@ -568,7 +375,6 @@ public class BarcodeRule {
b.setProvider(supplier);
b.setPlateSize(reelWidth);
b.setHeight(reelHeight);
//b.setMsl(msl);
codeBean.setBarcode(b);
return codeBean;
......@@ -609,8 +415,7 @@ public class BarcodeRule {
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@xxPRODATEyyMMdd@xxxEXPDATEyyMMdd@13@14@15@16@VSP@3S[RI]@xQTYxxxxxx@20@21@22@23";
rule = "[RI]_PN_PRODATEyyMMdd_QTYxxxx";
//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@@";
......@@ -622,64 +427,28 @@ public class BarcodeRule {
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@6D192400@14D210205@30PY@Z1@K0@16K0@V97294419@3SS191908055X1Z@Q4000NAR000@20T1@1TAM961458E@2T@1Z@@";
// codeStr = "=1+0x0-0x0=A0002";
//阳光电源
rule = "[RI]_PN_3_QTYxxxx";
rule = "[RI]_PN_PRODATEyyMMdd_QTY[0:5:4]";
codeStr = "4500065747_CS000069_180101_030000041";
codeStr = "4500089600_DC000045_190528_003000006";
//博郎 QR = [VEE PN] + [Quantity] + [UID] + [Expire date] + [MSD level] + [Light class] + [Color class] + [Forward voltage] + [00000000]
// codeStr = "001967370080049250852020112000010P130BT200MH00000000";
//
// rule = "PN[0:8:-1]QTY[8:5:-1]RI[13:7:-1]EXPDATEyyyyMMdd[20:8:-1]MSL[28:4:-1]BATCH[32:4:-1]SP[36:4:-1]";
// //佳世达
rule ="BATCH;PRODATEyyyy-MM-dd[1:10:-1]EXPD[-1:-4:-1];PN[1:12:-1]SP[13:5:-1]QTY[-1:-5:-1];RI";
codeStr = "L00002019090199951797;E2019-09-01 0365;B8C.R2003.V81506072019090103000;R506072019102200356";
//rule = "1;PN;BATCH;PRODATEyyyyMMdd;EXPDATEyyyyMMdd;QTY;RI";
//codeStr = ";RMK1608-K-B-10300;7B16081217B0;20200506;20250203;5000;000";
//rule = "1;2;PN;BATCH[2:0:-1];5;6;EXPDATEyyyy/MM/dd[2:0:8];QTY[2:0:-1];8;9;10;RI[2:0:-1]";
//codeStr = "锘緿M;IN13浠跺\uE69C缁勮灪涓濇壒09913涓栬揪;BM;PH11A-ZZG02;DDDDMO180525;GG624-71;SX2019/8/25 0:00:00;SL2;HG;DL;ZP;QT90CECB112D1448DA91F485D0AA984B08";
codeStr = "??M8431CZ8133ZHMJQ21EJ3545-2016;IN?????BM6005K01;PH20012005;SC;CJ;LH;DDMO20072200041;CB;SX2023-2-27 0:00:00;SL1;JL;KG;HG;DL;ZP;QT;XT1001AS10000000517DT2;";
rule = "1;2;PN[-1:0:-1];BATCH[-1:0:-1];5;6;7;8;9;EXPDATEyyyy-MM-dd[2:0:8];QTY[2:0:-1];12;13;14;15;16;17;RI[-1:0:-1]";
//rule = "1>PN[-1:0:6]>3>RI[-1:0:6]";
//codeStr = "<ckdh>CC200612000194</ckdh><jybh>ADK20-\n" +"JY19060012~/jybh>";
//rule = "1;2;PN[-1:0:-1];BATCH[-1:0:-1];5;6;7;8;9;EXPDATEyyyy-MM-dd[2:0:8];QTY[2:0:-1];12;13;14;15;16;17;RI[-1:0:-1]";
//codeStr = "DM8431CZ8133ZHMJQ21EJ3545-2016;IN;BM6005K01;PH20012005;SC;CJ;LH;DDMO20072200041;CB;SX2023-2-27 00:10:00;SL1;JL;KG;HG;DL;ZP;QT;XT1001AS10000000517DT2;";
//803
//rule = "PN[6:0:-1],EXPDATEyyyy-MM-dd[-1:0:-1],BATCH,RI";
//codeStr = "ALPHA OL107E,2021-11-11,00714026Z,002";
codeStr = "IAC;6013A0073981;20200810;R2032E1128;10000;MI020A600568;";
//codeStr = "IAC;B23D45;20200204;DCRFFD;45;WER23;";
rule = "SP;PN;PRODATEyyw[0:4:0];BATCH;QTY;RI;";
//rule = "SP;PN;PRODATEyyyyMMdd[0:8:0];BATCH;QTY;RI;";
codeStr = "L0000000000SB4371503H;E20201026 0365;B7H.22424.211821872020102603000;R821872020102602187";
rule = "BATCH;2;PN[1:12:-1]SP[13:5:-1]QTY[-1:-5:-1];RI";
// codeStr = "64.10R05.6DL-960286|20213239|5000|W64960286132194G0|CYNTEC";
// rule = "PN|BATCH|QTY|RI|SP";
codeStr = "=7x8=4500065747_CS000069_180101_030000004";
rule = "PN|BATCH|QTY|RI|SP";
codeStr = "=7x8=64.22005.6DL-015402|-20191011|10000|M640154024119FJZ7|YAGEO";
codeStr="=7x8=78.10421.2FL-602371|SB2271867K-2024|10000|M786023712420S2U0|DARFON";
BarcodeRule br = BarcodeRule.newRule(rule);
Barcode b = br.toCodeBean(codeStr).getBarcode();
if(b != null){
System.out.println("PN:"+b.getPartNumber());
System.out.println("RI:"+b.getBarcode());
System.out.println("Amount:"+b.getAmount());
System.out.println("BATCH:"+b.getBatch());
//System.out.println("MSL:"+b.getMsl());
System.out.println("PRODATE:"+b.getProduceDate());
System.out.println("EXPDATE:"+b.getExpireDate());
System.out.println("Supllier:"+b.getProvider());
System.out.println("Memo:"+b.getMemo());
}else{
log.info("解析失败");
}
Barcode b = br.toCodeBean(codeStr).getBarcode();
System.out.println("PN:"+b.getPartNumber());
// System.out.println("RI:"+b.getBarcode());
// System.out.println("Amount:"+b.getAmount());
// System.out.println("BATCH:"+b.getBatch());
//System.out.println("PRODATE:"+b.getProduceDate());
// System.out.println("EXPDATE:"+b.getExpireDate());
System.out.println(b.getBarcode());
System.out.println(b.getBatch());
System.out.println(b.getProvider());
System.out.println(b.getProviderNumber());
// URI uri = new URI("smb://materialtower:Flextronics1@10.222.42.48/materialtower/re20190822161258_result.txt");
// String smbFileName = uri.getScheme() + "://" + uri.getHost() + "" + uri.getPath() + "abc.txt";
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!