package test_data import ( "bufio" "encoding/json" "github.com/shopspring/decimal" "github.com/spf13/viper" "github.com/xuri/excelize/v2" "log" "math" "os" "path/filepath" "sort" "strconv" "strings" "testData/global" "testData/model" "testData/request" "testData/utils" "time" ) func ExportCPMap(req *request.CPMap) string { // CP测试没有子批次 var fileHandles []*model.FileHandled global.PostGreSQL.Where("pbi = ? AND product = ? AND lot = ?", req.PBI, req.Product, req.Lot). Order("wafer_id").Find(&fileHandles) cpFile := excelize.NewFile() passStyle := CPMapPassStyle(cpFile) failStyle := CPMapFailStyle(cpFile) _ = cpFile.SetSheetName("Sheet1", "Summary") allBinMap := make(map[string]int) for _, fileHandle := range fileHandles { _, err := strconv.Atoi(fileHandle.WaferID) if err != nil { continue } f, err := os.Open(fileHandle.Path) if err != nil { log.Println(err) } scanner := bufio.NewScanner(f) datas := make([][]string, 0) fieldMap := make(map[string]int) for scanner.Scan() { line := scanner.Text() s := strings.Split(line, ",") datas = append(datas, s[:len(s)-1]) if len(datas) == 1 { for index, cell := range s { if index == len(s)-1 { continue } fieldMap[cell] = index } } } if len(datas) >= 1 { datas = datas[1:] } for _, data := range datas { allBinMap[data[fieldMap["SOFT_BIN"]]] = 1 } f.Close() } var binStrs []string for k := range allBinMap { binStrs = append(binStrs, "BIN "+k) } sort.Slice(binStrs, func(i, j int) bool { iInt, _ := strconv.Atoi(binStrs[i][4:]) jInt, _ := strconv.Atoi(binStrs[j][4:]) return iInt < jInt }) var title []string title = append(title, []string{"产品", "工序", "PBI", "批号", "片号", "Total", "Pass", "Fail", "Yield"}...) rowIndex := 1 title = append(title, binStrs...) indexMap := make(map[string]int) for index, field := range title { indexMap[field] = index + 1 } _ = cpFile.SetSheetRow("Summary", "A1", &title) maxProbability, minProbability, maxRowIndex, minRowIndex := "-1", "-1", -1, -1 for _, fileHandle := range fileHandles { _, err := strconv.Atoi(fileHandle.WaferID) if err != nil { continue } rowIndex++ f, err := os.Open(fileHandle.Path) if err != nil { log.Println(err) } scanner := bufio.NewScanner(f) datas := make([][]string, 0) fieldMap := make(map[string]int) for scanner.Scan() { line := scanner.Text() s := strings.Split(line, ",") datas = append(datas, s[:len(s)-1]) if len(datas) == 1 { for index, cell := range s { if index == len(s)-1 { continue } fieldMap[cell] = index } } } if len(datas) >= 1 { datas = datas[1:] } //sbinHbinMap := make(map[string]map[string]string) sbinHbinMap := make(map[string]string) sbinHbinByte := []byte(fileHandle.SbinHbin) _ = json.Unmarshal(sbinHbinByte, &sbinHbinMap) var rowData []interface{} var latestDatas [][]string endRow, endCol := 0, 0 binMap := make(map[string]int) sheetIndex, _ := cpFile.NewSheet(fileHandle.WaferID) sheetName := cpFile.GetSheetName(sheetIndex) for _, data := range datas { //binInfoMap := sbinHbinMap[data[fieldMap["SOFT_BIN"]]] x := data[fieldMap["X_COORD"]] y := data[fieldMap["Y_COORD"]] xInt, _ := strconv.Atoi(x) yInt, _ := strconv.Atoi(y) if endRow < xInt { endRow = xInt } if endCol < yInt { endCol = yInt } //xDecimal, _ := decimal.NewFromString(x) //yDecimal, _ := decimal.NewFromString(y) //endRowDecimal, _ := decimal.NewFromString(endRow) //endColDecimal, _ := decimal.NewFromString(endCol) //if endRowDecimal.LessThan(xDecimal) { // endRow = x //} //if endColDecimal.LessThan(yDecimal) { // endCol = y //} isAppendData := true for _, mainData := range latestDatas { if mainData[0] == x && mainData[1] == y { mainData = []string{x, y, data[fieldMap["SOFT_BIN"]], sbinHbinMap[data[fieldMap["SOFT_BIN"]]]} //mainData = []string{x, y, data[fieldMap["SOFT_BIN"]], binInfoMap["HBin"]} isAppendData = false break } } if isAppendData { latestDatas = append(latestDatas, []string{x, y, data[fieldMap["SOFT_BIN"]], sbinHbinMap[data[fieldMap["SOFT_BIN"]]]}) //field[5] //data[fieldMap["SOFT_BIN"]], binInfoMap["HBin"]}) //field[5] } } for _, mainData := range latestDatas { binMap[mainData[2]]++ x := utils.NumToRow(mainData[0]) y := mainData[1] if mainData[2] == "1" { _ = cpFile.SetCellValue(sheetName, x+y, 1) _ = cpFile.SetCellStyle(sheetName, x+y, x+y, passStyle) } else { _ = cpFile.SetCellValue(sheetName, x+y, mainData[2]+":"+mainData[3]) _ = cpFile.SetCellStyle(sheetName, x+y, x+y, failStyle) } } _ = cpFile.SetColWidth(sheetName, utils.NumToRow("1"), utils.NumToRow(strconv.Itoa(endRow)), 2) //endColIndex, _ := strconv.Atoi(endCol) //for i := 0; i < endColIndex; i++ { // _ = cpFile.SetRowHeight(sheetName, i, 10) //} for i := 0; i < endCol; i++ { _ = cpFile.SetRowHeight(sheetName, i, 10) } totalDecimal := decimal.NewFromFloat(float64(len(latestDatas))) passDecimal := decimal.NewFromFloat(float64(binMap["1"])) maxProbabilityDecimal, _ := decimal.NewFromString(maxProbability) minProbabilityDecimal, _ := decimal.NewFromString(minProbability) var passProbability string if totalDecimal.IsZero() { passProbability = "0" minProbability = passProbability minRowIndex = rowIndex if maxProbability == "-1" { maxProbability = passProbability maxRowIndex = rowIndex } } else { passProbability = passDecimal.Div(totalDecimal).Round(4).Mul(decimal.NewFromInt(100)).String() if maxProbability == "-1" { maxProbability = passProbability maxRowIndex = rowIndex } else { if maxProbabilityDecimal.LessThan(passDecimal.Div(totalDecimal).Round(4).Mul(decimal.NewFromInt(100))) { maxProbability = passProbability maxRowIndex = rowIndex } } if minProbability == "-1" { minProbability = passProbability minRowIndex = rowIndex } else { if passDecimal.Div(totalDecimal).Round(4).Mul(decimal.NewFromInt(100)).LessThan(minProbabilityDecimal) { minProbability = passProbability minRowIndex = rowIndex } } } total, _ := strconv.Atoi(totalDecimal.String()) pass, _ := strconv.Atoi(passDecimal.String()) fail, _ := strconv.Atoi(totalDecimal.Sub(passDecimal).String()) rowData = []interface{}{fileHandle.Product, fileHandle.Step, fileHandle.PBI, fileHandle.Lot, fileHandle.WaferID, total, pass, fail, passProbability + "%"} for _, bin := range binStrs { if quantity, ok := binMap[bin[4:]]; !ok { rowData = append(rowData, "") } else { rowData = append(rowData, quantity) } } _ = cpFile.SetSheetRow("Summary", "A"+strconv.Itoa(rowIndex), &rowData) f.Close() } // Total _ = cpFile.SetSheetCol("Summary", "D"+strconv.Itoa(rowIndex+1), &[]interface{}{"Total", "Max Yield", "Min Yield", "Average Yield"}) _ = cpFile.SetCellValue("Summary", "E"+strconv.Itoa(rowIndex+1), rowIndex-1) _ = cpFile.SetCellFormula("Summary", "F"+strconv.Itoa(rowIndex+1), "=SUM(F2:F"+strconv.Itoa(rowIndex)+")") _ = cpFile.SetCellFormula("Summary", "G"+strconv.Itoa(rowIndex+1), "=SUM(G2:G"+strconv.Itoa(rowIndex)+")") _ = cpFile.SetCellFormula("Summary", "H"+strconv.Itoa(rowIndex+1), "=SUM(H2:H"+strconv.Itoa(rowIndex)+")") // Max Yield _ = cpFile.SetCellFormula("Summary", "E"+strconv.Itoa(rowIndex+2), "=E"+strconv.Itoa(maxRowIndex)) _ = cpFile.SetCellFormula("Summary", "F"+strconv.Itoa(rowIndex+2), "=F"+strconv.Itoa(maxRowIndex)) _ = cpFile.SetCellFormula("Summary", "G"+strconv.Itoa(rowIndex+2), "=G"+strconv.Itoa(maxRowIndex)) _ = cpFile.SetCellFormula("Summary", "H"+strconv.Itoa(rowIndex+2), "=H"+strconv.Itoa(maxRowIndex)) _ = cpFile.SetCellFormula("Summary", "I"+strconv.Itoa(rowIndex+2), "=I"+strconv.Itoa(maxRowIndex)) // Min Yield _ = cpFile.SetCellFormula("Summary", "E"+strconv.Itoa(rowIndex+3), "=E"+strconv.Itoa(minRowIndex)) _ = cpFile.SetCellFormula("Summary", "F"+strconv.Itoa(rowIndex+3), "=F"+strconv.Itoa(minRowIndex)) _ = cpFile.SetCellFormula("Summary", "G"+strconv.Itoa(rowIndex+3), "=G"+strconv.Itoa(minRowIndex)) _ = cpFile.SetCellFormula("Summary", "H"+strconv.Itoa(rowIndex+3), "=H"+strconv.Itoa(minRowIndex)) _ = cpFile.SetCellFormula("Summary", "I"+strconv.Itoa(rowIndex+3), "=I"+strconv.Itoa(minRowIndex)) // Average Yield percentStyle, _ := cpFile.NewStyle(&excelize.Style{ NumFmt: 10, }) _ = cpFile.SetCellFormula("Summary", "I"+strconv.Itoa(rowIndex+4), "=G"+strconv.Itoa(rowIndex+1)+"/F"+strconv.Itoa(rowIndex+1)) _ = cpFile.SetCellStyle("Summary", "F"+strconv.Itoa(rowIndex+4), "F"+strconv.Itoa(rowIndex+4), percentStyle) for binIndex, bin := range binStrs { colName := utils.NumToRow(strconv.Itoa(indexMap[bin])) // Total _ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+1), "=SUM("+colName+"2:"+colName+strconv.Itoa(rowIndex)+")") // Max Yield _ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+2), "="+colName+strconv.Itoa(maxRowIndex)) // Min Yield _ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+3), "="+colName+strconv.Itoa(minRowIndex)) // Average Yield _ = cpFile.SetCellFormula("Summary", colName+strconv.Itoa(rowIndex+4), "="+colName+strconv.Itoa(rowIndex+1)+"/F"+strconv.Itoa(rowIndex+1)) if binIndex == len(binStrs)-1 { _ = cpFile.SetCellStyle("Summary", "I"+strconv.Itoa(rowIndex+4), colName+strconv.Itoa(rowIndex+4), percentStyle) } } _ = cpFile.SetColWidth("Summary", "A", "A", 15) _ = cpFile.SetColWidth("Summary", "C", "C", 30) _ = cpFile.SetColWidth("Summary", "D", "D", 20) filePath := time.Now().Format("_CpMap_2006-01-02_150405.xlsx") filePath = req.Product + "_" + req.Lot + filePath filePath = filepath.Join(utils.MakeSavePath("CP"), filePath) _ = cpFile.SaveAs(filePath) defer cpFile.Close() return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath } type SelectionInfo struct { Field string Min float64 Max float64 Average float64 StandardDeviation float64 Num float64 } func ExportHistogram(req *request.Histogram) string { var fileHandle *model.FileHandled global.PostGreSQL.Where("product = ? AND lot = ? AND pbi = ?", req.Product, req.Lot, req.PBI).Find(&fileHandle) histogramFile := excelize.NewFile() row := 1 m := make(map[string]map[string]string) _ = json.Unmarshal([]byte(fileHandle.TitleInfo), &m) sheetName := histogramFile.GetSheetName(0) _ = histogramFile.SetSheetName(sheetName, "汇总表") sheetName = "汇总表" if ((len(req.SubBatch) == 0 && len(req.WaferID) == 0) || len(req.SubBatch) != 0 || len(req.WaferID) != 0) && !req.SpliteSubBatch { start := row selectionInfo := make(map[string][]float64) var datas [][]string fieldMap := make(map[string]int) if len(req.SubBatch) == 0 && len(req.WaferID) == 0 { datas, fieldMap = LotInMultipleFiles(req.Product, req.Lot, req.PBI, req.Step) } else { if len(req.SubBatch) != 0 { for _, subBatch := range req.SubBatch { subBatchDatas, subBatchFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, subBatch, "", req.Step) datas = append(datas, subBatchDatas...) fieldMap = subBatchFieldMap } } else if len(req.WaferID) != 0 { for _, waferID := range req.WaferID { waferIDDatas, waferIDFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, "", waferID, req.Step) datas = append(datas, waferIDDatas...) fieldMap = waferIDFieldMap } } } for _, data := range datas { if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") { continue } for _, selection := range req.Selections { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[selection]]) selectionFloat, _ := selectionDecimal.Float64() var filed string if req.SpliteSite { filed = selection + "_Site" + data[fieldMap["SITE_NUM"]] } else { filed = selection } if _, ok := selectionInfo[filed]; !ok { selectionInfo[filed] = []float64{math.MaxInt, math.MinInt, 0, 0} } if selectionInfo[filed][0] > selectionFloat { selectionInfo[filed][0] = selectionFloat } if selectionInfo[filed][1] < selectionFloat { selectionInfo[filed][1] = selectionFloat } selectionInfo[filed][2] += selectionFloat selectionInfo[filed][3]++ } } selectionHistogramX := make(map[string][]string) selectionHistogramY := make(map[string][]int) for k, v := range selectionInfo { selectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0} selectionHistogramX[k] = []string{} minDecimal := decimal.NewFromFloat(v[0]) if minDecimal.Abs().LessThan(decimal.NewFromInt(1)) { if minDecimal.LessThan(decimal.NewFromInt(0)) { minDecimal = minDecimal.RoundUp(0) } else { minDecimal = minDecimal.RoundDown(0) } } else { if minDecimal.LessThan(decimal.NewFromInt(0)) { minDecimal = minDecimal.RoundDown(1) } else { minDecimal = minDecimal.RoundUp(1) } } maxDecimal := decimal.NewFromFloat(v[1]) if maxDecimal.Abs().LessThan(decimal.NewFromInt(1)) { if maxDecimal.LessThan(decimal.NewFromInt(0)) { maxDecimal = maxDecimal.RoundDown(0) } else { maxDecimal = maxDecimal.RoundUp(0) } } else { if maxDecimal.LessThan(decimal.NewFromInt(0)) { maxDecimal = maxDecimal.RoundDown(1) } else { maxDecimal = maxDecimal.RoundUp(1) } } interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20)) for i := 0; i < 21; i++ { xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String() selectionHistogramX[k] = append(selectionHistogramX[k], xPoint) } } for _, data := range datas { if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") { continue } for k, v := range selectionInfo { var field, site string if req.SpliteSite { field = strings.Split(k, "_Site")[0] site = strings.Split(k, "_Site")[1] if !(site == data[fieldMap["SITE_NUM"]]) { continue } } else { field = k } if data[fieldMap[field]] != "" { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[field]]) averageDecimal := decimal.NewFromFloat(v[4]) d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)). Float64() for i := 0; i < len(selectionHistogramX[k]); i++ { if i == len(selectionHistogramX[k])-1 { xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[k][i]) if xPointDecimal.Equals(selectionDecimal) { selectionHistogramY[k][i]++ } break } xPointMin, _ := decimal.NewFromString(selectionHistogramX[k][i]) xPointMax, _ := decimal.NewFromString(selectionHistogramX[k][i+1]) if _, ok := selectionHistogramY[k]; !ok { selectionHistogramY[k] = make([]int, len(selectionHistogramX[k])) } if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) { selectionHistogramY[k][i]++ } } v[5] += d v[6]++ } } } rowIndex := 2 histogramFile.NewSheet("All") selectionValueMap := make(map[string][]int) for k, v := range selectionHistogramX { sheetX := []interface{}{k} for _, x := range v { sheetX = append(sheetX, x) } sheetY := []interface{}{"数量"} for _, y := range selectionHistogramY[k] { sheetY = append(sheetY, y) } for idx, rows := range [][]interface{}{ sheetX, sheetY, } { cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx) if err != nil { log.Println(err) } if err = histogramFile.SetSheetRow("All", cell, &rows); err != nil { log.Println(err) } } selectionValueMap[k] = []int{rowIndex, len(selectionHistogramY[k]) + 1} rowIndex += 2 } a := 1 for _, selection := range req.Selections { b := 1 for k, v := range selectionValueMap { if strings.Contains(k, selection) { if err := histogramFile.AddChart("All", utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{ Type: excelize.Col, Series: []excelize.ChartSeries{ { Name: "All" + "!$A$1", Categories: "All" + "!$B$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: "All" + "!$B$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "none", Size: 10, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "left", }, Title: []excelize.RichTextRun{ { Text: k, }, }, PlotArea: excelize.ChartPlotArea{ ShowCatName: false, ShowLeaderLines: false, ShowVal: true, }, ShowBlanksAs: "zero", }, &excelize.Chart{ Type: excelize.Line, Series: []excelize.ChartSeries{ { Name: "All" + "!$A$1", Categories: "All" + "!$B$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: "All" + "!$B$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "none", Size: 10, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "right", }, PlotArea: excelize.ChartPlotArea{ ShowCatName: false, ShowLeaderLines: false, }, }); err != nil { log.Println(err) } b += 9 } } a += 20 } col := 1 _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ "", "最小值", "最大值", "平均值", "δ", "数量", "Limit L", "Limit H", "UNIT"}) col++ siteDiff := make(map[string][]float64) selectionUnit := make(map[string]string) var selectionInfoArray []SelectionInfo for _, selection := range req.Selections { var oneSelectionInfo []SelectionInfo for k, v := range selectionInfo { if !strings.Contains(k, selection) { continue } oneSelectionInfo = append(oneSelectionInfo, SelectionInfo{ Field: k, Min: v[0], Max: v[1], Average: v[4], StandardDeviation: math.Sqrt(v[5] / v[6]), Num: v[3], }) } sort.Slice(oneSelectionInfo, func(i, j int) bool { return oneSelectionInfo[i].Field < oneSelectionInfo[j].Field }) selectionInfoArray = append(selectionInfoArray, oneSelectionInfo...) } for _, v := range selectionInfoArray { field := strings.Split(v.Field, "_Site")[0] limitLDecimal, _ := decimal.NewFromString(m[field]["LimitL"]) limitLFloat, _ := limitLDecimal.Float64() limitUDecimal, _ := decimal.NewFromString(m[field]["LimitU"]) limitUFloat, _ := limitUDecimal.Float64() if req.SpliteSite { if _, ok := siteDiff[field]; !ok { siteDiff[field] = []float64{v.Average, v.Average, v.StandardDeviation, v.StandardDeviation} selectionUnit[field] = m[field]["Unit"] } else { if v.Average > siteDiff[field][0] { siteDiff[field][0] = v.Average } if v.Average < siteDiff[field][1] { siteDiff[field][1] = v.Average } if v.StandardDeviation > siteDiff[field][2] { siteDiff[field][2] = v.StandardDeviation } if v.StandardDeviation < siteDiff[field][3] { siteDiff[field][3] = v.StandardDeviation } } } _ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start), &[]interface{}{ v.Field, v.Min, v.Max, v.Average, v.StandardDeviation, v.Num, limitLFloat, limitUFloat, m[field]["Unit"]}) col++ } _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), TitleStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+5), ActualLimitStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+6), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+8), LimitStyle(histogramFile)) _ = histogramFile.SetColWidth(sheetName, "A", utils.NumToRow(strconv.Itoa(col-1)), 20) if req.SpliteSite { start += 10 col = 2 _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ "SITE差异"}) _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "", "平均值", "δ", "UNIT"}) for k, v := range siteDiff { _ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ k, v[0] - v[1], v[2] - v[3], selectionUnit[k]}) col++ } _ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), NormalSheetStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1), TitleStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3), ActualLimitStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4), LimitStyle(histogramFile)) } } else { start := row selectionUnit := make(map[string]string) subBatchDiff := make(map[string][]float64) var groups []string var isFT bool if len(req.SubBatch) != 0 { groups = req.SubBatch isFT = true } else if len(req.WaferID) != 0 { groups = req.WaferID } else if len(req.SubBatch) == 0 && len(req.WaferID) == 0 { global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?", req.Product, req.Lot, req.PBI).Group("wafer_id").Select("wafer_id").Find(&groups) if len(groups) == 0 || groups[0] == "" { global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?", req.Product, req.Lot, req.PBI).Group("sub_batch").Select("sub_batch").Find(&groups) isFT = true } } for _, group := range groups { siteSelectionInfo := make(map[string][]float64) selectionInfo := make(map[string][]float64) var datas [][]string fieldMap := make(map[string]int) if isFT { datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, group, "", req.Step) } else { datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, "", group, req.Step) } group = strings.ReplaceAll(group, "-", "_") for _, data := range datas { if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") { continue } for _, selection := range req.Selections { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[selection]]) selectionFloat, _ := selectionDecimal.Float64() var filed string if req.SpliteSite { filed = selection + "_Site" + data[fieldMap["SITE_NUM"]] } else { filed = selection } if _, ok := siteSelectionInfo[filed]; !ok { siteSelectionInfo[filed] = []float64{math.MaxInt, math.MinInt, 0, 0} } if siteSelectionInfo[filed][0] > selectionFloat { siteSelectionInfo[filed][0] = selectionFloat } if siteSelectionInfo[filed][1] < selectionFloat { siteSelectionInfo[filed][1] = selectionFloat } siteSelectionInfo[filed][2] += selectionFloat siteSelectionInfo[filed][3]++ if _, ok := selectionInfo[selection]; !ok { selectionInfo[selection] = []float64{math.MaxInt, math.MinInt, 0, 0} } if selectionInfo[selection][0] > selectionFloat { selectionInfo[selection][0] = selectionFloat } if selectionInfo[selection][1] < selectionFloat { selectionInfo[selection][1] = selectionFloat } selectionInfo[selection][2] += selectionFloat selectionInfo[selection][3]++ } } selectionHistogramX := make(map[string][]string) selectionHistogramY := make(map[string][]int) for k, v := range siteSelectionInfo { siteSelectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0} if req.SpliteSite { selectionHistogramX[group+"_"+k] = []string{} minDecimal := decimal.NewFromFloat(v[0]) if minDecimal.LessThan(decimal.NewFromInt(0)) { minDecimal = minDecimal.RoundUp(0) } else { minDecimal = minDecimal.RoundDown(0) } maxDecimal := decimal.NewFromFloat(v[1]) if maxDecimal.LessThan(decimal.NewFromInt(0)) { maxDecimal = maxDecimal.RoundDown(0) } else { maxDecimal = maxDecimal.RoundUp(0) } interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20)) for i := 0; i < 21; i++ { xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String() selectionHistogramX[group+"_"+k] = append(selectionHistogramX[group+"_"+k], xPoint) } } } for k, v := range selectionInfo { selectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0} if !req.SpliteSite { selectionHistogramX[group+"_"+k] = []string{} minDecimal := decimal.NewFromFloat(v[0]) if minDecimal.LessThan(decimal.NewFromInt(0)) { minDecimal = minDecimal.RoundUp(0) } else { minDecimal = minDecimal.RoundDown(0) } maxDecimal := decimal.NewFromFloat(v[1]) if maxDecimal.LessThan(decimal.NewFromInt(0)) { maxDecimal = maxDecimal.RoundDown(0) } else { maxDecimal = maxDecimal.RoundUp(0) } interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20)) for i := 0; i < 21; i++ { xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String() selectionHistogramX[group+"_"+k] = append(selectionHistogramX[group+"_"+k], xPoint) } } } for _, data := range datas { if req.OnlyPass && !(data[fieldMap["SOFT_BIN"]] == "1") { continue } for k, v := range siteSelectionInfo { var field, site string if req.SpliteSite { field = strings.Split(k, "_Site")[0] site = strings.Split(k, "_Site")[1] if !(site == data[fieldMap["SITE_NUM"]]) { continue } } else { field = k } if data[fieldMap[field]] != "" { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[field]]) averageDecimal := decimal.NewFromFloat(v[4]) d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)). Float64() if req.SpliteSite { a := group + "_" + k for i := 0; i < len(selectionHistogramX[a]); i++ { if i == len(selectionHistogramX[a])-1 { xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[a][i]) if xPointDecimal.Equals(selectionDecimal) { selectionHistogramY[a][i]++ } break } xPointMin, _ := decimal.NewFromString(selectionHistogramX[a][i]) xPointMax, _ := decimal.NewFromString(selectionHistogramX[a][i+1]) if _, ok := selectionHistogramY[a]; !ok { selectionHistogramY[a] = make([]int, len(selectionHistogramX[a])) } if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) { selectionHistogramY[a][i]++ } } } v[5] += d v[6]++ } } for k, v := range selectionInfo { if data[fieldMap[k]] != "" { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[k]]) averageDecimal := decimal.NewFromFloat(v[4]) d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)). Float64() if !req.SpliteSite { field := group + "_" + k for i := 0; i < len(selectionHistogramX[field]); i++ { if i == len(selectionHistogramX[field])-1 { xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[field][i]) if xPointDecimal.Equals(selectionDecimal) { selectionHistogramY[field][i]++ } break } xPointMin, _ := decimal.NewFromString(selectionHistogramX[field][i]) xPointMax, _ := decimal.NewFromString(selectionHistogramX[field][i+1]) if _, ok := selectionHistogramY[field]; !ok { selectionHistogramY[field] = make([]int, len(selectionHistogramX[field])) } if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) { selectionHistogramY[field][i]++ } } } v[5] += d v[6]++ } } } rowIndex, colIndex := 2, 1 histogramFile.NewSheet(group) selectionValueMap := make(map[string][]int) for k, v := range selectionHistogramX { sheetX := []interface{}{k} for _, x := range v { sheetX = append(sheetX, x) } sheetY := []interface{}{"数量"} for _, y := range selectionHistogramY[k] { sheetY = append(sheetY, y) } for idx, rows := range [][]interface{}{ sheetX, sheetY, } { cell, err := excelize.CoordinatesToCellName(colIndex, rowIndex+idx) if err != nil { log.Println(err) } if err = histogramFile.SetSheetRow(group, cell, &rows); err != nil { log.Println(err) } } selectionValueMap[k] = []int{rowIndex, len(selectionHistogramY[k]) + 1} rowIndex += 2 } a := 1 for _, selection := range req.Selections { b := 1 for k, v := range selectionValueMap { if strings.Contains(k, selection) { if err := histogramFile.AddChart(group, utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{ Type: excelize.Col, Series: []excelize.ChartSeries{ { Name: group + "!$A$1", Categories: group + "!$B$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: group + "!$B$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "none", Size: 10, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "left", }, Title: []excelize.RichTextRun{ { Text: k, }, }, PlotArea: excelize.ChartPlotArea{ ShowCatName: false, ShowLeaderLines: false, ShowVal: true, }, ShowBlanksAs: "zero", }, &excelize.Chart{ Type: excelize.Line, Series: []excelize.ChartSeries{ { Name: group + "!$A$1", Categories: group + "!$B$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: group + "!$B$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "none", Size: 10, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "right", }, PlotArea: excelize.ChartPlotArea{ ShowCatName: false, ShowLeaderLines: false, }, }); err != nil { log.Println(err) } b += 9 } } a += 20 } col := 1 _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ "子批:" + group}) _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "", "最小值", "最大值", "平均值", "δ", "数量", "Limit L", "Limit H", "UNIT"}) col++ siteDiff := make(map[string][]float64) var selectionInfoArray []SelectionInfo for _, selection := range req.Selections { var oneSelectionInfo []SelectionInfo for k, v := range siteSelectionInfo { if !strings.Contains(k, selection) { continue } oneSelectionInfo = append(oneSelectionInfo, SelectionInfo{ Field: k, Min: v[0], Max: v[1], Average: v[4], StandardDeviation: math.Sqrt(v[5] / v[6]), Num: v[3], }) } sort.Slice(oneSelectionInfo, func(i, j int) bool { return oneSelectionInfo[i].Field < oneSelectionInfo[j].Field }) selectionInfoArray = append(selectionInfoArray, oneSelectionInfo...) } for _, v := range selectionInfoArray { field := strings.Split(v.Field, "_Site")[0] limitLDecimal, _ := decimal.NewFromString(m[field]["LimitL"]) limitLFloat, _ := limitLDecimal.Float64() limitUDecimal, _ := decimal.NewFromString(m[field]["LimitU"]) limitUFloat, _ := limitUDecimal.Float64() selectionUnit[field] = m[field]["Unit"] if req.SpliteSite { if _, ok := siteDiff[field]; !ok { siteDiff[field] = []float64{v.Average, v.Average, v.StandardDeviation, v.StandardDeviation} } else { if v.Average > siteDiff[field][0] { siteDiff[field][0] = v.Average } if v.Average < siteDiff[field][1] { siteDiff[field][1] = v.Average } if v.StandardDeviation > siteDiff[field][2] { siteDiff[field][2] = v.StandardDeviation } if v.StandardDeviation < siteDiff[field][3] { siteDiff[field][3] = v.StandardDeviation } } } _ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ v.Field, v.Min, v.Max, v.Average, v.StandardDeviation, v.Num, limitLFloat, limitUFloat, m[field]["Unit"]}) col++ } for k, v := range selectionInfo { if _, ok := subBatchDiff[k]; !ok { subBatchDiff[k] = []float64{v[4], v[4], v[5] / v[6], v[5] / v[6]} selectionUnit[k] = m[k]["Unit"] } else { if v[4] > subBatchDiff[k][0] { subBatchDiff[k][0] = v[4] } if v[4] < subBatchDiff[k][1] { subBatchDiff[k][1] = v[4] } if v[5]/v[6] > subBatchDiff[k][2] { subBatchDiff[k][2] = v[5] / v[6] } if v[5]/v[6] < subBatchDiff[k][3] { subBatchDiff[k][3] = v[5] / v[6] } } } _ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), NormalSheetStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1), TitleStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+6), ActualLimitStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+7), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+9), LimitStyle(histogramFile)) _ = histogramFile.SetColWidth(sheetName, "A", utils.NumToRow(strconv.Itoa(col-1)), 20) start += 10 if req.SpliteSite { start += 1 col = 2 _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ "SITE差异-" + group}) _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "", "平均值", "δ", "UNIT"}) for k, v := range siteDiff { _ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ k, v[0] - v[1], v[2] - v[3], selectionUnit[k]}) col++ } _ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), NormalSheetStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1), TitleStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3), ActualLimitStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4), LimitStyle(histogramFile)) start += 5 } start += 1 } start += 1 col := 2 _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ "批次差异汇总"}) _ = histogramFile.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "", "平均值", "δ", "UNIT"}) if len(req.SubBatch) > 1 { for _, selection := range req.Selections { _ = histogramFile.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ selection, subBatchDiff[selection][0] - subBatchDiff[selection][1], subBatchDiff[selection][2] - subBatchDiff[selection][3], selectionUnit[selection]}) col++ } } _ = histogramFile.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), NormalSheetStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1), TitleStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3), ActualLimitStyle(histogramFile)) _ = histogramFile.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4), LimitStyle(histogramFile)) } filePath := time.Now().Format("_直方图_2006-01-02_150405.xlsx") filePath = req.Product + "_" + req.Lot + filePath filePath = filepath.Join(utils.MakeSavePath("直方图"), filePath) _ = histogramFile.SaveAs(filePath) defer histogramFile.Close() return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath } func ExportScatter(req *request.Scatter) string { scatterFile := excelize.NewFile() //((len(req.SubBatch) == 0 && len(req.WaferID) == 0) || len(req.SubBatch) != 0 || len(req.WaferID) != 0) && if !req.SpliteSubBatch { var datas [][]string fieldMap := make(map[string]int) if len(req.SubBatch) == 0 && len(req.WaferID) == 0 { datas, fieldMap = LotInMultipleFiles(req.Product, req.Lot, req.PBI, req.Step) } else { if len(req.SubBatch) != 0 { for _, subBatch := range req.SubBatch { subBatchDatas, subBatchFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, subBatch, "", req.Step) datas = append(datas, subBatchDatas...) fieldMap = subBatchFieldMap } } else if len(req.WaferID) != 0 { for _, waferID := range req.WaferID { waferIDDatas, waferIDFieldMap := LotInAFile(req.Product, req.Lot, req.PBI, "", waferID, req.Step) datas = append(datas, waferIDDatas...) fieldMap = waferIDFieldMap } } } selectionPointsMap := make(map[string]map[string]int) for _, data := range datas { if req.OnlyPass { if data[fieldMap["SOFT_BIN"]] != "1" { continue } } for _, selection := range req.XYSelection { var field string if req.SpliteSite { field = selection.X + "," + selection.Y + "_SITE" + data[fieldMap["SITE_NUM"]] } else { field = selection.X + "," + selection.Y } if _, ok := selectionPointsMap[field]; !ok { selectionPointsMap[field] = map[string]int{} } selectionPointsMap[field][data[fieldMap[selection.X]]+","+data[fieldMap[selection.Y]]]++ } } scatterFile.SetSheetName("Sheet1", "All") sheetName := scatterFile.GetSheetName(0) selectionValueMap := make(map[string][]int) rowIndex := 1 for _, selection := range req.XYSelection { for k := range selectionPointsMap { if strings.Contains(k, selection.X+","+selection.Y) { sheetX := []interface{}{k, selection.X} sheetY := []interface{}{"", selection.Y} for point := range selectionPointsMap[k] { pointXY := strings.Split(point, ",") xDecimal, _ := decimal.NewFromString(pointXY[0]) x, _ := xDecimal.Float64() yDecimal, _ := decimal.NewFromString(pointXY[1]) y, _ := yDecimal.Float64() sheetX = append(sheetX, x) sheetY = append(sheetY, y) } for idx, row := range [][]interface{}{ sheetX, sheetY, } { cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx) if err != nil { log.Println(err) } if err = scatterFile.SetSheetRow(sheetName, cell, &row); err != nil { log.Println(err) } } selectionValueMap[k] = []int{rowIndex, len(sheetY) + 1} rowIndex += 2 } } } a := 1 for _, selection := range req.XYSelection { b := 1 for k, v := range selectionValueMap { if strings.Contains(k, selection.X+","+selection.Y) { if err := scatterFile.AddChart("All", utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{ Type: excelize.Scatter, Series: []excelize.ChartSeries{ { Name: "All" + "!$A$1", Categories: "All" + "!$C$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: "All" + "!$C$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "circle", Size: 1, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "bottom", }, Title: []excelize.RichTextRun{ { Text: k, }, }, //PlotArea: excelize.ChartPlotArea{ // ShowCatName: false, // ShowLeaderLines: false, //}, ShowBlanksAs: "zero", }); err != nil { log.Println(err) } b += 9 } } a += 20 } } else { var groups []string var isFT bool if len(req.SubBatch) != 0 { groups = req.SubBatch isFT = true } else if len(req.WaferID) != 0 { groups = req.WaferID } else if len(req.SubBatch) == 0 && len(req.WaferID) == 0 { global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?", req.Product, req.Lot, req.PBI).Group("wafer_id").Select("wafer_id").Find(&groups) if len(groups) == 0 || groups[0] == "" { global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?", req.Product, req.Lot, req.PBI).Group("sub_batch").Select("sub_batch").Find(&groups) isFT = true } } for _, group := range groups { var datas [][]string fieldMap := make(map[string]int) if isFT { datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, group, "", req.Step) } else { datas, fieldMap = LotInAFile(req.Product, req.Lot, req.PBI, "", group, req.Step) } group = strings.ReplaceAll(group, "-", "_") selectionPointsMap := make(map[string]map[string]int) for _, data := range datas { if req.OnlyPass { if data[fieldMap["SOFT_BIN"]] != "1" { continue } } for _, selection := range req.XYSelection { var field string if req.SpliteSite { field = selection.X + "," + selection.Y + "_SITE" + data[fieldMap["SITE_NUM"]] } else { field = selection.X + "," + selection.Y } if _, ok := selectionPointsMap[field]; !ok { selectionPointsMap[field] = map[string]int{} } selectionPointsMap[field][data[fieldMap[selection.X]]+","+data[fieldMap[selection.Y]]]++ } } scatterFile.NewSheet(group) selectionValueMap := make(map[string][]int) rowIndex := 1 for _, selection := range req.XYSelection { for k := range selectionPointsMap { if strings.Contains(k, selection.X+","+selection.Y) { sheetX := []interface{}{k, selection.X} sheetY := []interface{}{"", selection.Y} for point := range selectionPointsMap[k] { pointXY := strings.Split(point, ",") xDecimal, _ := decimal.NewFromString(pointXY[0]) x, _ := xDecimal.Float64() yDecimal, _ := decimal.NewFromString(pointXY[1]) y, _ := yDecimal.Float64() sheetX = append(sheetX, x) sheetY = append(sheetY, y) } for idx, row := range [][]interface{}{ sheetX, sheetY, } { cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx) if err != nil { log.Println(err) } if err = scatterFile.SetSheetRow(group, cell, &row); err != nil { log.Println(err) } } selectionValueMap[k] = []int{rowIndex, len(sheetY) + 1} rowIndex += 2 } } } a := 1 for _, selection := range req.XYSelection { b := 1 for k, v := range selectionValueMap { if strings.Contains(k, selection.X+","+selection.Y) { if err := scatterFile.AddChart(group, utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{ Type: excelize.Scatter, Series: []excelize.ChartSeries{ { Name: group + "!$A$1", Categories: group + "!$C$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: group + "!$C$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "circle", Size: 1, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "bottom", }, Title: []excelize.RichTextRun{ { Text: k, }, }, //PlotArea: excelize.ChartPlotArea{ // ShowCatName: false, // ShowLeaderLines: false, //}, ShowBlanksAs: "zero", }); err != nil { log.Println(err) } b += 9 } } a += 20 } } } scatterFile.DeleteSheet("Sheet1") filePath := time.Now().Format("_散点图_2006-01-02_150405.xlsx") filePath = req.Product + "_" + req.Lot + filePath filePath = filepath.Join(utils.MakeSavePath("散点图"), filePath) _ = scatterFile.SaveAs(filePath) defer scatterFile.Close() return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath } func ExportFT() string { //start := carbon.Now().SubWeek().Format("Y-m-d") //end := carbon.Now().Format("Y-m-d") //var fileHandles []*model.FileHandled //global.PostGreSQL.Where("ending_time != ? AND DATE(ending_time) BETWEEN ? AND ?", "", start, end). // Find(&fileHandles) file := excelize.NewFile() var ftLists []*model.FTList //global.PostGreSQL.Where("order_date BETWEEN ? AND ?", start, end).Find(&ftLists) //global.PostGreSQL.Find(&ftLists) var a []string global.PostGreSQL.Model(&model.Report{}).Where("pbi != ?", "").Group("pbi").Select("pbi").Find(&a) global.PostGreSQL.Where("pbi IN ?", a).Find(&ftLists) //global.PostGreSQL.Where("product = ? AND lot = ? AND pbi = ?", "MP5016-5EC0", "S3S592", "M589-2306200001").Find(&ftLists) //var exportFTs []*model.ExportFT sheetName := "FT记录总表" _, _ = file.NewSheet(sheetName) _, _ = file.NewSheet("FT记录BIN表") _ = file.SetSheetRow(sheetName, "A1", &[]interface{}{"成品型号", "晶圆批次", "PBI", "测试厂", "测试程序", "下单日期", "丝印", "结批测试数量", "结批良品数量", "结批良率", "初测良品数量", "初测良率", "测试数量", "良品数量", "测试良率", "回收率", "测试数量差异", "良品数量差异", "硬件bin", "SITE差异", "叠料风险", }) ftRecordIndex := 2 for _, ftList := range ftLists { ftList.Product = strings.ReplaceAll(ftList.Product, "管装", "") var warning *model.Warning global.PostGreSQL.Where("step LIKE ? AND product = ?", "%FT%", ftList.Product). Preload("ProductionControl").Preload("PassProbabilityDiff").Preload("BinControl"). Preload("SelectionDiffControl").Preload("StackingMaterialsWarning").Preload("HistogramSelection"). Preload("ScatterSelection").Find(&warning) var testQuantity, firstPassQuantity, firstPassProbability, passQuantity, passProbability string var finalTestQuantity, finalPassQuantity, finalPassProbability, returnProbability string var reports []*model.Report global.PostGreSQL.Where("step LIKE ? AND product = ? AND lot = ? AND pbi = ?", "%FT%", ftList.Product, ftList.Lot, ftList.PBI).Find(&reports) for _, report := range reports { reportTestQuantityDecimal, _ := decimal.NewFromString(report.TestQuantity) testQuantityDecimal, _ := decimal.NewFromString(testQuantity) testQuantity = testQuantityDecimal.Add(reportTestQuantityDecimal).String() reportFirstPassQuantityDecimal, _ := decimal.NewFromString(report.FirstPassQuantity) firstPassQuantityDecimal, _ := decimal.NewFromString(firstPassQuantity) firstPassQuantity = firstPassQuantityDecimal.Add(reportFirstPassQuantityDecimal).String() reportPassQuantityDecimal, _ := decimal.NewFromString(report.PassQuantity) passQuantityDecimal, _ := decimal.NewFromString(passQuantity) passQuantity = passQuantityDecimal.Add(reportPassQuantityDecimal).String() } testQuantityDecimal, _ := decimal.NewFromString(testQuantity) firstPassQuantityDecimal, _ := decimal.NewFromString(firstPassQuantity) passQuantityDecimal, _ := decimal.NewFromString(passQuantity) if !testQuantityDecimal.IsZero() { firstPassProbability = firstPassQuantityDecimal.Div(testQuantityDecimal).Round(4). Mul(decimal.NewFromInt(100)).String() + "%" passProbability = passQuantityDecimal.Div(testQuantityDecimal).Round(4). Mul(decimal.NewFromInt(100)).String() + "%" returnProbability = passQuantityDecimal.Sub(firstPassQuantityDecimal).Div(testQuantityDecimal).Round(4). Mul(decimal.NewFromInt(100)).String() + "%" } var finalReports []*model.FinalReport hardBinCounter := make(map[string]float64) global.PostGreSQL.Where("step LIKE ? AND product = ? AND lot = ? AND pbi = ?", "%FT%", ftList.Product, ftList.Lot, ftList.PBI).Find(&finalReports) for _, finalReport := range finalReports { reportTestQuantityDecimal, _ := decimal.NewFromString(finalReport.ReportTestQuantity) finalTestQuantityDecimal, _ := decimal.NewFromString(finalTestQuantity) finalTestQuantity = finalTestQuantityDecimal.Add(reportTestQuantityDecimal).String() reportPassQuantityDecimal, _ := decimal.NewFromString(finalReport.ReportPassQuantity) finalPassQuantityDecimal, _ := decimal.NewFromString(finalPassQuantity) finalPassQuantity = finalPassQuantityDecimal.Add(reportPassQuantityDecimal).String() bin1Decimal, _ := decimal.NewFromString(finalReport.Bin1) bin1, _ := bin1Decimal.Float64() hardBinCounter["BIN1"] += bin1 bin2Decimal, _ := decimal.NewFromString(finalReport.Bin2) bin2, _ := bin2Decimal.Float64() hardBinCounter["BIN2"] += bin2 bin3Decimal, _ := decimal.NewFromString(finalReport.Bin3) bin3, _ := bin3Decimal.Float64() hardBinCounter["BIN3"] += bin3 bin4Decimal, _ := decimal.NewFromString(finalReport.Bin4) bin4, _ := bin4Decimal.Float64() hardBinCounter["BIN4"] += bin4 bin5Decimal, _ := decimal.NewFromString(finalReport.Bin5) bin5, _ := bin5Decimal.Float64() hardBinCounter["BIN5"] += bin5 bin6Decimal, _ := decimal.NewFromString(finalReport.Bin6) bin6, _ := bin6Decimal.Float64() hardBinCounter["BIN6"] += bin6 bin7Decimal, _ := decimal.NewFromString(finalReport.Bin7) bin7, _ := bin7Decimal.Float64() hardBinCounter["BIN7"] += bin7 bin8Decimal, _ := decimal.NewFromString(finalReport.Bin8) bin8, _ := bin8Decimal.Float64() hardBinCounter["BIN8"] += bin8 bin9Decimal, _ := decimal.NewFromString(finalReport.Bin9) bin9, _ := bin9Decimal.Float64() hardBinCounter["BIN9"] += bin9 bin10Decimal, _ := decimal.NewFromString(finalReport.Bin10) bin10, _ := bin10Decimal.Float64() hardBinCounter["BIN10"] += bin10 //outlookFailDecimal, _ := decimal.NewFromString(finalReport.OutlookFail) //otherDecimal, _ := decimal.NewFromString(finalReport.Other) //bentStitchDecimal, _ := decimal.NewFromString(finalReport.BentStitch) //scrappedDecimal, _ := decimal.NewFromString(finalReport.Scrapped) //if !reportTestQuantityDecimal.IsZero() { // finalReport.Bin1 = bin1Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin2 = bin2Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin3 = bin3Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin4 = bin4Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin5 = bin5Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin6 = bin6Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin7 = bin7Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin8 = bin8Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin9 = bin9Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Bin10 = bin10Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.OutlookFail = outlookFailDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Other = otherDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.BentStitch = bentStitchDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" // finalReport.Scrapped = scrappedDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). // String() + "%" //} } finalTestQuantityDecimal, _ := decimal.NewFromString(finalTestQuantity) finalPassQuantityDecimal, _ := decimal.NewFromString(finalPassQuantity) if !finalTestQuantityDecimal.IsZero() { finalPassProbability = finalPassQuantityDecimal.Div(finalTestQuantityDecimal).Round(4). Mul(decimal.NewFromInt(100)).String() + "%" } var testQuantityDiff, passQuantityDiff string hardBinState := "正常" if !testQuantityDecimal.IsZero() { testQuantityDiff = testQuantityDecimal.Sub(finalTestQuantityDecimal).Div(testQuantityDecimal).Round(4). Mul(decimal.NewFromInt(100)).String() + "%" passQuantityDiff = passQuantityDecimal.Sub(finalPassQuantityDecimal).Div(passQuantityDecimal).Round(4). Mul(decimal.NewFromInt(100)).String() + "%" for _, binControl := range warning.BinControl { binDecimal := decimal.NewFromFloat(hardBinCounter[binControl.Bin]) limitDecimal, _ := decimal.NewFromString(strings.ReplaceAll(binControl.BinFailLimitH, "%", "")) if !binDecimal.Div(testQuantityDecimal).Mul(decimal.NewFromInt(100)).LessThanOrEqual(limitDecimal) { hardBinState = "异常" } } } var fileHandles []*model.FileHandled global.PostGreSQL.Where("step = ? AND pbi = ? AND product = ? AND lot = ?", "FT", ftList.PBI, ftList.Product, ftList.Lot).Find(&fileHandles) siteDiffState := "正常" stackingMaterials := "否" for _, fileHandle := range fileHandles { f, err := os.Open(fileHandle.Path) if err != nil { log.Println(err) } scanner := bufio.NewScanner(f) datas := make([][]string, 0) fieldMap := make(map[string]int) for scanner.Scan() { line := scanner.Text() s := strings.Split(line, ",") datas = append(datas, s[:len(s)-1]) if len(datas) == 1 { for index, cell := range s { if index == len(s)-1 { continue } fieldMap[cell] = index } } } if len(datas) >= 1 { datas = datas[1:] } siteCounter := make(map[string]int64) sitePassCounter := make(map[string]int64) selectionCounter := make(map[string]map[string]string) stackingMaterialsArray := make(map[string]map[string][]float64) for _, data := range datas { siteCounter[data[fieldMap["SITE_NUM"]]]++ if _, ok := stackingMaterialsArray[data[fieldMap["SITE_NUM"]]]; !ok { stackingMaterialsArray[data[fieldMap["SITE_NUM"]]] = make(map[string][]float64) } if data[fieldMap["SOFT_BIN"]] == "1" { sitePassCounter[data[fieldMap["SITE_NUM"]]]++ for _, stackingMaterialsWarning := range warning.StackingMaterialsWarning { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[stackingMaterialsWarning.Selection]]) selectionFloat64, _ := selectionDecimal.Float64() if _, ok := stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection]; !ok { stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection] = []float64{selectionFloat64} } else { //quantity, _ := strconv.Atoi(stackingMaterialsWarning.Quantity) if len(stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection]) > stackingMaterialsWarning.Quantity { //quantity stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection] = stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection][1:] } stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection] = append( stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection], selectionFloat64) } if len(stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection]) == stackingMaterialsWarning.Quantity { diff, _ := strconv.ParseFloat(stackingMaterialsWarning.Diff, 64) midArray := stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection] sort.Slice(midArray, func(i, j int) bool { return midArray[i] > midArray[j] }) if midArray[0]-midArray[len(midArray)-1] > diff { stackingMaterials = "是" } } } } else { for _, stackingMaterialsWarning := range warning.StackingMaterialsWarning { stackingMaterialsArray[data[fieldMap["SITE_NUM"]]][stackingMaterialsWarning.Selection] = []float64{} } } for _, selectionDiffControl := range warning.SelectionDiffControl { if _, ok := selectionCounter[data[fieldMap["SITE_NUM"]]]; !ok { selectionCounter[data[fieldMap["SITE_NUM"]]] = make(map[string]string) } sumDecimal, _ := decimal.NewFromString(selectionCounter[data[fieldMap["SITE_NUM"]]][selectionDiffControl.Selection]) aDecimal, _ := decimal.NewFromString(data[fieldMap[selectionDiffControl.Selection]]) selectionCounter[data[fieldMap["SITE_NUM"]]][selectionDiffControl.Selection] = sumDecimal.Add(aDecimal).String() } } for _, selectionDiffControl := range warning.SelectionDiffControl { maxAverage, minAverage := math.SmallestNonzeroFloat64, math.MaxFloat64 for _, v := range selectionCounter { sumDecimal, _ := decimal.NewFromString(v[selectionDiffControl.Selection]) average, _ := sumDecimal.Div(decimal.NewFromInt(siteCounter[selectionDiffControl.Selection])).Float64() if average > maxAverage { maxAverage = average } if average < minAverage { minAverage = average } } siteDiffDecimal, _ := decimal.NewFromString(selectionDiffControl.SiteDiff) averageDiffDecimal := decimal.NewFromFloat(maxAverage - minAverage) if !siteDiffDecimal.LessThanOrEqual(averageDiffDecimal) { siteDiffState = "异常" break } } if siteDiffState == "正常" { for site := range siteCounter { sumDecimal := decimal.NewFromInt(siteCounter[site]) sumPassDecimal := decimal.NewFromInt(sitePassCounter[site]) sitePassProbabilityDecimal, _ := decimal.NewFromString(strings.ReplaceAll(warning.FirstPassProbabilityLimitL, "%", "")) if !sumPassDecimal.Div(sumDecimal).Mul(decimal.NewFromInt(100)).LessThanOrEqual(sitePassProbabilityDecimal) { siteDiffState = "异常" break } } } f.Close() } //exportFTs = append(exportFTs, &model.ExportFT{ // Product: ftList.Product, // Lot: ftList.Lot, // PBI: ftList.PBI, // Factory: ftList.Factory, // TestProgram: ftList.TestProgram, // OrderDate: ftList.OrderDate, // Seal: ftList.Seal, // FinalTestQuantity: finalTestQuantity, // FinalPassQuantity: finalPassQuantity, // FinalPassProbability: finalPassProbability, // FirstPassQuantity: firstPassQuantity, // FirstPassProbability: firstPassProbability, // TestQuantity: testQuantity, // PassQuantity: passQuantity, // PassProbability: passProbability, // ReturnProbability: returnProbability, // TestQuantityDiff: testQuantityDiff, // PassQuantityDiff: passQuantityDiff, // HardBinState: hardBinState, // SiteDiffState: siteDiffState, // StackingMaterials: stackingMaterials, //}) _ = file.SetCellStyle(sheetName, "A1", "U1", ExportFTSheetTitleStyle(file)) _ = file.SetSheetRow(sheetName, "A"+strconv.Itoa(ftRecordIndex), &[]interface{}{ftList.Product, ftList.Lot, ftList.PBI, ftList.Factory, ftList.TestProgram, ftList.OrderDate, ftList.Seal, finalTestQuantity, finalPassQuantity, finalPassProbability, firstPassQuantity, firstPassProbability, testQuantity, passQuantity, passProbability, returnProbability, testQuantityDiff, passQuantityDiff, hardBinState, siteDiffState, stackingMaterials}) _ = file.SetRowStyle(sheetName, ftRecordIndex, ftRecordIndex, NormalSheetStyle(file)) _ = file.SetCellHyperLink(sheetName, "B"+strconv.Itoa(ftRecordIndex), ftList.Lot+"参数差异汇总表!A1", "Location") if warning.FirstPassProbabilityLimitL != "" { s := strings.ReplaceAll(firstPassProbability, "%", "") sDecimal, _ := decimal.NewFromString(s) firstPassProbabilityLimitLDecimal, _ := decimal.NewFromString(strings.ReplaceAll(warning.FirstPassProbabilityLimitL, "%", "")) if sDecimal.LessThan(firstPassProbabilityLimitLDecimal) { _ = file.SetCellStyle(sheetName, "L"+strconv.Itoa(ftRecordIndex), "L"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } } if warning.PassProbabilityLimitL != "" { s := strings.ReplaceAll(passProbability, "%", "") sDecimal, _ := decimal.NewFromString(s) passProbabilityLimitLDecimal, _ := decimal.NewFromString(strings.ReplaceAll(warning.PassProbabilityLimitL, "%", "")) if sDecimal.LessThan(passProbabilityLimitLDecimal) { _ = file.SetCellStyle(sheetName, "O"+strconv.Itoa(ftRecordIndex), "O"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } } if warning.ReturnProbabilityLimitH != "" { s := strings.ReplaceAll(returnProbability, "%", "") sDecimal, _ := decimal.NewFromString(s) returnProbabilityLimitHDecimal, _ := decimal.NewFromString(strings.ReplaceAll(warning.ReturnProbabilityLimitH, "%", "")) if returnProbabilityLimitHDecimal.LessThan(sDecimal) { _ = file.SetCellStyle(sheetName, "P"+strconv.Itoa(ftRecordIndex), "P"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } } for _, productionControl := range warning.ProductionControl { if productionControl.Factory == ftList.Factory { testQuantityDiffStr := strings.ReplaceAll(testQuantityDiff, "%", "") sDecimal, _ := decimal.NewFromString(testQuantityDiffStr) testQuantityDiffHDecimal, _ := decimal.NewFromString(strings.ReplaceAll(productionControl.TestQuantityDiffH, "%", "")) testQuantityDiffLDecimal, _ := decimal.NewFromString(strings.ReplaceAll(productionControl.TestQuantityDiffL, "%", "")) if sDecimal.LessThan(testQuantityDiffLDecimal) || testQuantityDiffHDecimal.LessThan(sDecimal) { _ = file.SetCellStyle(sheetName, "Q"+strconv.Itoa(ftRecordIndex), "Q"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } passQuantityDiffStr := strings.ReplaceAll(passQuantityDiff, "%", "") saDecimal, _ := decimal.NewFromString(passQuantityDiffStr) passQuantityDiffHDecimal, _ := decimal.NewFromString(strings.ReplaceAll(productionControl.PassQuantityDiffH, "%", "")) passQuantityDiffLDecimal, _ := decimal.NewFromString(strings.ReplaceAll(productionControl.PassQuantityDiffL, "%", "")) if saDecimal.LessThan(passQuantityDiffLDecimal) || passQuantityDiffHDecimal.LessThan(saDecimal) { _ = file.SetCellStyle(sheetName, "R"+strconv.Itoa(ftRecordIndex), "R"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } break } } if hardBinState == "异常" { _ = file.SetCellStyle(sheetName, "S"+strconv.Itoa(ftRecordIndex), "S"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } if siteDiffState == "异常" { _ = file.SetCellStyle(sheetName, "T"+strconv.Itoa(ftRecordIndex), "T"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } if stackingMaterials == "是" { _ = file.SetCellStyle(sheetName, "U"+strconv.Itoa(ftRecordIndex), "U"+strconv.Itoa(ftRecordIndex), ExportWarningStyle(file)) } if len(finalReports) > 0 { ExportFTDetailsSheet(finalReports, file, warning.BinControl) } if warning != nil { ExportFTHistogramSheet(warning, ftList, file) ExportFTScatterSheet(warning, ftList, file) } ftRecordIndex++ } //_ = file.SetRowStyle(sheetName, 2, ftRecordIndex-1, NormalSheetStyle(file)) _ = file.SetColWidth(sheetName, "A", "U", 20) //ExportFTSumSheet(exportFTs, file) file.DeleteSheet("Sheet1") filePath := time.Now().Format("FT自动导出_2006-01-02_150405.xlsx") filePath = filepath.Join(utils.MakeSavePath("自动导出"), filePath) file.SaveAs(filePath) defer file.Close() return strings.ReplaceAll(viper.GetString("domain"), "\\", "/") + filePath } func ExportFTSumSheet(exportFTs []*model.ExportFT, f *excelize.File) { sheetName := "FT记录总表" _, _ = f.NewSheet(sheetName) _ = f.SetSheetRow(sheetName, "A1", &[]interface{}{"成品型号", "晶圆批次", "PBI", "测试厂", "测试程序", "下单日期", "丝印", "结批测试数量", "结批良品数量", "结批良率", "初测良品数量", "初测良率", "测试数量", "良品数量", "测试良率", "回收率", "测试数量差异", "良品数量差异", "硬件bin", "SITE差异", "叠料风险", }) _ = f.SetCellStyle(sheetName, "A1", "U1", ExportFTSheetTitleStyle(f)) for index, exportFT := range exportFTs { _ = f.SetSheetRow(sheetName, "A"+strconv.Itoa(index+2), &[]interface{}{exportFT.Product, exportFT.Lot, exportFT.PBI, exportFT.Factory, exportFT.TestProgram, exportFT.OrderDate, exportFT.Seal, exportFT.FinalTestQuantity, exportFT.FinalPassQuantity, exportFT.FinalPassProbability, exportFT.FirstPassQuantity, exportFT.FirstPassProbability, exportFT.TestQuantity, exportFT.PassQuantity, exportFT.PassProbability, exportFT.ReturnProbability, exportFT.TestQuantityDiff, exportFT.PassQuantityDiff, exportFT.HardBinState, exportFT.SiteDiffState, exportFT.StackingMaterials}) } _ = f.SetRowStyle(sheetName, 2, len(exportFTs)+1, NormalSheetStyle(f)) _ = f.SetColWidth(sheetName, "A", "U", 20) } func ExportFTDetailsSheet(finalReports []*model.FinalReport, f *excelize.File, binControls []model.BinControl) { sheetName := "FT记录BIN表" //_, _ = f.NewSheet(sheetName) _ = f.SetSheetRow(sheetName, "A1", &[]interface{}{"成品型号", "晶圆批次", "PBI", "测试厂", "测试程序", "下单日期", "丝印", "子批", "结批测试数量", "结批良品数量", "结批良率", "BIN1占比", "BIN2占比", "BIN3占比", "BIN4占比", "BIN5占比", "BIN6占比", "BIN7占比", "BIN8占比", "BIN9占比", "BIN10占比", "外观不良", "少数", "弯脚", "报废", "BIN1", "BIN2", "BIN3", "BIN4", "BIN5", "BIN6", "BIN7", "BIN8", "BIN9", "BIN10", }) _ = f.SetCellStyle(sheetName, "A1", "AN1", ExportFTSheetTitleStyle(f)) for index, finalReport := range finalReports { for _, binControl := range binControls { var bin string binControl.Bin = strings.ToUpper(binControl.Bin) if binControl.Bin == "BIN1" { bin = finalReport.Bin1 } else if binControl.Bin == "BIN2" { bin = finalReport.Bin2 } else if binControl.Bin == "BIN3" { bin = finalReport.Bin3 } else if binControl.Bin == "BIN4" { bin = finalReport.Bin4 } else if binControl.Bin == "BIN5" { bin = finalReport.Bin5 } else if binControl.Bin == "BIN6" { bin = finalReport.Bin6 } else if binControl.Bin == "BIN7" { bin = finalReport.Bin7 } else if binControl.Bin == "BIN8" { bin = finalReport.Bin8 } else if binControl.Bin == "BIN9" { bin = finalReport.Bin9 } else if binControl.Bin == "BIN10" { bin = finalReport.Bin10 } if binControl.BinFailLimitH != "" { s := strings.ReplaceAll(bin, "%", "") sDecimal, _ := decimal.NewFromString(s) binFailLimitHDecimal, _ := decimal.NewFromString(strings.ReplaceAll(binControl.BinFailLimitH, "%", "")) if binFailLimitHDecimal.LessThan(sDecimal) { _ = f.SetCellStyle(sheetName, "L"+strconv.Itoa(index+2), "L"+strconv.Itoa(index+2), ExportWarningStyle(f)) } } } reportTestQuantityDecimal, _ := decimal.NewFromString(finalReport.ReportTestQuantity) var bin1, bin2, bin3, bin4, bin5, bin6, bin7, bin8, bin9, bin10, outlookFail, other, bentStitch, scrapped string if !reportTestQuantityDecimal.IsZero() { bin1Decimal, _ := decimal.NewFromString(finalReport.Bin1) bin1 = bin1Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin2Decimal, _ := decimal.NewFromString(finalReport.Bin2) bin2 = bin2Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin3Decimal, _ := decimal.NewFromString(finalReport.Bin3) bin3 = bin3Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin4Decimal, _ := decimal.NewFromString(finalReport.Bin4) bin4 = bin4Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin5Decimal, _ := decimal.NewFromString(finalReport.Bin5) bin5 = bin5Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin6Decimal, _ := decimal.NewFromString(finalReport.Bin6) bin6 = bin6Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin7Decimal, _ := decimal.NewFromString(finalReport.Bin7) bin7 = bin7Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin8Decimal, _ := decimal.NewFromString(finalReport.Bin8) bin8 = bin8Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin9Decimal, _ := decimal.NewFromString(finalReport.Bin9) bin9 = bin9Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bin10Decimal, _ := decimal.NewFromString(finalReport.Bin10) bin10 = bin10Decimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" outlookFailDecimal, _ := decimal.NewFromString(finalReport.OutlookFail) outlookFail = outlookFailDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" otherDecimal, _ := decimal.NewFromString(finalReport.Other) other = otherDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" bentStitchDecimal, _ := decimal.NewFromString(finalReport.BentStitch) bentStitch = bentStitchDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" scrappedDecimal, _ := decimal.NewFromString(finalReport.Scrapped) scrapped = scrappedDecimal.Div(reportTestQuantityDecimal).Round(4).Mul(decimal.NewFromInt(100)). String() + "%" } _ = f.SetSheetRow(sheetName, "A"+strconv.Itoa(index+2), &[]interface{}{finalReport.Product, finalReport.Lot, finalReport.PBI, finalReport.Factory, finalReport.TestProgram, finalReport.OrderDate, finalReport.Seal, finalReport.SubBatch, finalReport.ReportTestQuantity, finalReport.ReportPassQuantity, finalReport.ReportPassProbability, bin1, bin2, bin3, bin4, bin5, bin6, bin7, bin8, bin9, bin10, finalReport.OutlookFail, finalReport.Other, finalReport.BentStitch, finalReport.Scrapped, finalReport.Bin1, finalReport.Bin2, finalReport.Bin3, finalReport.Bin4, finalReport.Bin5, finalReport.Bin6, finalReport.Bin7, finalReport.Bin8, finalReport.Bin9, finalReport.Bin10, outlookFail, other, bentStitch, scrapped}) } _ = f.SetRowStyle(sheetName, 2, len(finalReports)+1, NormalSheetStyle(f)) _ = f.SetColWidth(sheetName, "A", "AN", 20) } func ExportFTHistogramSheet(warning *model.Warning, ftList *model.FTList, f *excelize.File) { row := 1 start := row selectionUnit := make(map[string]string) subBatchDiff := make(map[string][]float64) sheetName := ftList.Lot + "参数差异汇总表" f.NewSheet(sheetName) var groups []string global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?", ftList.Product, ftList.Lot, ftList.PBI).Group("sub_batch").Select("sub_batch").Find(&groups) for _, group := range groups { var fileHandle *model.FileHandled global.PostGreSQL.Where("product = ? AND lot = ? AND pbi = ? AND sub_batch = ?", ftList.Product, ftList.Lot, ftList.PBI, group).Find(&fileHandle) m := make(map[string]map[string]string) _ = json.Unmarshal([]byte(fileHandle.TitleInfo), &m) siteSelectionInfo := make(map[string][]float64) selectionInfo := make(map[string][]float64) var datas [][]string fieldMap := make(map[string]int) datas, fieldMap = LotInAFile(ftList.Product, ftList.Lot, ftList.PBI, group, "", "FT") group = strings.ReplaceAll(group, "-", "_") if len(datas) == 0 { continue } for _, data := range datas { if data[fieldMap["SOFT_BIN"]] != "1" { continue } for _, histogramSelection := range warning.HistogramSelection { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[histogramSelection.Selection]]) selectionFloat, _ := selectionDecimal.Float64() var filed string filed = histogramSelection.Selection if _, ok := siteSelectionInfo[filed]; !ok { siteSelectionInfo[filed] = []float64{math.MaxInt, math.MinInt, 0, 0} } if siteSelectionInfo[filed][0] > selectionFloat { siteSelectionInfo[filed][0] = selectionFloat } if siteSelectionInfo[filed][1] < selectionFloat { siteSelectionInfo[filed][1] = selectionFloat } siteSelectionInfo[filed][2] += selectionFloat siteSelectionInfo[filed][3]++ if _, ok := selectionInfo[histogramSelection.Selection]; !ok { selectionInfo[histogramSelection.Selection] = []float64{math.MaxInt, math.MinInt, 0, 0} } if selectionInfo[histogramSelection.Selection][0] > selectionFloat { selectionInfo[histogramSelection.Selection][0] = selectionFloat } if selectionInfo[histogramSelection.Selection][1] < selectionFloat { selectionInfo[histogramSelection.Selection][1] = selectionFloat } selectionInfo[histogramSelection.Selection][2] += selectionFloat selectionInfo[histogramSelection.Selection][3]++ } } selectionHistogramX := make(map[string][]string) selectionHistogramY := make(map[string][]int) for k, v := range siteSelectionInfo { siteSelectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0} } for k, v := range selectionInfo { selectionInfo[k] = []float64{v[0], v[1], v[2], v[3], v[2] / v[3], 0, 0} selectionHistogramX[group+"_"+k] = []string{} minDecimal := decimal.NewFromFloat(v[0]) if minDecimal.LessThan(decimal.NewFromInt(0)) { minDecimal = minDecimal.RoundUp(0) } else { minDecimal = minDecimal.RoundDown(0) } maxDecimal := decimal.NewFromFloat(v[1]) if maxDecimal.LessThan(decimal.NewFromInt(0)) { maxDecimal = maxDecimal.RoundDown(0) } else { maxDecimal = maxDecimal.RoundUp(0) } interval := maxDecimal.Sub(minDecimal).Div(decimal.NewFromInt(20)) for i := 0; i < 21; i++ { xPoint := minDecimal.Add(interval.Mul(decimal.NewFromInt(int64(i)))).String() selectionHistogramX[group+"_"+k] = append(selectionHistogramX[group+"_"+k], xPoint) } } for _, data := range datas { if data[fieldMap["SOFT_BIN"]] != "1" { continue } for k, v := range siteSelectionInfo { var field string field = k if data[fieldMap[field]] != "" { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[field]]) averageDecimal := decimal.NewFromFloat(v[4]) d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)). Float64() v[5] += d v[6]++ } } for k, v := range selectionInfo { if data[fieldMap[k]] != "" { selectionDecimal, _ := decimal.NewFromString(data[fieldMap[k]]) averageDecimal := decimal.NewFromFloat(v[4]) d, _ := averageDecimal.Sub(selectionDecimal).Pow(decimal.NewFromInt(2)). Float64() field := group + "_" + k for i := 0; i < len(selectionHistogramX[field]); i++ { if i == len(selectionHistogramX[field])-1 { xPointDecimal, _ := decimal.NewFromString(selectionHistogramX[field][i]) if xPointDecimal.Equals(selectionDecimal) { selectionHistogramY[field][i]++ } break } xPointMin, _ := decimal.NewFromString(selectionHistogramX[field][i]) xPointMax, _ := decimal.NewFromString(selectionHistogramX[field][i+1]) if _, ok := selectionHistogramY[field]; !ok { selectionHistogramY[field] = make([]int, len(selectionHistogramX[field])) } if xPointMin.LessThanOrEqual(selectionDecimal) && selectionDecimal.LessThan(xPointMax) { selectionHistogramY[field][i]++ } } v[5] += d v[6]++ } } } rowIndex, colIndex := 2, 1 histogramSheetName := ftList.Lot + "_" + group + "直方图" f.NewSheet(histogramSheetName) selectionValueMap := make(map[string][]int) for k, v := range selectionHistogramX { sheetX := []interface{}{k} for _, x := range v { sheetX = append(sheetX, x) } sheetY := []interface{}{"数量"} for _, y := range selectionHistogramY[k] { sheetY = append(sheetY, y) } for idx, rows := range [][]interface{}{ sheetX, sheetY, } { cell, err := excelize.CoordinatesToCellName(colIndex, rowIndex+idx) if err != nil { log.Println(err) } if err = f.SetSheetRow(histogramSheetName, cell, &rows); err != nil { log.Println(err) } } selectionValueMap[k] = []int{rowIndex, len(selectionHistogramY[k]) + 1} rowIndex += 2 } a := 1 for _, histogramSelection := range warning.HistogramSelection { b := 1 for k, v := range selectionValueMap { if strings.Contains(k, histogramSelection.Selection) { if err := f.AddChart(histogramSheetName, utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{ Type: excelize.Col, Series: []excelize.ChartSeries{ { Name: histogramSheetName + "!$A$1", Categories: histogramSheetName + "!$B$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: histogramSheetName + "!$B$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "none", Size: 10, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "left", }, Title: []excelize.RichTextRun{ { Text: k, }, }, PlotArea: excelize.ChartPlotArea{ ShowCatName: false, ShowLeaderLines: false, ShowVal: true, }, ShowBlanksAs: "zero", }, &excelize.Chart{ Type: excelize.Line, Series: []excelize.ChartSeries{ { Name: group + "!$A$1", Categories: group + "!$B$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: group + "!$B$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "none", Size: 10, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "right", }, PlotArea: excelize.ChartPlotArea{ ShowCatName: false, ShowLeaderLines: false, }, }); err != nil { log.Println(err) } b += 9 } } a += 20 } col := 1 _ = f.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ ftList.Product + "_" + ftList.Lot + "_" + group}) var selectionInfoArray []SelectionInfo for _, histogramSelection := range warning.HistogramSelection { var oneSelectionInfo []SelectionInfo for k, v := range siteSelectionInfo { if !strings.Contains(k, histogramSelection.Selection) { continue } oneSelectionInfo = append(oneSelectionInfo, SelectionInfo{ Field: k, Min: v[0], Max: v[1], Average: v[4], StandardDeviation: math.Sqrt(v[5] / v[6]), Num: v[3], }) } sort.Slice(oneSelectionInfo, func(i, j int) bool { return oneSelectionInfo[i].Field < oneSelectionInfo[j].Field }) selectionInfoArray = append(selectionInfoArray, oneSelectionInfo...) } if len(selectionInfoArray) > 0 { _ = f.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "直方图", "", "最小值", "最大值", "平均值", "δ", "数量", "Limit L", "Limit H", "UNIT"}) _ = f.SetCellHyperLink(sheetName, "A"+strconv.Itoa(start+1), ftList.Lot+"_"+group+"直方图!A1", "Location") _ = f.SetCellHyperLink(sheetName, "B"+strconv.Itoa(start+1), ftList.Lot+"_"+group+"散点图!A1", "Location") } else { _ = f.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "", "", "最小值", "最大值", "平均值", "δ", "数量", "Limit L", "Limit H", "UNIT"}) } col++ for k, v := range selectionInfoArray { field := strings.Split(v.Field, "_Site")[0] limitLDecimal, _ := decimal.NewFromString(m[field]["LimitL"]) limitLFloat, _ := limitLDecimal.Float64() limitUDecimal, _ := decimal.NewFromString(m[field]["LimitU"]) limitUFloat, _ := limitUDecimal.Float64() selectionUnit[field] = m[field]["Unit"] if k == 0 { _ = f.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ "散点图", v.Field, v.Min, v.Max, v.Average, v.StandardDeviation, v.Num, limitLFloat, limitUFloat, m[field]["Unit"]}) } else { _ = f.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ "", v.Field, v.Min, v.Max, v.Average, v.StandardDeviation, v.Num, limitLFloat, limitUFloat, m[field]["Unit"]}) } col++ } for k, v := range selectionInfo { if _, ok := subBatchDiff[k]; !ok { subBatchDiff[k] = []float64{v[4], v[4], v[5] / v[6], v[5] / v[6]} selectionUnit[k] = m[k]["Unit"] } else { if v[4] > subBatchDiff[k][0] { subBatchDiff[k][0] = v[4] } if v[4] < subBatchDiff[k][1] { subBatchDiff[k][1] = v[4] } if v[5]/v[6] > subBatchDiff[k][2] { subBatchDiff[k][2] = v[5] / v[6] } if v[5]/v[6] < subBatchDiff[k][3] { subBatchDiff[k][3] = v[5] / v[6] } } } _ = f.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), NormalSheetStyle(f)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+2), TitleStyle(f)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start+3), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+7), ActualLimitStyle(f)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start+8), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+10), LimitStyle(f)) _ = f.SetColWidth(sheetName, "A", utils.NumToRow(strconv.Itoa(col-1)), 20) start += 11 start += 1 } start += 1 col := 2 _ = f.SetSheetCol(sheetName, "A"+strconv.Itoa(start), &[]interface{}{ "批次差异汇总"}) _ = f.SetSheetCol(sheetName, "A"+strconv.Itoa(start+1), &[]interface{}{ "", "平均值", "δ", "UNIT"}) for _, histogramSelection := range warning.HistogramSelection { if len(subBatchDiff[histogramSelection.Selection]) != 0 { _ = f.SetSheetCol(sheetName, utils.NumToRow(strconv.Itoa(col))+strconv.Itoa(start+1), &[]interface{}{ histogramSelection.Selection, subBatchDiff[histogramSelection.Selection][0] - subBatchDiff[histogramSelection.Selection][1], subBatchDiff[histogramSelection.Selection][2] - subBatchDiff[histogramSelection.Selection][3], selectionUnit[histogramSelection.Selection]}) col++ } } _ = f.MergeCell(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start), NormalSheetStyle(f)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start+1), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+1), TitleStyle(f)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start+2), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+3), ActualLimitStyle(f)) _ = f.SetCellStyle(sheetName, "A"+strconv.Itoa(start+4), utils.NumToRow(strconv.Itoa(col-1))+strconv.Itoa(start+4), LimitStyle(f)) } func ExportFTScatterSheet(warning *model.Warning, ftList *model.FTList, f *excelize.File) { var groups []string global.PostGreSQL.Model(&model.FileHandled{}).Where("product = ? AND lot = ? AND pbi = ?", ftList.Product, ftList.Lot, ftList.PBI).Group("sub_batch").Select("sub_batch").Find(&groups) for _, group := range groups { var datas [][]string fieldMap := make(map[string]int) datas, fieldMap = LotInAFile(ftList.Product, ftList.Lot, ftList.PBI, group, "", "FT") if len(datas) == 0 { continue } group = strings.ReplaceAll(group, "-", "_") selectionPointsMap := make(map[string]map[string]int) for _, data := range datas { if data[fieldMap["SOFT_BIN"]] != "1" { continue } for _, scatterSelection := range warning.ScatterSelection { var field string field = scatterSelection.XSelection + "," + scatterSelection.YSelection if _, ok := selectionPointsMap[field]; !ok { selectionPointsMap[field] = map[string]int{} } selectionPointsMap[field][data[fieldMap[scatterSelection.XSelection]]+","+data[fieldMap[scatterSelection.YSelection]]]++ } } sheetName := ftList.Lot + "_" + group + "散点图" f.NewSheet(sheetName) selectionValueMap := make(map[string][]int) rowIndex := 1 for _, scatterSelection := range warning.ScatterSelection { for k := range selectionPointsMap { if strings.Contains(k, scatterSelection.XSelection+","+scatterSelection.YSelection) { sheetX := []interface{}{k, scatterSelection.XSelection} sheetY := []interface{}{"", scatterSelection.YSelection} for point := range selectionPointsMap[k] { pointXY := strings.Split(point, ",") xDecimal, _ := decimal.NewFromString(pointXY[0]) x, _ := xDecimal.Float64() yDecimal, _ := decimal.NewFromString(pointXY[1]) y, _ := yDecimal.Float64() sheetX = append(sheetX, x) sheetY = append(sheetY, y) } for idx, row := range [][]interface{}{ sheetX, sheetY, } { cell, err := excelize.CoordinatesToCellName(1, rowIndex+idx) if err != nil { log.Println(err) } if err = f.SetSheetRow(sheetName, cell, &row); err != nil { log.Println(err) } } selectionValueMap[k] = []int{rowIndex, len(sheetY) + 1} rowIndex += 2 } } } a := 1 for _, scatterSelection := range warning.ScatterSelection { b := 1 for k, v := range selectionValueMap { if strings.Contains(k, scatterSelection.XSelection+","+scatterSelection.YSelection) { if err := f.AddChart(sheetName, utils.NumToRow(strconv.Itoa(b))+strconv.Itoa(a), &excelize.Chart{ Type: excelize.Scatter, Series: []excelize.ChartSeries{ { Name: sheetName + "!$A$1", Categories: sheetName + "!$C$" + strconv.Itoa(v[0]) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]), Values: sheetName + "!$C$" + strconv.Itoa(v[0]+1) + ":$" + utils.NumToRow(strconv.Itoa(v[1])) + "$" + strconv.Itoa(v[0]+1), Marker: excelize.ChartMarker{ Symbol: "circle", Size: 1, }, }, }, Format: excelize.GraphicOptions{ ScaleX: 1, ScaleY: 1, OffsetX: 15, OffsetY: 10, }, Legend: excelize.ChartLegend{ Position: "bottom", }, Title: []excelize.RichTextRun{ { Text: k, }, }, //PlotArea: excelize.ChartPlotArea{ // ShowCatName: false, // ShowLeaderLines: false, //}, ShowBlanksAs: "zero", }); err != nil { log.Println(err) } b += 9 } } a += 20 } } }