package implments

import (
	"LAPP_ACURA_MOM_BACKEND/conf"
	baseDal "LAPP_ACURA_MOM_BACKEND/dao/base"
	dal "LAPP_ACURA_MOM_BACKEND/dao/jit"
	meDal "LAPP_ACURA_MOM_BACKEND/dao/me"
	omDal "LAPP_ACURA_MOM_BACKEND/dao/om"
	"LAPP_ACURA_MOM_BACKEND/db"
	"LAPP_ACURA_MOM_BACKEND/global"
	"LAPP_ACURA_MOM_BACKEND/grmi"
	"LAPP_ACURA_MOM_BACKEND/infra/logger"
	meta "LAPP_ACURA_MOM_BACKEND/meta/jit"
	omMeta "LAPP_ACURA_MOM_BACKEND/meta/om"
	baseModel "LAPP_ACURA_MOM_BACKEND/models/base"
	model "LAPP_ACURA_MOM_BACKEND/models/jit"
	meModel "LAPP_ACURA_MOM_BACKEND/models/me"
	plnModel "LAPP_ACURA_MOM_BACKEND/models/pln"
	"LAPP_ACURA_MOM_BACKEND/plc"
	"LAPP_ACURA_MOM_BACKEND/utils"
	"encoding/hex"
	"github.com/go-xorm/xorm"
	uuid "github.com/iris-contrib/go.uuid"
	json "github.com/json-iterator/go"
	"github.com/kataras/iris/v12"
	"strings"
	"time"
)

// 南沙

// NSPackPLC 南沙项目 座椅入库 plc交互逻辑
func (impl *PackOrderServiceImplement) NSPackPLC(user *global.User, packOrderId string, projectId string, location string, packOrder *model.PackOrder) error {
	// 写入plc数据格式：第一位质量，第二位安道拓还是鹰泰生产，第三到二十七位为5种座椅四位机种代码+一位颜色代码合起来
	// 第一位：1为合格，0为不合格
	// 第二位：1为安道拓，0为樱泰
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	packOrderDao := dal.NewPackOrderDAO(session, user.PlantNr, user.UserId)
	seatData, err := impl.MakeUpSeatData(session, user, packOrderId, location, projectId)
	if err != nil {
		innerErr := impl.SaveOfflineError(session, user, packOrderId, 0, err.Error(), location, "")
		if innerErr != nil {
			return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + err.Error())
		}
		return err
	}
	if seatData == "" {
		return nil
	}
	packOrder.RFID = seatData
	err = packOrderDao.UpdateOne(packOrder)
	if err != nil {
		errorInfo := "保存座椅数据至包装单失败, 错误:" + err.Error()
		innerErr := impl.SaveOfflineError(session, user, packOrderId, 1, err.Error(), location, seatData)
		if innerErr != nil {
			return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
		}
		return grmi.NewBusinessError(errorInfo)
	}
	err, step := impl.WriteSeatDataToPLC(seatData, location)
	if err != nil {
		innerErr := impl.SaveOfflineError(session, user, packOrderId, step, err.Error(), location, seatData)
		if innerErr != nil {
			return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + err.Error())
		}
		return err
	}
	return nil
}

// SaveOfflineError 将座椅下线写入plc阶段的错误保存到数据表
func (impl *PackOrderServiceImplement) SaveOfflineError(session *xorm.Session, user *global.User, packOrderId string, step int, errInfo string, location string, seatData string) error {
	packOrderPlcErrDao := dal.NewPackOrderPlcErrorDAO(session, user.PlantNr, user.UserId)
	errData := model.PackOrderPlcError{
		PackOrderId: packOrderId,
		Status:      model.PACK_STATUS_PLANNED,
		Step:        step,
		ErrorType:   model.ERROR_TYTPE_PLC,
		ErrorInfo:   errInfo,
		Location:    location,
		SeatData:    seatData,
		Source:      "NS",
	}
	return packOrderPlcErrDao.InsertOne(&errData)
}

// WriteSeatDataToPLC 将座椅下线写入plc
func (impl *PackOrderServiceImplement) WriteSeatDataToPLC(seatData string, location string) (error, int) {
	//	1. 读取地址位 检验是否可以写入
	var plcAddress, DBAddress, plcType string
	var dbLen int
	if location == model.PACK_LOCATION_ON_LINE {
		plcAddress = conf.DbConfig.OfflinePLCAddress
		DBAddress = conf.DbConfig.OfflineDBSign
		plcType = conf.DbConfig.OfflinePLCType
	} else {
		plcAddress = conf.DbConfig.RepairPlcAddress
		DBAddress = conf.DbConfig.RepairPlcReadSign
		plcType = conf.DbConfig.RepairPlcType
	}
	valueType := "Boolean"
	retryCount := conf.DbConfig.OfflinePLCRetry
	data := baseModel.PLC{
		PLCType:      plcType,
		PLCAddress:   plcAddress,
		ValueAddress: DBAddress,
		ValueType:    valueType,
		RetryCount:   retryCount,
	}
	err := impl.SeatOfflineReadSign(data)
	if err != nil {
		return err, 2
	}
	// 2. 写入座椅数据
	if location == model.PACK_LOCATION_ON_LINE {
		DBAddress = conf.DbConfig.OfflineDBSave
		dbLen = conf.DbConfig.OfflineDBSaveLen
	} else {
		DBAddress = conf.DbConfig.RepairPlcWrite
		dbLen = conf.DbConfig.RepairPlcWriteLen
	}
	valueType = "Bytes"
	data = baseModel.PLC{
		PLCType:      plcType,
		PLCAddress:   plcAddress,
		ValueAddress: DBAddress,
		ValueType:    valueType,
		RetryCount:   retryCount,
		ValueText:    seatData,
		ByteCount:    dbLen,
	}
	err = impl.SeatOfflineWriteSeatData(data)
	if err != nil {
		return err, 3
	}

	// 3. 写入保存完成
	if location == model.PACK_LOCATION_ON_LINE {
		DBAddress = conf.DbConfig.OfflineDBSign
	} else {
		DBAddress = conf.DbConfig.RepairPlcReadSign
	}
	valueType = "Boolean"
	data = baseModel.PLC{
		PLCType:      plcType,
		PLCAddress:   plcAddress,
		ValueAddress: DBAddress,
		ValueType:    valueType,
		RetryCount:   retryCount,
		ValueText:    true,
	}
	err = impl.SeatOfflineWriteSign(data)
	if err != nil {
		return err, 4
	}
	return nil, 0
}

