package event import ( "encoding/hex" "fmt" "git.rosy.net.cn/baseapi/utils" "git.rosy.net.cn/jx-callback/business/jxutils" "git.rosy.net.cn/jx-callback/business/model" "git.rosy.net.cn/jx-callback/business/model/dao" "git.rosy.net.cn/jx-callback/globals" "net" "regexp" "strconv" "strings" "sync" "time" ) const ( heartText = "1e000f02000151" printText = "1e00180200" printSuccessText = "1e001802000150" printErrWithoutPaper = "05" printMsgAlreadySend = 2 //已经发出打印消息 printMsgSuccess = 1 //打印成功 printMsgWait = 0 //待打印 printMsgFail = -1 //打印失败(打印机报出) printMsgErr = -2 //京西报出 printMsgAlreadyLoad = 3 //已放入队列 heartErrNormal = "00" //正常 heartErrWithoutPaper = "04" //心跳错,缺纸 heartErrHot = "08" //过热 printerStatusOnlineWithoutPaper = 2 //在线缺纸 printerStatusOnline = 1 //在线 printerStatusOffline = -1 //离线 ) //标签 const ( signBR = "
" //换行 signCenter = "
" //居中 signLeft = "" //居左 signRight = "" //居右 signBig = "" //字体放大 signHighBig = "" //字体纵向放大 signWideBig = "" //字体横向放大 signQrCenter = "" //二维码居中 signQrLeft = "" //二维码居左 signQrRight = "" //二维码居右 signSound = "" //声音 hexSignBROrEXE = "0a" hexSignCenter = "1b6101" hexSignLeft = "1b6100" hexSignRight = "1b6102" hexSignNormal = "1b2100" hexSignBig = "1b2130" hexSignHighBig = "1b2110" hexSignWideBig = "1b2120" hexSignQrCenter = "1d5802" hexSignQrLeft = "1d5800" hexSignQrRight = "1d5804" hexSignQr = "1b5a000106" //"1b5a000106" 0600 : 后面二维码的字节数 hexSignQrEnd = "000a1b40" //000a0a0a1b40 hexSignSound = "1d6b40" byteSignBR = "3c62723e" //换行 byteSignCenter = "3c63656e7465723e" //居中 byteSignLeft = "3c6c6566743e" //居左 byteSignRight = "3c72696768743e" //居右 byteSignBig = "3c623e" //字体放大 byteSignHighBig = "3c68623e" //字体纵向放大 byteSignWideBig = "3c77623e" //字体横向放大 byteSignQrCenter = "3c7172633e" byteSignQrLeft = "3c71726c3e" byteSignQrRight = "3c7172723e" byteSignSound = "3c736f756e643e" byteSignCenterE = "3c2f63656e7465723e" //居中 byteSignLeftE = "3c2f6c6566743e" //居左 byteSignRightE = "3c2f72696768743e" //居右 byteSignBigE = "3c2f623e" //字体放大 byteSignHighBigE = "3c2f68623e" //字体纵向放大 byteSignWideBigE = "3c2f77623e" //字体横向放大 byteSignQrCenterE = "3c2f7172633e" byteSignQrLeftE = "3c2f71726c3e" byteSignQrRightE = "3c2f7172723e" byteSignSoundE = "3c2f736f756e643e" ) var ( //t = &TcpClient{} printErrMap = map[string]string{ printErrWithoutPaper: "打印机缺纸!", } signMap = map[string]string{ byteSignBR: hexSignBROrEXE, } regexpQrc = regexp.MustCompile(byteSignQrCenter + "(.*?)" + byteSignQrCenterE) regexpQrl = regexp.MustCompile(byteSignQrLeft + "(.*?)" + byteSignQrLeftE) regexpQrr = regexp.MustCompile(byteSignQrRight + "(.*?)" + byteSignQrRightE) regexpSound = regexp.MustCompile(byteSignSound + "(.*?)" + byteSignSoundE) //printMsgChan = make(chan *model.PrintMsg, 1024) //printMsgCallbackMap = make(map[string]chan string, 1024) //printMsgChanFail = make(chan *model.PrintMsg, 1024) timeoutChan = make(chan int, 10) ) type PrintInfo struct { C net.Conn Status int // 2 //在线缺纸 1 //在线 -1 //离线 StatusTime time.Time } //连接的客户端,吧每个客户端都放进来 type TcpClient struct { Clients map[string]*PrintInfo //放tcp连接的,printNo 为key MsgMap map[string]chan *model.PrintMsg //放打印信息的,printNo为key CallBackMap map[string]chan string //放打印信息回调信息的,printNo为key *sync.RWMutex } type GetPrintStatus struct { PrintNo string //打印机编号 AppID int } //从连接池删除,并关闭连接 func (t *TcpClient) delConn(key string) { t.Lock() defer t.Unlock() if t.Clients[key].C != nil { t.Clients[key].C.Close() } delete(t.Clients, key) } //添加到连接池中 func (t *TcpClient) addConn(c net.Conn, key string, status int) { t.Lock() defer t.Unlock() globals.SugarLogger.Debugf("addConn key: %s", key) t.Clients[key] = &PrintInfo{ C: c, Status: status, StatusTime: time.Now(), } } func (t *TcpClient) buildMsgMap(key string) { t.Lock() defer t.Unlock() dataChan := make(chan *model.PrintMsg, 1024) t.MsgMap[key] = dataChan } func (t *TcpClient) buildCallBackMap(key string) { t.Lock() defer t.Unlock() dataChan := make(chan string, 1024) t.CallBackMap[key] = dataChan } func (t *TcpClient) getPrintStatus(key string) int { t.RLock() defer t.RUnlock() if t.isExist(key) { return t.Clients[key].Status } else { return printerStatusOffline } } func (t *TcpClient) getPrintConn(key string) net.Conn { t.RLock() defer t.RUnlock() if t.isExist(key) { return t.Clients[key].C } else { return nil } } func (t *TcpClient) isExistMsg(key string) bool { t.RLock() defer t.RUnlock() if t.MsgMap[key] == nil { return false } else { return true } } func (t *TcpClient) isExistCallback(key string) bool { t.RLock() defer t.RUnlock() if t.CallBackMap[key] == nil { return false } else { return true } } func (t *TcpClient) isExist(key string) bool { t.RLock() defer t.RUnlock() if t.Clients[key] == nil { return false } else { return true } } func (t *TcpClient) setPrintStatus(key string, status int) { t.Lock() defer t.Unlock() if t.isExist(key) { t.Clients[key].Status = status t.Clients[key].StatusTime = time.Now() } } func (t *TcpClient) addMsgChan(printMsg *model.PrintMsg) { t.Lock() defer t.Unlock() if !t.isExistMsg(printMsg.PrintNo) { t.buildMsgMap(printMsg.PrintNo) } globals.SugarLogger.Debugf("addMsgChan msgID: %s", printMsg.MsgID) t.MsgMap[printMsg.PrintNo] <- printMsg } func (t *TcpClient) addCallbackChan(key, data string) { t.Lock() defer t.Unlock() if !t.isExistCallback(key) { t.buildCallBackMap(key) } t.CallBackMap[key] <- data } func NewTcpClient() *TcpClient { t := &TcpClient{ Clients: make(map[string]*PrintInfo), CallBackMap: make(map[string]chan string), MsgMap: make(map[string]chan *model.PrintMsg), } t.RWMutex = new(sync.RWMutex) return t } func printStatus2JxStatus(printStatus string) (status int) { if printStatus == heartErrWithoutPaper { return printerStatusOnlineWithoutPaper } else if printStatus == heartErrNormal { return printerStatusOnline } return status } func getCallbackMsgInfo(data string) (orderNo int64, printNo string) { orderNo = h8l82int(data[len(data)-6:len(data)-4], data[len(data)-4:len(data)-2]) printNoData, _ := hex.DecodeString(data[len(printSuccessText) : len(data)-6]) printNo = string(printNoData) return orderNo, printNo } func changePrinterStatus(printNo string, status int) { var ( db = dao.GetDB() ) if printer, err := dao.GetPrinter(db, printNo); err == nil && printer != nil { feilds := []string{} if printer.Status != status { printer.Status = status feilds = append(feilds, "Status") } isOnline := 0 if status == printerStatusOnline || status == printerStatusOnlineWithoutPaper { isOnline = model.YES } else { isOnline = model.NO } if isOnline != printer.IsOnline { printer.IsOnline = isOnline feilds = append(feilds, "IsOnline") } if len(feilds) > 0 { dao.UpdateEntity(db, printer, feilds...) } } } func buildMsg(printMsg *model.PrintMsg) (data []byte, err error) { var ( content = printMsg.Content orderNo = printMsg.OrderNo str = "1e" const1 = "0200ff50" printInit = "1b40" //打印机初始化 //voice = "1d6b401dfd001a01015b7631365d736f756e64622cc4fad3d0d0c2b6a9b5a5c0b1" //语音,中国 //qr = "1d58021b5a0001061600747470733a2f2f7777772e62616964752e636f6d2f1b000A0A0A1B40" orderNoHexH, orderNoHexL, printData string ) //写入数据 orderNoHexH, orderNoHexL = int2h8l8(int64(orderNo)) printDataGBK, _ := jxutils.Utf8ToGbk([]byte(replaceContentOther(content))) printData = hex.EncodeToString(printDataGBK) printData = replaceContent(printData, printMsg) lenData := int64(len(str) + len(const1) + len(orderNoHexH) + len(orderNoHexL) + len(printInit) + 2 + 4 + len(printData)) x1, x2 := int2h8l8(lenData / 2) dataStr := str + x1 + x2 + const1 + orderNoHexH + orderNoHexL + printInit + printData check := getCheckSum(dataStr) return jxutils.Hextob(dataStr + check), err } func replaceContentOther(content string) string { return strings.ReplaceAll(content, "⃣️", " ") } func getCheckSum(str string) (check string) { var sum int64 for i := 0; i < len(str); i = i + 2 { b := string(str[i]) + string(str[i+1]) bt, _ := strconv.ParseInt(b, 16, 32) sum += bt } _, check = int2h8l8(sum) return check } //内容中的标签替换成指令 func replaceContent(content string, printMsg *model.PrintMsg) (result string) { var ( lenqr int hexLenqr string ) result = content for k, v := range signMap { if strings.Contains(result, k) { result = strings.ReplaceAll(result, k, v) } } if strings.Contains(result, byteSignCenter) && strings.Contains(result, byteSignCenterE) { result = strings.ReplaceAll(result, byteSignCenter, hexSignCenter) result = strings.ReplaceAll(result, byteSignCenterE, hexSignBROrEXE+hexSignLeft) } if strings.Contains(result, byteSignLeft) && strings.Contains(result, byteSignLeftE) { result = strings.ReplaceAll(result, byteSignLeft, hexSignLeft) result = strings.ReplaceAll(result, byteSignLeftE, hexSignBROrEXE+hexSignLeft) } if strings.Contains(result, byteSignRight) && strings.Contains(result, byteSignRightE) { result = strings.ReplaceAll(result, byteSignRight, hexSignRight) result = strings.ReplaceAll(result, byteSignRightE, hexSignBROrEXE+hexSignLeft) } if strings.Contains(result, byteSignBig) && strings.Contains(result, byteSignBigE) { result = strings.ReplaceAll(result, byteSignBig, hexSignBig) result = strings.ReplaceAll(result, byteSignBigE, hexSignBROrEXE+hexSignNormal) } if strings.Contains(result, byteSignHighBig) && strings.Contains(result, byteSignHighBigE) { result = strings.ReplaceAll(result, byteSignHighBig, hexSignHighBig) result = strings.ReplaceAll(result, byteSignHighBigE, hexSignBROrEXE+hexSignNormal) } if strings.Contains(result, byteSignWideBig) && strings.Contains(result, byteSignWideBigE) { result = strings.ReplaceAll(result, byteSignWideBig, hexSignWideBig) result = strings.ReplaceAll(result, byteSignWideBigE, hexSignBROrEXE+hexSignNormal) } if strings.Contains(result, byteSignQrCenter) && strings.Contains(result, byteSignQrCenterE) { if qrs := regexpQrc.FindStringSubmatch(result); len(qrs) > 0 { lenqr = len(qrs[1]) / 2 hexLenqr = fmt.Sprintf("%x", lenqr) if len(hexLenqr) < 2 { hexLenqr = "0" + hexLenqr } } result = strings.ReplaceAll(result, byteSignQrCenter, hexSignQrCenter+hexSignQr+hexLenqr+"00") result = strings.ReplaceAll(result, byteSignQrCenterE, hexSignQrEnd) } if strings.Contains(result, byteSignQrLeft) && strings.Contains(result, byteSignQrLeftE) { if qrs := regexpQrl.FindStringSubmatch(result); len(qrs) > 0 { lenqr = len(qrs[1]) / 2 hexLenqr = fmt.Sprintf("%x", lenqr) if len(hexLenqr) < 2 { hexLenqr = "0" + hexLenqr } } result = strings.ReplaceAll(result, byteSignQrLeft, hexSignQrLeft+hexSignQr+hexLenqr+"00") result = strings.ReplaceAll(result, byteSignQrLeftE, hexSignQrEnd) } if strings.Contains(result, byteSignQrRight) && strings.Contains(result, byteSignQrRightE) { if qrs := regexpQrr.FindStringSubmatch(result); len(qrs) > 0 { lenqr = len(qrs[1]) hexLenqr = fmt.Sprintf("%x", lenqr) if len(hexLenqr) < 2 { hexLenqr = "0" + hexLenqr } } result = strings.ReplaceAll(result, byteSignQrRight, hexSignQrRight+hexSignQr+hexLenqr+"00") result = strings.ReplaceAll(result, byteSignQrRightE, hexSignQrEnd) } if strings.Contains(result, byteSignSound) && strings.Contains(result, byteSignSoundE) { if sounds := regexpSound.FindStringSubmatch(result); len(sounds) > 0 { sound := sounds[1] if printer, _ := dao.GetPrinter(dao.GetDB(), printMsg.PrintNo); printer != nil { //先把结束标签消了 result = strings.ReplaceAll(result, byteSignSoundE, "") //fd 固定 //001a (声音数据长度高八位低八位) //0101 固定 soundPrefix := "" if printer.Sound != "" { soundPrefix = "[v" + utils.Int2Str(printer.Volume*2) + "]" + printer.Sound } else { soundPrefix = "[v" + utils.Int2Str(printer.Volume*2) + "]" } hexPrefix, _ := jxutils.Utf8ToGbk([]byte(soundPrefix)) hexPrefixStr := hex.EncodeToString(hexPrefix) realSound := hexPrefixStr + sound allLen := fmt.Sprintf("%x", (len("fd001a0101")+len(realSound))/2) if len(allLen) < 2 { allLen = "0" + allLen } soundLenH, soundLenX := int2h8l8(int64((len(realSound) + len("0101")) / 2)) result = strings.ReplaceAll(result, byteSignSound, hexSignSound+allLen+"fd"+soundLenH+soundLenX+"0100"+hexPrefixStr) } } } return result } func checkPrintMsg(db *dao.DaoDB, printMsg *model.PrintMsg) (err error) { if printMsg.Content == "" { return fmt.Errorf("此打印信息内容为空!printMsg printNo:[%v], orderNo :[%v]", printMsg.PrintNo, printMsg.OrderNo) } if printMsg.PrintNo == "" { return fmt.Errorf("此打印信息打印机编号为空!printMsg printNo:[%v], orderNo :[%v]", printMsg.PrintNo, printMsg.OrderNo) } if printMsg.OrderNo == 0 { return fmt.Errorf("此打印信息订单序号为空!printMsg printNo:[%v], orderNo :[%v]", printMsg.PrintNo, printMsg.OrderNo) } if printer, err := dao.GetPrinter(db, printMsg.PrintNo); err == nil { if printer != nil { if printer.FlowFlag == 1 { return fmt.Errorf("此打印机当月流量已用完,请及时充值!printNo:[%v]", printMsg.PrintNo) } } } return err } func int2h8l8(i int64) (h, l string) { origin2 := fmt.Sprintf("%b", i) flag := 16 - len(origin2) for i := 0; i < flag; i++ { origin2 = "0" + origin2 } begin8 := origin2[:8] end8 := origin2[8:] r1, _ := strconv.ParseInt(begin8, 2, 32) r2, _ := strconv.ParseInt(end8, 2, 32) h = fmt.Sprintf("%x", r1) l = fmt.Sprintf("%x", r2) if len(h) < 2 { h = "0" + h } if len(l) < 2 { l = "0" + l } return h, l } func h8l82int(h, l string) (i int64) { s1, s2 := xtob(h), xtob(l) flag1 := 8 - len(s1) flag2 := 8 - len(s2) for m := 0; m < flag1; m++ { s1 = "0" + s1 } for j := 0; j < flag2; j++ { s2 = "0" + s2 } i, _ = strconv.ParseInt(s1+s2, 2, 10) return i } func xtob(x string) string { base, _ := strconv.ParseInt(x, 16, 10) return strconv.FormatInt(base, 2) }