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" // 老版心跳 heartTextNew = "1e001a02000151" // 新版心跳 printText = "1e00180200" // 老版打印回调 printTextNew = "1e00190200" // 新版打印回调 printSuccessText = "1e001802000150" // 老版消息打印 printSuccessTextNew = "1e001902000150" // 新版消息打印 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 //离线 printerStatusOfflineAll = -9 //其他异常状态 PrintSoundMaxNumber = 16 // 十六进制最大补位 PlaceFillingParam = "0" // 补位参数 ) //标签 const ( signBR = "
" //换行 signCenter = "
" //居中 signLeft = "" //居左 signRight = "" //居右 signBig = "" //字体放大 signHighBig = "" //字体纵向放大 signWideBig = "" //字体横向放大 signQrCenter = "" //二维码居中 signQrLeft = "" //二维码居左 signQrRight = "" //二维码居右 signSound, signSoundEnd = "", "" // 声音结束标签 // GPRS通讯说明,打印机识别二进制码 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" // 音频指令(自定义语音指令) hexSignSoundSolidification = "1B594155" // 音频指令(固化指令) //起始标签 -- 自定义标签utf8转码为gbk字符集 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 ( 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) regexpSoundSpan = regexp.MustCompile(signSound + "(.*?)" + signSoundEnd) ) 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 //TimeoutMap map[string]chan bool //退出channel TimeoutMap map[string]bool //退出channel *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() 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) buildTimeoutMap(key string) { t.Lock() defer t.Unlock() //dataChan := make(chan bool) //t.TimeoutMap[key] = dataChan t.TimeoutMap[key] = true } func (t *TcpClient) getTimeOut(key string) bool { t.RLock() defer t.RUnlock() //return <-t.TimeoutMap[key] return t.TimeoutMap[key] } func (t *TcpClient) buildAllMap(key string) { t.Lock() defer t.Unlock() t.MsgMap[key] = make(chan *model.PrintMsg, 1024) t.CallBackMap[key] = make(chan string, 1024) t.TimeoutMap[key] = true } func (t *TcpClient) getPrintStatus(key string) int { t.RLock() defer t.RUnlock() if t.Clients[key] != nil { return t.Clients[key].Status } else { return printerStatusOfflineAll } } func (t *TcpClient) getPrintConn(key string) net.Conn { t.RLock() defer t.RUnlock() if t.Clients[key] != nil { return t.Clients[key].C } else { return nil } } func (t *TcpClient) getPrintStatusTime(key string) time.Time { t.RLock() defer t.RUnlock() if t.Clients[key] != nil { return t.Clients[key].StatusTime } else { return utils.ZeroTimeValue } } // 获取连接对象 func (t *TcpClient) getClients(key string) *PrintInfo { t.RLock() defer t.RUnlock() return t.Clients[key] } 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.Clients[key] != nil { t.Clients[key].Status = status //t.Clients[key].StatusTime = time.Now() } } func (t *TcpClient) setPrintStatusTime(key string) { t.Lock() defer t.Unlock() if t.Clients[key] != nil { //t.Clients[key].Status = status t.Clients[key].StatusTime = time.Now() } } func (t *TcpClient) addMsgChan(printMsg *model.PrintMsg) (err error) { t.Lock() defer func() { t.Unlock() if r := recover(); r != nil && r.(error).Error() == "send on closed channel" { err = fmt.Errorf("send on closed channel") } }() if t.MsgMap[printMsg.PrintNo] == nil { dataChan := make(chan *model.PrintMsg, 1024) t.MsgMap[printMsg.PrintNo] = dataChan } t.MsgMap[printMsg.PrintNo] <- printMsg return err } func (t *TcpClient) addCallbackChan(key, data string) { t.Lock() defer t.Unlock() if t.CallBackMap[key] == nil { dataChan := make(chan string, 1024) t.CallBackMap[key] = dataChan } t.CallBackMap[key] <- data } func (t *TcpClient) GetCallbackChan(key string) string { t.RLock() defer t.RUnlock() if t.CallBackMap[key] == nil { return "" } return <-t.CallBackMap[key] } func (t *TcpClient) clear(key string) { t.Lock() defer t.Unlock() t.Clients[key].C.Close() delete(t.Clients, key) close(t.MsgMap[key]) delete(t.MsgMap, key) close(t.CallBackMap[key]) delete(t.CallBackMap, key) delete(t.TimeoutMap, key) } func NewTcpClient() *TcpClient { t := &TcpClient{ Clients: make(map[string]*PrintInfo), CallBackMap: make(map[string]chan string), MsgMap: make(map[string]chan *model.PrintMsg), TimeoutMap: make(map[string]bool, 0), } 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 { var 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 printer.OfflineCount++ feilds = append(feilds, "OfflineCount") } 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 ) //写入数据 no, err := strconv.ParseInt(orderNo, 10, 64) if err != nil { globals.SugarLogger.Debug("order_msg Order_no 转换异常") } orderNoHexH, orderNoHexL = int2h8l8(no) // 将数据与模板组装 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 } //替换特殊字符,上面那个hextob转不了,先替换一下 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) { var soundStr []string for _, v1 := range strings.Split(result, byteSignSoundE) { v1 += byteSignSoundE if sounds := regexpSound.FindStringSubmatch(v1); len(sounds) > 0 { sound := sounds[1] // 将语音包转换为十六进制 voice := "" for _, v := range strings.Split(sound, "2c") { voice += hexSignSoundSolidification soundNum, _ := hex.DecodeString(v) // 十六进制转字符串 intSound, _ := strconv.ParseInt(string(soundNum), 10, 64) int16Sound := strconv.FormatInt(intSound, 16) if intSound < PrintSoundMaxNumber { // 小于十六补位 voice += PlaceFillingParam + int16Sound } else { voice += int16Sound } } soundStr = append(soundStr, voice) } } result = strings.ReplaceAll(result, byteSignSound, "*") result = strings.ReplaceAll(result, byteSignSoundE, "&") for i := 0; i < len(soundStr); i++ { fmt.Println("result=", result) start := strings.Index(result, "*") end := strings.Index(result, "&") result = strings.Replace(result, result[start:end+1], soundStr[i], 1) } } // 自动合成语音功能 //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, "") // 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] 1", printMsg.PrintNo, printMsg.OrderNo) } if printMsg.PrintNo == "" { return fmt.Errorf("此打印信息打印机编号为空!printMsg printNo:[%v], orderNo :[%v] 2", printMsg.PrintNo, printMsg.OrderNo) } if printMsg.OrderNo == "" { return fmt.Errorf("此打印信息订单序号为空!printMsg printNo:[%v], orderNo :[%v] 3", 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 != 0 { h = "0" + h } if len(l)%2 != 0 { 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, 32) return i } func xtob(x string) string { base, _ := strconv.ParseInt(x, 16, 10) return strconv.FormatInt(base, 2) }