2403 lines
91 KiB
Go
2403 lines
91 KiB
Go
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)
|
|
selectionXLimitMap := make(map[string][]float64)
|
|
selectionYLimitMap := make(map[string][]float64)
|
|
|
|
rowIndex := 1
|
|
for _, selection := range req.XYSelection {
|
|
for k := range selectionPointsMap {
|
|
if strings.Contains(k, selection.X+","+selection.Y) {
|
|
if _, ok := selectionXLimitMap[k]; !ok {
|
|
selectionXLimitMap[k] = []float64{math.MinInt64, math.MaxFloat64}
|
|
}
|
|
if _, ok := selectionYLimitMap[k]; !ok {
|
|
selectionYLimitMap[k] = []float64{math.MinInt64, math.MaxFloat64}
|
|
}
|
|
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)
|
|
if x > selectionXLimitMap[k][0] {
|
|
selectionXLimitMap[k][0] = x
|
|
}
|
|
if x < selectionXLimitMap[k][1] {
|
|
selectionXLimitMap[k][1] = x
|
|
}
|
|
if y > selectionYLimitMap[k][0] {
|
|
selectionYLimitMap[k][0] = y
|
|
}
|
|
if y < selectionYLimitMap[k][1] {
|
|
selectionYLimitMap[k][1] = 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,
|
|
XAxis: excelize.ChartAxis{
|
|
Maximum: &selectionXLimitMap[k][0],
|
|
Minimum: &selectionXLimitMap[k][1],
|
|
},
|
|
YAxis: excelize.ChartAxis{
|
|
Maximum: &selectionYLimitMap[k][0],
|
|
Minimum: &selectionYLimitMap[k][1],
|
|
},
|
|
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)
|
|
selectionXLimitMap := make(map[string][]float64)
|
|
selectionYLimitMap := make(map[string][]float64)
|
|
|
|
rowIndex := 1
|
|
for _, selection := range req.XYSelection {
|
|
for k := range selectionPointsMap {
|
|
if strings.Contains(k, selection.X+","+selection.Y) {
|
|
if _, ok := selectionXLimitMap[k]; !ok {
|
|
selectionXLimitMap[k] = []float64{math.MinInt64, math.MaxFloat64}
|
|
}
|
|
if _, ok := selectionYLimitMap[k]; !ok {
|
|
selectionYLimitMap[k] = []float64{math.MinInt64, math.MaxFloat64}
|
|
}
|
|
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)
|
|
if x > selectionXLimitMap[k][0] {
|
|
selectionXLimitMap[k][0] = x
|
|
}
|
|
if x < selectionXLimitMap[k][1] {
|
|
selectionXLimitMap[k][1] = x
|
|
}
|
|
if y > selectionYLimitMap[k][0] {
|
|
selectionYLimitMap[k][0] = y
|
|
}
|
|
if y < selectionYLimitMap[k][1] {
|
|
selectionYLimitMap[k][1] = 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,
|
|
XAxis: excelize.ChartAxis{
|
|
Maximum: &selectionXLimitMap[k][0],
|
|
Minimum: &selectionXLimitMap[k][1],
|
|
},
|
|
YAxis: excelize.ChartAxis{
|
|
Maximum: &selectionYLimitMap[k][0],
|
|
Minimum: &selectionYLimitMap[k][1],
|
|
},
|
|
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 LIKE ?", "%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
|
|
}
|
|
}
|
|
}
|