// SeatOfflineReadSign 座椅入库 读取标识位
func (impl *PackOrderServiceImplement) SeatOfflineReadSign(data baseModel.PLC) error {
	log, _ := logger.NewLogger("plc", "JIT")
	var retryNum int
	for {
		postData, err := json.Marshal(data)
		if err != nil {
			log.Error("座椅下线plc写入, 序列化读取标识位请求数据失败, error:" + err.Error())
			return grmi.NewBusinessError("座椅下线plc写入, 序列化读取标识位请求数据失败, error:" + err.Error())
		}
		log.Debug("座椅下线plc写入, 读取标识位请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCReadBackend)
		boday, err := utils.Post(conf.DbConfig.PLCReadBackend, postData)
		if err != nil {
			log.Error("座椅下线plc写入, 读取标识位调用失败, error:" + err.Error())
			return grmi.NewBusinessError("座椅下线plc写入, 读取标识位调用失败, error:" + err.Error())
		}
		log.Debug("座椅下线plc写入, 读取标识位请求响应:" + string(boday))

		var response baseModel.Response
		err = json.Unmarshal(boday, &response)
		if err != nil {
			log.Error("座椅下线plc写入, 解析读取标识位后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 解析读取标识位后台数据失败, error:" + err.Error())
		}
		if response.Code != iris.StatusOK {
			log.Error("座椅下线plc写入，读取标识位调用后台失败, 后台返回错误, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入，读取标识位调用后台失败, 后台返回错误, 错误:" + response.Msg)
		}
		plcData, ok := response.Data.(map[string]interface{})
		if !ok {
			log.Error("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		}
		successInterface, exist := plcData["IsSuccess"]
		if !exist {
			log.Error("座椅下线plc写入, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
		}
		success, ok := successInterface.(bool)
		if !ok {
			log.Error("座椅下线plc写入, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
		}
		if !success {
			log.Error("座椅下线plc写入, 读取写入标识后台plc执行失败, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 读取写入标识后台plc执行失败, 后台数据:" + string(boday))
		}
		content, exist := plcData["Content"]
		if !exist {
			log.Error("座椅下线plc写入, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
		}
		sign, ok := content.(bool)
		if !ok {
			log.Error("座椅下线plc写入, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
			return grmi.NewBusinessError("座椅下线plc写入, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
		}
		if sign {
			if retryNum >= 3 {
				log.Error("座椅下线plc写入, 读取标识位不允许写入且达到最大次数, 后台数据:" + string(boday))
				return grmi.NewBusinessError("座椅下线plc写入, 读取标识位不允许写入且达到最大次数, 后台数据:" + string(boday))
			}
			log.Error("座椅下线plc写入, 读取标识位不允许写入, 后台数据:" + string(boday))
			retryNum++
			time.Sleep(time.Millisecond * 500)
			continue
		} else {
			break
		}
	}
	return nil
}

// SeatOfflineWriteSeatData 座椅入库写入座椅数据
func (impl *PackOrderServiceImplement) SeatOfflineWriteSeatData(data baseModel.PLC) error {
	log, _ := logger.NewLogger("plc", "JIT")
	data.ValueText = hex.EncodeToString([]byte(data.ValueText.(string)))
	postData, err := json.Marshal(data)
	if err != nil {
		log.Error("座椅下线plc写入, 序列化座椅数据请求数据失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅下线plc写入, 序列化座椅数据请求数据失败, error:" + err.Error())
	}
	log.Debug("座椅下线plc写入, 写入座椅数据请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCWriteBackend)
	boday, err := utils.Post(conf.DbConfig.PLCWriteBackend, postData)
	if err != nil {
		log.Error("座椅下线plc写入,写入座椅数据调用失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅下线plc写入, 写入座椅数据调用失败, error:" + err.Error())
	}
	log.Debug("座椅下线plc写入, 写入座椅数据请求响应:" + string(boday))

	var response baseModel.Response
	err = json.Unmarshal(boday, &response)
	if err != nil {
		log.Error("座椅下线plc写入, 解析写入座椅数据后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 解析写入座椅数据后台数据失败, error:" + err.Error())
	}
	if response.Code != iris.StatusOK {
		log.Error("座椅下线plc写入，写入座椅数据调用后台失败, 后台返回错误, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入，写入座椅数据调用后台失败, 后台返回错误, 错误:" + response.Msg)
	}
	plcData, ok := response.Data.(map[string]interface{})
	if !ok {
		log.Error("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	successInterface, exist := plcData["IsSuccess"]
	if !exist {
		log.Error("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	success, ok := successInterface.(bool)
	if !ok {
		log.Error("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	if !success {
		log.Error("座椅下线plc写入, 写入座椅数据后台plc执行失败, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入座椅数据后台plc执行失败, 后台数据:" + string(boday))
	}
	return nil
}

// SeatOfflineWriteSign 座椅入库写入标识位
func (impl *PackOrderServiceImplement) SeatOfflineWriteSign(data baseModel.PLC) error {
	log, _ := logger.NewLogger("plc", "JIT")
	postData, err := json.Marshal(data)
	if err != nil {
		log.Error("座椅下线plc写入, 序列化保存完成请求数据失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅下线plc写入, 序列化保存完成请求数据失败, error:" + err.Error())
	}
	log.Debug("座椅下线plc写入, 写入保存完成请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCWriteBackend)
	boday, err := utils.Post(conf.DbConfig.PLCWriteBackend, postData)
	if err != nil {
		log.Error("座椅下线plc写入,写入保存完成调用失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅下线plc写入, 写入保存完成调用失败, error:" + err.Error())
	}
	log.Debug("座椅下线plc写入, 写入保存完成请求响应:" + string(boday))
	var response baseModel.Response
	err = json.Unmarshal(boday, &response)
	if err != nil {
		log.Error("座椅下线plc写入, 解析写入保存完成后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 解析写入保存完成后台数据失败, error:" + err.Error())
	}
	if response.Code != iris.StatusOK {
		log.Error("座椅下线plc写入，写入保存完成调用后台失败, 后台返回错误, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入，写入保存完成调用后台失败, 后台返回错误, 错误:" + response.Msg)
	}
	plcData, ok := response.Data.(map[string]interface{})
	if !ok {
		log.Error("座椅下线plc写入, 写入保存完成后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入保存完成后台返回data格式不正确, 后台数据:" + string(boday))
	}
	successInterface, exist := plcData["IsSuccess"]
	if !exist {
		log.Error("座椅下线plc写入, 写入保存完成后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入保存完成后台返回data格式不正确, 后台数据:" + string(boday))
	}
	success, ok := successInterface.(bool)
	if !ok {
		log.Error("座椅下线plc写入, 写入保存完成后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入保存完成后台返回data格式不正确, 后台数据:" + string(boday))
	}
	if !success {
		log.Error("座椅下线plc写入, 写入保存完成后台plc执行失败, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅下线plc写入, 写入保存完成后台plc执行失败, 后台数据:" + string(boday))
	}
	return nil
}

// MakeUpSeatData 拼接座椅数据
func (impl *PackOrderServiceImplement) MakeUpSeatData(session *xorm.Session, user *global.User, packOrderId string, location string, projectId string) (string, error) {
	packLstDao := dal.NewPackOrderItemLstDAO(session, user.PlantNr, user.UserId)
	productDao := meDal.NewProductDAO(session, user.PlantNr, user.UserId)
	projectDao := meDal.NewProjectDAO(session, user.PlantNr, user.UserId)
	serialOrderDao := omDal.NewSerialOrderDAO(session, user.PlantNr, user.UserId)
	packOrderItemLi, err := packLstDao.Select([]grmi.Predicate{meta.PackOrderItemLst_PackOrderId.NewPredicate(grmi.Equal, packOrderId),
		meta.PackOrderItemLst_Status.NewPredicate(grmi.Equal, model.PACK_STATUS_CLOSED)}, []grmi.Field{meta.PackOrderItemLst_Pos})
	if len(packOrderItemLi) == 0 {
		return "", nil
	}
	if err != nil {
		errInfo := "查询包装单子项数据失败, error:" + err.Error()
		return "", grmi.NewBusinessError(errInfo)
	}
	project, err := projectDao.SelectOne(projectId)
	if err != nil {
		errorInfo := "查询项目数据失败, 错误:" + err.Error()
		return "", grmi.NewBusinessError(errorInfo)
	}
	serialOrderIdLi := make([]interface{}, 0, len(packOrderItemLi))
	var seatData string
	seatMap := make(map[string]*meModel.Product)
	for _, packOrderItem := range packOrderItemLi {
		serialOrderIdLi = append(serialOrderIdLi, packOrderItem.SerialOrderId)
		product, err := productDao.SelectOne(packOrderItem.PartId, projectId)
		if err != nil {
			errInfo := "查询包装单子项总成数据失败, error:" + err.Error()
			innerErr := impl.SaveOfflineError(session, user, packOrderId, 0, errInfo, location, "")
			if innerErr != nil {
				return "", grmi.NewBusinessError(errInfo + ", 保存错误失败, error:" + err.Error())
			}
			return "", err
		}
		if product == nil {
			errInfo := "查询包装单子项总成不存在, 总成ID:" + packOrderItem.PartId
			return "", grmi.NewBusinessError(errInfo)
		}
		seatMap[product.CtrlStr1] = product
	}
	product, exist := seatMap[meModel.PRODUCT_TYPE_MS]
	if !exist {
		errInfo := "包装单子项中不存在主驾"
		return "", grmi.NewBusinessError(errInfo)
	}
	seatData += product.MachineCode
	seatData += product.ColorValue
	product, exist = seatMap[meModel.PRODUCT_TYPE_PS]
	if !exist {
		errInfo := "包装单子项中不存在副驾"
		return "", grmi.NewBusinessError(errInfo)
	}
	seatData += product.MachineCode
	seatData += product.ColorValue
	product, exist = seatMap[meModel.PRODUCT_TYPE_40]
	if !exist {
		errInfo := "包装单子项中不存在40%"
		return "", grmi.NewBusinessError(errInfo)
	}
	seatData += product.MachineCode
	seatData += product.ColorValue
	product, exist = seatMap[meModel.PRODUCT_TYPE_60]
	if !exist {
		errInfo := "包装单子项中不存在60%"
		return "", grmi.NewBusinessError(errInfo)
	}
	seatData += product.MachineCode
	seatData += product.ColorValue
	product, exist = seatMap[meModel.PRODUCT_TYPE_CUSHION]
	if !exist {
		errInfo := "包装单子项中不存在坐垫"
		return "", grmi.NewBusinessError(errInfo)
	}
	seatData += product.MachineCode
	seatData += product.ColorValue
	serialOrderLi, err := serialOrderDao.Select([]grmi.Predicate{omMeta.SerialOrder_SerialOrderId.NewPredicate(grmi.Include, serialOrderIdLi...)}, nil)
	if err != nil {
		errInfo := "查询包装单子项工单数据失败, error:" + err.Error()
		return "", grmi.NewBusinessError(errInfo)
	}
	if len(serialOrderIdLi) != len(packOrderItemLi) {
		errInfo := "查询包装单子项工单数据缺失"
		return "", grmi.NewBusinessError(errInfo)
	}
	var status = `1`
	for _, serialOrder := range serialOrderLi {
		if serialOrder.SerialOrderStatus.Status != baseModel.WO_STATUS_FINISHED || serialOrder.QualityCheck != baseModel.QualityCheck_STATUS_OK {
			status = `0`
		}
	}
	seatData = status + "1" + seatData
	if err = session.Begin(); err != nil {
		return "", grmi.NewBusinessError("开启事务失败, 错误:" + err.Error())
	}
	snrDao := baseDal.NewSnrDAO(session, user.PlantNr, user.UserId)
	var num string
	if location == model.PACK_LOCATION_ON_LINE {
		num, err = snrDao.GetNextSnr(project.PackOrderRFIDSnr)
	} else {
		num, err = snrDao.GetNextSnr(project.PackOrderOutLineRFIDSnr)
	}
	if err != nil {
		_ = session.Rollback()
		return "", grmi.NewBusinessError("获取RFID流水号失败, 错误:" + err.Error())
	}
	_ = session.Commit()
	timeStr := time.Now().Format("060102")
	seatData = seatData + timeStr + num
	return seatData, nil
}

// BackFlushReadSign 座椅回写 读取标识位
func (impl *PackOrderServiceImplement) BackFlushReadSign() (bool, error) {
	data := baseModel.PLC{
		PLCType:      conf.DbConfig.BackFlushPlcType,
		PLCAddress:   conf.DbConfig.BackFlushPlcAddress,
		ValueAddress: conf.DbConfig.BackFlushPlcReadSign,
		ValueType:    "Boolean",
		RetryCount:   conf.DbConfig.OfflinePLCRetry,
	}
	log, _ := logger.NewLogger("plc", "JIT")
	var retryNum int
	for {
		postData, err := json.Marshal(data)
		if err != nil {
			log.Error("座椅数据回写, 序列化读取标识位请求数据失败, error:" + err.Error())
			return false, grmi.NewBusinessError("座椅数据回写, 序列化读取标识位请求数据失败, error:" + err.Error())
		}
		log.Debug("座椅数据回写, 读取标识位请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCReadBackend)
		boday, err := utils.Post(conf.DbConfig.PLCReadBackend, postData)
		if err != nil {
			log.Error("座椅数据回写, 读取标识位调用失败, error:" + err.Error())
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位调用失败, error:" + err.Error())
		}
		log.Debug("座椅数据回写, 读取标识位请求响应:" + string(boday))

		var response baseModel.Response
		err = json.Unmarshal(boday, &response)
		if err != nil {
			log.Error("座椅数据回写, 解析读取标识位后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 解析读取标识位后台数据失败, error:" + err.Error())
		}
		if response.Code != iris.StatusOK {
			log.Error("座椅数据回写，读取标识位调用后台失败, 后台返回错误, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写，读取标识位调用后台失败, 后台返回错误, 错误:" + response.Msg)
		}
		plcData, ok := response.Data.(map[string]interface{})
		if !ok {
			log.Error("座椅数据回写, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
		}
		successInterface, exist := plcData["IsSuccess"]
		if !exist {
			log.Error("座椅数据回写, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
		}
		success, ok := successInterface.(bool)
		if !ok {
			log.Error("座椅数据回写, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
		}
		if !success {
			log.Error("座椅数据回写, 读取写入标识后台plc执行失败, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位后台plc执行失败, 后台数据:" + string(boday))
		}
		content, exist := plcData["Content"]
		if !exist {
			log.Error("座椅数据回写, 读取写入标识后台返回data格式不正确, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
		}
		sign, ok := content.(bool)
		if !ok {
			log.Error("座椅数据回写, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
			return false, grmi.NewBusinessError("座椅数据回写, 读取标识位后台返回data格式不正确, 后台数据:" + string(boday))
		}
		if sign {
			return true, nil
		} else {
			if retryNum >= 3 {
				return false, nil
			}
			log.Error("座椅数据回写, 读取标识位不允许写入, 后台数据:" + string(boday))
			retryNum++
			time.Sleep(time.Millisecond * 500)
			continue
		}
	}
}

// BackFlushReadSeatData 座椅回写 读取座椅数据
func (impl *PackOrderServiceImplement) BackFlushReadSeatData() (string, error) {
	log, _ := logger.NewLogger("plc", "JIT")
	data := baseModel.PLC{
		PLCType:      conf.DbConfig.BackFlushPlcType,
		PLCAddress:   conf.DbConfig.BackFlushPlcAddress,
		ValueAddress: conf.DbConfig.BackFlushPlcReadData,
		ValueType:    "Bytes",
		RetryCount:   conf.DbConfig.OfflinePLCRetry,
		ByteCount:    conf.DbConfig.BackFlushPlcReadLen,
	}
	postData, err := json.Marshal(data)
	if err != nil {
		log.Error("座椅数据回写, 序列化读取座椅数据请求数据失败, error:" + err.Error())
		return "", grmi.NewBusinessError("座椅数据回写, 序列化读取座椅数据请求数据失败, error:" + err.Error())
	}
	log.Debug("座椅数据回写, 读取座椅数据请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCReadBackend)
	boday, err := utils.Post(conf.DbConfig.PLCReadBackend, postData)
	if err != nil {
		log.Error("座椅数据回写, 读取座椅数据调用失败, error:" + err.Error())
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据调用失败, error:" + err.Error())
	}
	log.Debug("座椅数据回写, 读取座椅数据请求响应:" + string(boday))

	var response baseModel.Response
	err = json.Unmarshal(boday, &response)
	if err != nil {
		log.Error("座椅数据回写, 解析读取座椅数据后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 解析读取座椅数据后台数据失败, error:" + err.Error())
	}
	if response.Code != iris.StatusOK {
		log.Error("座椅数据回写，读取座椅数据调用后台失败, 后台返回错误, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写，读取座椅数据调用后台失败, 后台返回错误, 错误:" + response.Msg)
	}
	plcData, ok := response.Data.(map[string]interface{})
	if !ok {
		log.Error("座椅数据回写, 写入座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	successInterface, exist := plcData["IsSuccess"]
	if !exist {
		log.Error("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	success, ok := successInterface.(bool)
	if !ok {
		log.Error("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	if !success {
		log.Error("座椅数据回写, 读取座椅数据后台plc执行失败, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据后台plc执行失败, 后台数据:" + string(boday))
	}
	content, exist := plcData["Content"]
	if !exist {
		log.Error("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	seatContent, ok := content.(string)
	if !ok {
		log.Error("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return "", grmi.NewBusinessError("座椅数据回写, 读取座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	length := len(seatContent)
	for index, item := range seatContent {
		if item == 0 {
			length = index
			break
		}
	}
	return seatContent[:length], nil
}

// BackFlushClearSeatData 座椅回写 清空座椅数据
func (impl *PackOrderServiceImplement) BackFlushClearSeatData() error {
	log, _ := logger.NewLogger("plc", "JIT")
	data := baseModel.PLC{
		PLCType:      conf.DbConfig.BackFlushPlcType,
		PLCAddress:   conf.DbConfig.BackFlushPlcAddress,
		ValueAddress: conf.DbConfig.BackFlushPlcReadData,
		ValueType:    "Bytes",
		RetryCount:   conf.DbConfig.OfflinePLCRetry,
		ByteCount:    conf.DbConfig.BackFlushPlcReadLen,
		ValueText:    strings.Repeat("0", 96),
	}
	postData, err := json.Marshal(data)
	if err != nil {
		log.Error("座椅数据回写, 序列化清空座椅数据请求数据失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅数据回写, 序列化清空椅数据请求数据失败, error:" + err.Error())
	}
	log.Debug("座椅数据回写, 清空座椅数据请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCWriteBackend)
	boday, err := utils.Post(conf.DbConfig.PLCWriteBackend, postData)
	if err != nil {
		log.Error("座椅数据回写, 清空座椅数据调用失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅数据回写, 清空座椅数据调用失败, error:" + err.Error())
	}
	log.Debug("座椅数据回写, 清空座椅数据请求响应:" + string(boday))

	var response baseModel.Response
	err = json.Unmarshal(boday, &response)
	if err != nil {
		log.Error("座椅数据回写, 清空读取座椅数据后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 清空读取座椅数据后台数据失败, error:" + err.Error())
	}
	if response.Code != iris.StatusOK {
		log.Error("座椅数据回写，清空座椅数据调用后台失败, 后台返回错误, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写，清空座椅数据调用后台失败, 后台返回错误, 错误:" + response.Msg)
	}
	plcData, ok := response.Data.(map[string]interface{})
	if !ok {
		log.Error("座椅数据回写, 清空座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 清空座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	successInterface, exist := plcData["IsSuccess"]
	if !exist {
		log.Error("座椅数据回写, 清空座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 清空座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	success, ok := successInterface.(bool)
	if !ok {
		log.Error("座椅数据回写, 清空座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 清空座椅数据后台返回data格式不正确, 后台数据:" + string(boday))
	}
	if !success {
		log.Error("座椅数据回写, 清空座椅数据后台plc执行失败, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 清空座椅数据后台plc执行失败, 后台数据:" + string(boday))
	}
	return nil
}

// BackFlushWriteSign 座椅回写 写入完成标标识
func (impl *PackOrderServiceImplement) BackFlushWriteSign() error {
	data := baseModel.PLC{
		PLCType:      conf.DbConfig.BackFlushPlcType,
		PLCAddress:   conf.DbConfig.BackFlushPlcAddress,
		ValueAddress: conf.DbConfig.BackFlushPlcReadSign,
		ValueType:    "Boolean",
		RetryCount:   conf.DbConfig.OfflinePLCRetry,
		ValueText:    false,
	}
	log, _ := logger.NewLogger("座椅数据回写", "JIT")
	postData, err := json.Marshal(data)
	if err != nil {
		log.Error("座椅数据回写, 序列化读取完成请求数据失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅数据回写, 序列化读取完成请求数据失败, error:" + err.Error())
	}
	log.Debug("座椅数据回写, 写入读取完成请求参数:" + string(postData) + ", url:" + conf.DbConfig.PLCWriteBackend)
	boday, err := utils.Post(conf.DbConfig.PLCWriteBackend, postData)
	if err != nil {
		log.Error("座椅数据回写,写入读取完成调用失败, error:" + err.Error())
		return grmi.NewBusinessError("座椅数据回写, 写入读取完成调用失败, error:" + err.Error())
	}
	log.Debug("座椅数据回写, 写入读取完成请求响应:" + string(boday))
	var response baseModel.Response
	err = json.Unmarshal(boday, &response)
	if err != nil {
		log.Error("座椅数据回写, 解析写入读取完成后台数据失败, error:" + err.Error() + ", 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 解析写入读取完成后台数据失败, error:" + err.Error())
	}
	if response.Code != iris.StatusOK {
		log.Error("座椅数据回写，写入读取完成调用后台失败, 后台返回错误, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写，写入读取完成调用后台失败, 后台返回错误, 错误:" + response.Msg)
	}
	plcData, ok := response.Data.(map[string]interface{})
	if !ok {
		log.Error("座椅数据回写, 写入读取完成后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 写入读取完成后台返回data格式不正确, 后台数据:" + string(boday))
	}
	successInterface, exist := plcData["IsSuccess"]
	if !exist {
		log.Error("座椅数据回写, 写入读取完成后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 写入读取完成后台返回data格式不正确, 后台数据:" + string(boday))
	}
	success, ok := successInterface.(bool)
	if !ok {
		log.Error("座椅数据回写, 写入读取完成后台返回data格式不正确, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 写入读取完成后台返回data格式不正确, 后台数据:" + string(boday))
	}
	if !success {
		log.Error("座椅数据回写, 写入读取完成后台plc执行失败, 后台数据:" + string(boday))
		return grmi.NewBusinessError("座椅数据回写, 写入读取完成后台plc执行失败, 后台数据:" + string(boday))
	}
	return nil
}

func (impl *PackOrderServiceImplement) ReadPLCBackFlushDataTask() {

	log, _ := logger.NewLogger("座椅数据回写", "JIT")
	userId := "Scheduler"
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	plantDao := baseDal.NewPlantDAO(session, userId)
	plantLi, err := plantDao.Select(nil, nil)
	if err != nil {
		log.Error("查询工厂数据失败, 错误:" + err.Error())
		return
	}
	for _, plant := range plantLi {
		if plant.PlantProject != baseModel.PROJECT_NANSHA {
			continue
		}
		go impl.ReadPLCBackFlushData(&global.User{
			UserId:  userId,
			PlantNr: plant.PlantNr,
		})
	}
}

// ReadPLCBackFlushData 座椅回写 业务逻辑
func (impl *PackOrderServiceImplement) ReadPLCBackFlushData(user *global.User) {
	log, _ := logger.NewLogger("座椅数据回写", "JIT")
	uid, err := uuid.NewV1()
	if err != nil {
		log.Error("生成任务ID失败, 错误:" + err.Error())
		return
	}
	taskId := uid.String()
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	plcErrorDao := dal.NewPackOrderPlcErrorDAO(session, user.PlantNr, user.UserId)
	// 1. 先查看有没有数据方面的错误没有处理 如果有错误则不进行任务
	//dataErrorInfoLi, err := plcErrorDao.Select([]grmi.Predicate{
	//	meta.PackOrderPlcError_Location.NewPredicate(grmi.Equal, model.PACK_LOCATION_BACKFLUSH),
	//	meta.PackOrderPlcError_Status.NewPredicate(grmi.Equal, model.PACK_STATUS_PLANNED),
	//	meta.PackOrderPlcError_ErrorType.NewPredicate(grmi.Equal, plnModel.ERROR_TYPE_DATA),
	//}, nil)
	//if err != nil {
	//	log.Error("查询PLC错误项失败, 错误:"+err.Error(), "任务ID:"+taskId)
	//	return
	//}
	//if len(dataErrorInfoLi) != 0 {
	//	log.Info("存在尚未处理的数据项错误", "任务ID:"+taskId)
	//	return
	//}
	// 2. 先从PLC错误数据表中查看是否有没处理的错误
	errorInfoLi, err := plcErrorDao.Select([]grmi.Predicate{
		meta.PackOrderPlcError_Location.NewPredicate(grmi.Equal, model.PACK_LOCATION_BACKFLUSH),
		meta.PackOrderPlcError_Status.NewPredicate(grmi.Equal, model.PACK_STATUS_PLANNED),
		meta.PackOrderPlcError_ErrorType.NewPredicate(grmi.Equal, plnModel.ERROR_TYTPE_PLC),
	}, nil)
	if err != nil {
		log.Error("查询PLC错误项失败, 错误:"+err.Error(), "任务ID:"+taskId)
		return
	}
	// 如果有plc交互的错误 直接写入标识位 完成此次任务
	if len(errorInfoLi) != 0 {
		errorInfo := errorInfoLi[0]
		if err = session.Begin(); err != nil {
			log.Error("开启事务失败", "任务ID:"+taskId)
			return
		}
		errorInfo.Status = model.PACK_STATUS_CLOSED
		err = plcErrorDao.UpdateOne(&errorInfo)
		if err != nil {
			_ = session.Rollback()
			log.Error("更新PLC错误项状态失败, 错误:"+err.Error(), "任务ID:"+taskId)
			return
		}
		switch errorInfo.Step {
		case 3:
			err = impl.BackFlushClearSeatData()
			if err != nil {
				_ = session.Rollback()
				log.Error("根据PLC错误项清空座椅数据失败", "任务ID:"+taskId)
				return
			}
			fallthrough
		case 4:
			err = impl.BackFlushWriteSign()
			if err != nil {
				_ = session.Rollback()
				log.Error("根据PLC错误项写入完成标识失败", "任务ID:"+taskId)
				return
			}
		}
		_ = session.Commit()
		log.Info("完成PLC错误项修复, 错误ID:"+errorInfo.PackOrderId, "任务ID:"+taskId)
		return
	}
	// 如果没有PLC错误 从PLC读取数据回写PackOrderStatus的ShipStatus
	packOrderDao := dal.NewPackOrderDAO(session, user.PlantNr, user.UserId)
	packOrderStatusDao := dal.NewPackOrderStatusDAO(session, user.PlantNr, user.UserId)
	sign, err := impl.BackFlushReadSign()
	if err != nil {
		log.Error("读取标识位失败, 错误:"+err.Error(), "任务ID:"+taskId)
		return
	}
	if !sign {
		log.Info("暂无需要读取的数据", "任务ID:"+taskId)
		return
	}
	seatData, err := impl.BackFlushReadSeatData()
	if err != nil {
		log.Info("读取座椅数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
		return
	}
	seatBytes, err := hex.DecodeString(seatData)
	seatStr := string(seatBytes)
	if err != nil {
		log.Error("解码座椅数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
		plcErrorInfo := model.PackOrderPlcError{
			PackOrderId: taskId,
			Location:    model.PACK_LOCATION_BACKFLUSH,
			Status:      model.PACK_STATUS_PLANNED,
			Step:        -1,
			ErrorInfo:   "解码座椅数据失败, 错误:" + err.Error(),
			SeatData:    seatData,
			ErrorType:   plnModel.ERROR_TYPE_DATA,
		}
		err = plcErrorDao.InsertOne(&plcErrorInfo)
		if err != nil {
			log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
			return
		}
		err = impl.BackFlushClearSeatData()
		if err != nil {
			log.Error("清空座椅数据失败, 错误:" + err.Error())
			plcErrorInfo = model.PackOrderPlcError{
				PackOrderId: taskId,
				Location:    model.PACK_LOCATION_BACKFLUSH,
				Status:      model.PACK_STATUS_PLANNED,
				Step:        3,
				ErrorInfo:   "清空座椅数据失败, 错误:" + err.Error(),
				SeatData:    seatStr,
				ErrorType:   plnModel.ERROR_TYTPE_PLC,
			}
			err = plcErrorDao.InsertOne(&plcErrorInfo)
			if err != nil {
				log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
				return
			}
			return
		}
		err = impl.BackFlushWriteSign()
		if err != nil {
			log.Error("写入标识位失败, 错误:" + err.Error())
			plcErrorInfo = model.PackOrderPlcError{
				PackOrderId: taskId,
				Location:    model.PACK_LOCATION_BACKFLUSH,
				Status:      model.PACK_STATUS_PLANNED,
				Step:        4,
				ErrorInfo:   "写入标识位失败, 错误:" + err.Error(),
				SeatData:    seatStr,
				ErrorType:   plnModel.ERROR_TYTPE_PLC,
			}
			err = plcErrorDao.InsertOne(&plcErrorInfo)
			if err != nil {
				log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
				return
			}
			return
		}
	}
	packOrderLi, err := packOrderDao.Select([]grmi.Predicate{meta.PackOrder_RFID.NewPredicate(grmi.Equal, seatStr)}, nil)
	if err != nil {
		log.Error("查询包装单数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
		return
	}
	if len(packOrderLi) == 0 {
		log.Error("不存RFID对应的包装单, RFID:"+seatStr, "任务ID:"+taskId)
		plcErrorInfo := model.PackOrderPlcError{
			PackOrderId: taskId,
			Location:    model.PACK_LOCATION_BACKFLUSH,
			Status:      model.PACK_STATUS_PLANNED,
			Step:        -2,
			ErrorInfo:   "不存RFID对应的包装单, RFID:" + seatStr,
			SeatData:    seatStr,
			ErrorType:   plnModel.ERROR_TYPE_DATA,
		}
		err = plcErrorDao.InsertOne(&plcErrorInfo)
		if err != nil {
			log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
			return
		}
		err = impl.BackFlushClearSeatData()
		if err != nil {
			log.Error("清空座椅数据失败, 错误:" + err.Error())
			plcErrorInfo = model.PackOrderPlcError{
				PackOrderId: taskId,
				Location:    model.PACK_LOCATION_BACKFLUSH,
				Status:      model.PACK_STATUS_PLANNED,
				Step:        3,
				ErrorInfo:   "清空座椅数据失败, 错误:" + err.Error(),
				SeatData:    seatStr,
				ErrorType:   plnModel.ERROR_TYTPE_PLC,
			}
			err = plcErrorDao.InsertOne(&plcErrorInfo)
			if err != nil {
				log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
				return
			}
			return
		}
		err = impl.BackFlushWriteSign()
		if err != nil {
			log.Error("写入标识位失败, 错误:" + err.Error())
			plcErrorInfo = model.PackOrderPlcError{
				PackOrderId: taskId,
				Location:    model.PACK_LOCATION_BACKFLUSH,
				Status:      model.PACK_STATUS_PLANNED,
				Step:        4,
				ErrorInfo:   "写入标识位失败, 错误:" + err.Error(),
				SeatData:    seatStr,
				ErrorType:   plnModel.ERROR_TYTPE_PLC,
			}
			err = plcErrorDao.InsertOne(&plcErrorInfo)
			if err != nil {
				log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
				return
			}
			return
		}
	}
	if len(packOrderLi) > 1 {
		log.Error("该RFID绑定多个包装单, RFID:"+seatStr, "任务ID:"+taskId)
		plcErrorInfo := model.PackOrderPlcError{
			PackOrderId: taskId,
			Location:    model.PACK_LOCATION_BACKFLUSH,
			Status:      model.PACK_STATUS_PLANNED,
			Step:        -2,
			ErrorInfo:   "该RFID绑定多个包装单, RFID:" + seatStr,
			SeatData:    seatStr,
			ErrorType:   plnModel.ERROR_TYPE_DATA,
		}
		err = plcErrorDao.InsertOne(&plcErrorInfo)
		if err != nil {
			log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
			return
		}
		err = impl.BackFlushClearSeatData()
		if err != nil {
			log.Error("清空座椅数据失败, 错误:" + err.Error())
			plcErrorInfo = model.PackOrderPlcError{
				PackOrderId: taskId,
				Location:    model.PACK_LOCATION_BACKFLUSH,
				Status:      model.PACK_STATUS_PLANNED,
				Step:        3,
				ErrorInfo:   "清空座椅数据失败, 错误:" + err.Error(),
				SeatData:    seatStr,
				ErrorType:   plnModel.ERROR_TYTPE_PLC,
			}
			err = plcErrorDao.InsertOne(&plcErrorInfo)
			if err != nil {
				log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
				return
			}
			return
		}
		err = impl.BackFlushWriteSign()
		if err != nil {
			log.Error("写入标识位失败, 错误:" + err.Error())
			plcErrorInfo = model.PackOrderPlcError{
				PackOrderId: taskId,
				Location:    model.PACK_LOCATION_BACKFLUSH,
				Status:      model.PACK_STATUS_PLANNED,
				Step:        4,
				ErrorInfo:   "写入标识位失败, 错误:" + err.Error(),
				SeatData:    seatStr,
				ErrorType:   plnModel.ERROR_TYTPE_PLC,
			}
			err = plcErrorDao.InsertOne(&plcErrorInfo)
			if err != nil {
				log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
				return
			}
			return
		}
	}
	packOrder := packOrderLi[0]
	packOrderStatus, err := packOrderStatusDao.SelectOne(packOrder.PackOrderId)
	if err != nil {
		log.Error("查询包装单状态数据失败, 错误:" + err.Error())
		return
	}
	packOrderStatus.ShipStatus = model.SHIP_STATUS_CLOSED
	err = packOrderStatusDao.UpdateOne(packOrderStatus)
	if err != nil {
		log.Error("更新包装单状态失败, 错误:" + err.Error())
		return
	}
	packOrder.CtrlTime1 = grmi.DateTime(time.Now())
	err = packOrderDao.UpdateOne(&packOrder)
	if err != nil {
		log.Error("更新包装单回写时间失败, 错误:" + err.Error())
		return
	}
	err = impl.BackFlushClearSeatData()
	if err != nil {
		log.Error("清空座椅数据失败, 错误:" + err.Error())
		plcErrorInfo := model.PackOrderPlcError{
			PackOrderId: taskId,
			Location:    model.PACK_LOCATION_BACKFLUSH,
			Status:      model.PACK_STATUS_PLANNED,
			Step:        3,
			ErrorInfo:   "清空座椅数据失败, 错误:" + err.Error(),
			SeatData:    seatStr,
			ErrorType:   plnModel.ERROR_TYTPE_PLC,
		}
		err = plcErrorDao.InsertOne(&plcErrorInfo)
		if err != nil {
			log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
			return
		}
		return
	}
	err = impl.BackFlushWriteSign()
	if err != nil {
		log.Error("写入标识位失败, 错误:" + err.Error())
		plcErrorInfo := model.PackOrderPlcError{
			PackOrderId: taskId,
			Location:    model.PACK_LOCATION_BACKFLUSH,
			Status:      model.PACK_STATUS_PLANNED,
			Step:        4,
			ErrorInfo:   "写入标识位失败, 错误:" + err.Error(),
			SeatData:    seatStr,
			ErrorType:   plnModel.ERROR_TYTPE_PLC,
		}
		err = plcErrorDao.InsertOne(&plcErrorInfo)
		if err != nil {
			log.Error("写入PLC错误项数据失败, 错误:"+err.Error(), "任务ID:"+taskId)
			return
		}
		return
	}
}

// 讴歌

// AUCRAPackPLC 讴歌下线打包 PLC放行 -- 讴歌线
func (impl *PackOrderServiceImplement) AUCRAPackPLC() error {
	data := baseModel.PLC{
		PLCType:      conf.DbConfig.PermitThroughPlcType,
		PLCAddress:   conf.DbConfig.PermitThroughPlcAddress,
		ValueAddress: conf.DbConfig.PermitThroughPlcDB,
		ValueType:    "Boolean",
		RetryCount:   2,
		ValueText:    true,
	}
	err := impl.SeatOfflineWriteSign(data)
	return err
}

// BZPackPLC 讴歌下线打包 写包装数据到plc --缤智线
func (impl *PackOrderServiceImplement) BZPackPLC(user *global.User, workplaceConfig *baseModel.WorkPlaceConfig, packOrderId string, quality string, recordErr bool, step int) error {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	plcErrorDao := dal.NewPackOrderPlcErrorDAO(session, user.PlantNr, user.UserId)
	log, _ := logger.NewLogger("缤智线下线打包", "JIT")
	var (
		data baseModel.PLC
	)
	switch step {
	case 0, 1:
		data = baseModel.PLC{
			PLCType:      workplaceConfig.StrValue2,
			PLCAddress:   workplaceConfig.StrValue3,
			ValueAddress: workplaceConfig.StrValue4,
			ValueType:    "String",
			RetryCount:   workplaceConfig.IntValue2,
			ByteCount:    workplaceConfig.IntValue1,
		}
		existDataInterface, err := plc.ReadDataFromPLC(data, log, "String")
		if err != nil {
			errorInfo := "读取plc数据失败, " + err.Error()
			if recordErr {
				errorData := model.PackOrderPlcError{
					PackOrderId: packOrderId,
					Status:      model.PACK_STATUS_PLANNED,
					Step:        1,
					ErrorInfo:   errorInfo,
					ErrorType:   model.ERROR_TYTPE_PLC,
					Location:    model.PACK_LOCATION_ON_LINE,
					Source:      "BZ-" + quality,
					IntCtrl1:    workplaceConfig.WorkPlaceNr,
					IntCtrl2:    workplaceConfig.ConfigNr,
				}
				innerErr := plcErrorDao.InsertOne(&errorData)
				if innerErr != nil {
					return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
				}
			}
			return grmi.NewBusinessError(errorInfo)
		}
		existData := existDataInterface.(string)
		if existData != "" {
			errorInfo := "地址位数据未清空"
			if recordErr {
				errorData := model.PackOrderPlcError{
					PackOrderId: packOrderId,
					Status:      model.PACK_STATUS_PLANNED,
					Step:        1,
					ErrorInfo:   errorInfo,
					ErrorType:   model.ERROR_TYTPE_PLC,
					Location:    model.PACK_LOCATION_ON_LINE,
					Source:      "BZ-" + quality,
					IntCtrl1:    workplaceConfig.WorkPlaceNr,
					IntCtrl2:    workplaceConfig.ConfigNr,
				}
				innerErr := plcErrorDao.InsertOne(&errorData)
				if innerErr != nil {
					return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
				}
			}
			return grmi.NewBusinessError(errorInfo)
		}
		data = baseModel.PLC{
			PLCType:      workplaceConfig.StrValue2,
			PLCAddress:   workplaceConfig.StrValue3,
			ValueAddress: workplaceConfig.StrValue4,
			ValueType:    "String",
			ValueText:    packOrderId,
			RetryCount:   workplaceConfig.IntValue2,
			ByteCount:    workplaceConfig.IntValue1,
		}
		err = plc.WriteDataToPLC(data, log)
		if err != nil {
			errorInfo := "写入包装单条码到plc失败， " + err.Error()
			if recordErr {
				errorData := model.PackOrderPlcError{
					PackOrderId: packOrderId,
					Status:      model.PACK_STATUS_PLANNED,
					Step:        1,
					ErrorInfo:   errorInfo,
					ErrorType:   model.ERROR_TYTPE_PLC,
					Location:    model.PACK_LOCATION_ON_LINE,
					Source:      "BZ-" + quality,
					IntCtrl1:    workplaceConfig.WorkPlaceNr,
					IntCtrl2:    workplaceConfig.ConfigNr,
				}
				innerErr := plcErrorDao.InsertOne(&errorData)
				if innerErr != nil {
					return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
				}
			}
			return grmi.NewBusinessError(errorInfo)
		}
		fallthrough
	case 2:
		if quality == model.PACK_QUALITY_NG {
			data = baseModel.PLC{
				PLCType:      workplaceConfig.StrValue2,
				PLCAddress:   workplaceConfig.StrValue3,
				ValueAddress: workplaceConfig.StrValue5,
				ValueType:    "Boolean",
				RetryCount:   workplaceConfig.IntValue2,
			}
			existDataInterface, err := plc.ReadDataFromPLC(data, log, "Boolean")
			if err != nil {
				errorInfo := "读取plc数据失败, " + err.Error()
				if recordErr {
					errorData := model.PackOrderPlcError{
						PackOrderId: packOrderId,
						Status:      model.PACK_STATUS_PLANNED,
						Step:        1,
						ErrorInfo:   errorInfo,
						ErrorType:   model.ERROR_TYTPE_PLC,
						Location:    model.PACK_LOCATION_ON_LINE,
						Source:      "BZ-" + quality,
						IntCtrl1:    workplaceConfig.WorkPlaceNr,
						IntCtrl2:    workplaceConfig.ConfigNr,
					}
					innerErr := plcErrorDao.InsertOne(&errorData)
					if innerErr != nil {
						return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
					}
				}
				return grmi.NewBusinessError(errorInfo)
			}
			existBool := existDataInterface.(bool)
			if existBool {
				errorInfo := "地址位数据非0"
				if recordErr {
					errorData := model.PackOrderPlcError{
						PackOrderId: packOrderId,
						Status:      model.PACK_STATUS_PLANNED,
						Step:        1,
						ErrorInfo:   errorInfo,
						ErrorType:   model.ERROR_TYTPE_PLC,
						Location:    model.PACK_LOCATION_ON_LINE,
						Source:      "BZ-" + quality,
						IntCtrl1:    workplaceConfig.WorkPlaceNr,
						IntCtrl2:    workplaceConfig.ConfigNr,
					}
					innerErr := plcErrorDao.InsertOne(&errorData)
					if innerErr != nil {
						return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
					}
				}
				return grmi.NewBusinessError(errorInfo)
			}
			data = baseModel.PLC{
				PLCType:      workplaceConfig.StrValue2,
				PLCAddress:   workplaceConfig.StrValue3,
				ValueAddress: workplaceConfig.StrValue5,
				ValueType:    "Boolean",
				ValueText:    true,
				RetryCount:   workplaceConfig.IntValue2,
			}
			err = plc.WriteDataToPLC(data, log)
			if err != nil {
				errorInfo := "写入标识位到plc失败， " + err.Error()
				if recordErr {
					errorData := model.PackOrderPlcError{
						PackOrderId: packOrderId,
						Status:      model.PACK_STATUS_PLANNED,
						Step:        1,
						ErrorInfo:   errorInfo,
						ErrorType:   model.ERROR_TYTPE_PLC,
						Location:    model.PACK_LOCATION_ON_LINE,
						Source:      "BZ-" + quality,
						IntCtrl1:    workplaceConfig.WorkPlaceNr,
						IntCtrl2:    workplaceConfig.ConfigNr,
					}
					innerErr := plcErrorDao.InsertOne(&errorData)
					if innerErr != nil {
						return grmi.NewBusinessError(err.Error() + ", 保存错误失败, error:" + errorInfo)
					}
				}
				return grmi.NewBusinessError(errorInfo)
			}
		}
	}
	return nil
}


func (impl *PackOrderServiceImplement) BZPackPLCWithoutCheck(user *global.User, workplaceConfig *baseModel.WorkPlaceConfig, packOrderId string, quality string, recordErr bool, step int) error {
	engine := db.Eloquent.Master()
	session := engine.NewSession()
	defer session.Close()
	log, _ := logger.NewLogger("缤智线下线打包", "JIT")
	var (
		data baseModel.PLC
		err error
	)
	switch step {
	case 0, 1:

		data = baseModel.PLC{
			PLCType:      workplaceConfig.StrValue2,
			PLCAddress:   workplaceConfig.StrValue3,
			ValueAddress: workplaceConfig.StrValue4,
			ValueType:    "String",
			ValueText:    packOrderId,
			RetryCount:   workplaceConfig.IntValue2,
			ByteCount:    workplaceConfig.IntValue1,
		}
		err = plc.WriteDataToPLC(data, log)
		if err != nil {
			errorInfo := "写入包装单条码到plc失败， " + err.Error()
			return grmi.NewBusinessError(errorInfo)
		}
		fallthrough
	case 2:
		if quality == model.PACK_QUALITY_NG {
			data = baseModel.PLC{
				PLCType:      workplaceConfig.StrValue2,
				PLCAddress:   workplaceConfig.StrValue3,
				ValueAddress: workplaceConfig.StrValue5,
				ValueType:    "Boolean",
				ValueText:    true,
				RetryCount:   workplaceConfig.IntValue2,
			}
			err = plc.WriteDataToPLC(data, log)
			if err != nil {
				errorInfo := "写入标识位到plc失败， " + err.Error()
				return grmi.NewBusinessError(errorInfo)
			}
		}
	}
	return nil
}

