package controllers

import (
	"ColdVerify_local/lib"
	"ColdVerify_local/logs"
	"ColdVerify_local/models/Device"
	"ColdVerify_local/models/Task"
	"ColdVerify_local/models/VerifyTemplate"
	"errors"
	"fmt"
	beego "github.com/beego/beego/v2/server/web"
	"math"
	"net/http"
	"strconv"
	"strings"
	"time"
)

type TaskDataHandleController struct {
	beego.Controller
}

/*
同区域数据缺失
*/

// 测点自检 自动添加缺失终端，取关联绑定终端平均复制
func (c *TaskDataHandleController) SSE_Automatically_add_missing_terminal() {

	T_task_id := c.GetString("T_task_id") //  v26nplogbwt1
	println("T_task_id:", T_task_id)
	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}

	DeviceClassList_r := Device.Read_DeviceClassList_List_id_By_Terminal(Task_r.T_class, false)
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "终端总共：" + lib.To_string(len(DeviceClassList_r)) + " 正在检查自检探头..."})
	for _, class_ := range DeviceClassList_r {

		_, cnt := Task.Read_TaskData_ById_List(Task_r.T_task_id, class_.T_sn, class_.T_id, "", "", 0, 1)
		if cnt == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "找到" + class_.T_id + " " + class_.T_remark + " 自检探头"})

			// -----------开始平均复制到
			device := Device.DeviceClassList{
				T_class:  class_.T_class,
				T_id:     class_.T_id,
				T_sn:     class_.T_sn,
				T_remark: class_.T_remark,
			}

			c.SetAdjacentDeviceAVGTaskData(T_task_id, device, "", "")
			// -----------开始平均复制到结束
		}

	}
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

// 测点数据自检 自动添加缺失数据，取关联绑定终端平均复制
func (c *TaskDataHandleController) SSE_Automatically_add_missing_data() {

	T_task_id := c.GetString("T_task_id") //  v26nplogbwt1

	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}
	// 时间间隔 s
	T_saveT := 60

	DeviceClassList_list := Device.Read_DeviceClassList_List_id_By_Terminal(Task_r.T_class, false)

	// 分组统计每个sn的数据数量
	snList := Device.JoinDeviceClassListSnToString(DeviceClassList_list)
	TaskData_Total_GroupBySnId := Task.Read_TaskData_Total_GroupBySnId(Task_r.T_task_id, snList, "", "")
	if len(TaskData_Total_GroupBySnId) == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "暂无测点需要自检！"})
	}
	Devicedata_list_MAX := TaskData_Total_GroupBySnId[0].Total

	TaskData_Total_GroupBySnId_Map := make(map[string]int64)
	for _, v := range TaskData_Total_GroupBySnId {
		TaskData_Total_GroupBySnId_Map[v.T_sn] = v.Total
	}

	startTime, endTime := Task.Read_TaskData_T_time_T_Min_Max(Task_r.T_task_id, TaskData_Total_GroupBySnId[0].T_sn, TaskData_Total_GroupBySnId[0].T_id, "", "")

	logs.Println("数据标准数量：", Devicedata_list_MAX)
	// 选择 数据缺失的终端
	for _, DeviceClassList_r := range DeviceClassList_list {

		total, ok := TaskData_Total_GroupBySnId_Map[DeviceClassList_r.T_sn]
		if !ok {
			// 测点自检事再处理
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: DeviceClassList_r.T_id + " 终端自检数据 , 测点缺失，跳过"})
			continue
		}
		if Devicedata_list_MAX == total {
			continue
		}

		list, cnt := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, DeviceClassList_r.T_sn, DeviceClassList_r.T_id, "", "", 0, 9999)

		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: DeviceClassList_r.T_id + " 终端自检数据 ,数据差值：" + lib.To_string(Devicedata_list_MAX-cnt) + " "})

		// 开始结束时间不同，直接执行平均复制到
		if startTime != list[0].T_time || endTime != list[len(list)-1].T_time {
			if Devicedata_list_MAX-cnt != 1 {
				// -----------开始平均复制到
				device := Device.DeviceClassList{
					T_class:  DeviceClassList_r.T_class,
					T_id:     DeviceClassList_r.T_id,
					T_sn:     DeviceClassList_r.T_sn,
					T_remark: DeviceClassList_r.T_remark,
				}
				c.SetAdjacentDeviceAVGTaskData(T_task_id, device, "", "")
				// -----------开始平均复制到结束
				continue
			} else {
				if startTime != list[0].T_time {
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: DeviceClassList_r.T_id + " 找到到自检时间点 " + startTime + " ~ " + list[0].T_time + " 开始自检"})
					Task.InsertTaskData(Task_r.T_task_id, Task.TaskData_{
						T_sn:   list[0].T_sn,
						T_id:   list[0].T_id,
						T_t:    list[0].T_t,
						T_rh:   list[0].T_rh,
						T_time: startTime,
					})
					continue
				}
				if endTime != list[len(list)-1].T_time {
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: DeviceClassList_r.T_id + " 找到自检时间点 " + list[len(list)-1].T_time + " ~ " + endTime + " 开始自检"})
					Task.InsertTaskData(Task_r.T_task_id, Task.TaskData_{
						T_sn:   list[len(list)-1].T_sn,
						T_id:   list[len(list)-1].T_id,
						T_t:    list[len(list)-1].T_t,
						T_rh:   list[len(list)-1].T_rh,
						T_time: endTime,
					})
					continue
				}
			}

		}

		for i := 0; i < len(list)-1; i++ {

			current := list[i].T_time
			next := list[i+1].T_time

			ct, _ := time.Parse("2006-01-02 15:04:05", current)
			nt, _ := time.Parse("2006-01-02 15:04:05", next)
			interval := nt.Unix() - ct.Unix()
			//logs.Debug("时间间隔：", interval, "保存时间：", saveTime)
			//fmt.Println("当前：", current, "下一个：", next)
			// 缺一个时间点 补漏
			if int(interval) == 2*T_saveT {
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: list[i].T_id + " 找到自检时间点 " + current + " ~ " + next + " 开始自检"})

				t := ct.Add(time.Second * time.Duration(T_saveT)).Format("2006-01-02 15:04") //时间临时变量
				ttt := (list[i].T_t + list[i+1].T_t) / 2
				trht := (list[i].T_rh + list[i+1].T_rh) / 2
				Task.InsertTaskData(Task_r.T_task_id, Task.TaskData_{
					T_sn:   list[i].T_sn,
					T_id:   list[i].T_id,
					T_t:    ttt,
					T_rh:   trht,
					T_time: t,
				})
				continue
			}
			// 缺的数据大于一个时间点，执行平均复制到
			if int(interval) > T_saveT {
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: list[i].T_id + " 找到自检时间点 " + current + " ~ " + next + " 开始自检"})

				// -----------开始平均复制到
				device := Device.DeviceClassList{
					T_class:  DeviceClassList_r.T_class,
					T_id:     DeviceClassList_r.T_id,
					T_sn:     DeviceClassList_r.T_sn,
					T_remark: DeviceClassList_r.T_remark,
				}
				c.SetAdjacentDeviceAVGTaskData(T_task_id, device, "", "")
				// -----------开始平均复制到结束

				// 平均复制结束，跳出循环
				break

			}
		}

	}
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

// 数据持续时间    x   分钟 没有变化
func (c *TaskDataHandleController) SSE_Continuously_unchanged_data() {

	T_task_id := c.GetString("T_task_id")     //  v26nplogbwt1
	T_timeout, _ := c.GetInt("T_timeout", 30) //  持续时间

	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}

	// 获取 备注 下面关联设备，数量
	DeviceClassList_list := Device.Read_DeviceClassList_List_id_By_Terminal(Task_r.T_class, false)

	// 选择 数据缺失的终端
	var DeleteDeviceClassList []Device.DeviceClassList
	for _, DeviceClassList_r := range DeviceClassList_list {
		TaskData_list, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, DeviceClassList_r.T_sn, DeviceClassList_r.T_id, "", "", 0, 9999)

		if len(TaskData_list) == 0 {
			continue
		}

		maxCount, start, end := Task.FindUnchangedInterval(TaskData_list)
		if strings.Contains(DeviceClassList_r.T_remark, "保温箱外环境测点") ||
			strings.Contains(DeviceClassList_r.T_remark, "冷藏库作业口外部环境测点") ||
			strings.Contains(DeviceClassList_r.T_remark, "冷藏库外部环境测点") ||
			strings.Contains(DeviceClassList_r.T_remark, "冷藏柜外部环境测点") ||
			strings.Contains(DeviceClassList_r.T_remark, "冷藏车外部环境测点") ||
			strings.Contains(DeviceClassList_r.T_remark, "仓库室外测点") {
			continue
		}
		if maxCount > T_timeout {

			Task.DeleteTaskDataByTimeRange(T_task_id, DeviceClassList_r.T_sn, DeviceClassList_r.T_id, "", "")
			DeleteDeviceClassList = append(DeleteDeviceClassList, DeviceClassList_r)

			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "找到" + DeviceClassList_r.T_id + "  探头, " +
				"最多连续 " + lib.To_string(maxCount) + " 分钟没有变化，" +
				"开始时间：" + start + " 结束时间：" + end + "," +
				"开始自检"})

		}

	}

	for _, class_ := range DeleteDeviceClassList {
		// -----------开始平均复制到
		device := Device.DeviceClassList{
			T_class:  class_.T_class,
			T_id:     class_.T_id,
			T_sn:     class_.T_sn,
			T_remark: class_.T_remark,
		}

		c.SetAdjacentDeviceAVGTaskData(T_task_id, device, "", "")
		// -----------开始平均复制到结束

	}
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

// 区间数据校正  （布点区域数据自检） 均匀性布点,产品存放区域测点 区间数据超标校正 超标数据偏移到区间内
func (c *TaskDataHandleController) SSE_Interval_data_correction() {

	T_task_id := c.GetString("T_task_id") //  v26nplogbwt1

	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}

	温度控制范围最小值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最小值")))
	温度控制范围最高值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最高值")))
	if 温度控制范围最小值 == 0 || 温度控制范围最高值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围 标签值不正确！"})
		return
	}

	// 均匀性布点   产品存放区域测点
	部点终端_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "均匀性布点|产品存放区域测点|作业出入口总测点")
	if len(部点终端_list) <= 2 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "均匀性布点|产品存放区域测点|作业出入口总测点 太少了，至少两条以上！"})
		return
	}
	部点终端_sn_list := Device.JoinDeviceClassListSnToString(部点终端_list)

	var 开始时间, 结束时间, 趋势时间 string
	开始时间, 结束时间, 趋势时间 = c.GetStartTimeAndEndTime(Task_r, 部点终端_sn_list, 温度控制范围最高值)
	if 开始时间 == "" || 结束时间 == "" {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 开始时间 或 结束时间！"})
		return
	}

	type AVGClassList struct {
		T_sn   string
		T_id   string
		T_max  float64
		T_min  float64
		T_diff float64 // 最大最小值差异
	}

	fmt.Println("数据准备:", 开始时间, 结束时间, 温度控制范围最小值, 温度控制范围最高值)

	// -------------------- 温湿度绑定点vga_H --------------------
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "--- 进行处理 数据！---"})
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("开始时间：%s 结束时间:%s", 开始时间, 结束时间)})

	// 先整体向上偏移
	for _, i2 := range 部点终端_list {
		T_min := Task.Read_TaskData_min(T_task_id, i2.T_sn, i2.T_id, "", "")
		if T_min < RoundToDecimal(温度控制范围最小值+0.1, 1) {
			Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, i2.T_sn, i2.T_id, "", "", RoundToDecimal(温度控制范围最小值+0.1-T_min, 1), 0)
		}

	}

	// ----------获取保留数据------------
	//var 开空开, 保空开 string
	//var 开空开驼峰, 保空开驼峰 Task.CalculateHumps_R
	//var valueStrings1, valueStrings2 []string
	var BWXValueStrings []string

	if Task_r.T_device_type != "X" {
		//valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰 = c.GetRetainData(Task_r, 部点终端_list, false)
	} else {
		BWXValueStrings = c.GetBWXRetainData(Task_r, 部点终端_sn_list, 结束时间, 趋势时间)
	}
	// ----------获取保留数据结束------------

	var AVGClassList_r []AVGClassList
	for _, i2 := range 部点终端_list {
		T_max := Task.Read_TaskData_max(T_task_id, i2.T_sn, i2.T_id, 开始时间, 结束时间)
		T_min := Task.Read_TaskData_min(T_task_id, i2.T_sn, i2.T_id, "", "")
		AVGClassList_r = append(AVGClassList_r, AVGClassList{T_sn: i2.T_sn, T_id: i2.T_id, T_max: T_max, T_min: T_min, T_diff: T_max - T_min})
	}

	for _, AVGClassList_i := range AVGClassList_r {
		if AVGClassList_i.T_max < 温度控制范围最高值 && AVGClassList_i.T_min > 温度控制范围最小值 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "测点 " + lib.To_string(AVGClassList_i.T_id) +
				" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
				" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
				" 符合要求！"})
			continue
		}

		var vgaca float64

		if RoundToDecimal(AVGClassList_i.T_max-AVGClassList_i.T_min, 1) > RoundToDecimal(温度控制范围最高值-温度控制范围最小值-0.4, 1) {

			// 压缩
			diff := RoundToDecimal(AVGClassList_i.T_max-AVGClassList_i.T_min, 1)
			// 获取压缩度
			compress := RoundToDecimal((温度控制范围最高值-温度控制范围最小值-0.6)/diff, 2)
			// 压缩
			Task.UpdateTaskDataTemperatureAndHumidityByGeometric(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", compress, 1)

			T_max_compress := RoundToDecimal(AVGClassList_i.T_max*compress, 1)
			T_min_compress := RoundToDecimal(AVGClassList_i.T_min*compress, 1)
			// 判断压缩后是否在 温度控制范围最小值-温度控制范围最高值范围内 不做处理
			if T_max_compress <= RoundToDecimal(温度控制范围最高值-0.1, 1) && T_min_compress >= RoundToDecimal(温度控制范围最小值+0.1, 1) {

				参考值 := RoundToDecimal((温度控制范围最高值+温度控制范围最小值)/2, 1)
				中间值 := RoundToDecimal((AVGClassList_i.T_max+AVGClassList_i.T_min)/2, 1)

				if 参考值 > 中间值 {
					// 向上偏移
					vgaca = 参考值 - 中间值
					Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", vgaca, 0)
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
						" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
						" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
						" 压缩：" + lib.To_string(compress) +
						" 压缩后最大值:" + lib.To_string(T_max_compress) + "℃  " +
						" 压缩后最小值:" + lib.To_string(T_min_compress) + "℃  " +
						" 向上偏移：" + lib.To_string(vgaca) + "℃ "})
					continue
				}

				if 参考值 < 中间值 {
					// 向下偏移
					vgaca = 中间值 - 参考值
					Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", -vgaca, 0)
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
						" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
						" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
						" 压缩：" + lib.To_string(compress) +
						" 压缩后最大值:" + lib.To_string(T_max_compress) + "℃  " +
						" 压缩后最小值:" + lib.To_string(T_min_compress) + "℃  " +
						" 向下偏移：" + lib.To_string(vgaca) + "℃ "})
					continue
				}

				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
					" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
					" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
					" 压缩：" + lib.To_string(compress) +
					" 压缩后最大值:" + lib.To_string(T_max_compress) + "℃  " +
					" 压缩后最小值:" + lib.To_string(T_min_compress) + "℃  "})
				continue
			}
			// 压缩后仍高于 温度控制范围最高值，向下偏移
			if T_max_compress >= 温度控制范围最高值 {
				vgaca = RoundToDecimal((T_max_compress+T_min_compress)/2-(温度控制范围最高值+温度控制范围最小值)/2, 1)
				//vgaca = RoundToDecimal(T_max_compress-温度控制范围最高值+0.1, 1)
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
					" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
					" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
					" 压缩：" + lib.To_string(compress) +
					" 压缩后最大值:" + lib.To_string(RoundToDecimal(AVGClassList_i.T_max*compress, 1)) + "℃  " +
					" 压缩后最小值:" + lib.To_string(RoundToDecimal(AVGClassList_i.T_min*compress, 1)) + "℃  " +
					" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
					" 向下偏移：" + lib.To_string(vgaca) + "℃ "})

				Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", -vgaca, 0)
				continue
			}
			// 压缩后仍低于 温度控制范围最小值，向上偏移
			if T_min_compress <= 温度控制范围最小值 {
				// 向上偏移
				vgaca = RoundToDecimal((温度控制范围最高值+温度控制范围最小值)/2-(T_max_compress+T_min_compress)/2, 1)

				//vgaca = RoundToDecimal(温度控制范围最小值-AVGClassList_i.T_min*compress+0.1, 1)

				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
					" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
					" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
					" 压缩：" + lib.To_string(compress) +
					" 压缩后最大值:" + lib.To_string(RoundToDecimal(AVGClassList_i.T_max*compress, 1)) + "℃  " +
					" 压缩后最小值:" + lib.To_string(RoundToDecimal(AVGClassList_i.T_min*compress, 1)) + "℃  " +
					" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
					" 向上偏移：" + lib.To_string(vgaca) + "℃ "})

				Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", vgaca, 0)

			}
			continue
		} else {
			// 向下偏移
			if AVGClassList_i.T_max >= 温度控制范围最高值 {

				vgaca = RoundToDecimal(AVGClassList_i.T_max-温度控制范围最高值+0.2, 1)
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
					" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
					" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
					" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
					" 向下偏移：" + lib.To_string(vgaca) + "℃ "})
				// 偏移
				Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", -vgaca, 0)
				continue
			}
			if AVGClassList_i.T_min <= 温度控制范围最小值 {
				vgaca = RoundToDecimal(温度控制范围最小值-AVGClassList_i.T_min+0.2, 1)
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" + lib.To_string(AVGClassList_i.T_id) +
					" 最大值:" + lib.To_string(AVGClassList_i.T_max) + "℃  " +
					" 最小值:" + lib.To_string(AVGClassList_i.T_min) + "℃  " +
					" 数据偏差： " + lib.To_string(vgaca) + "℃ " +
					" 向上偏移：" + lib.To_string(vgaca) + "℃ "})
				Task.UpdateTaskDataTemperatureAndHumidity(Task_r.T_task_id, AVGClassList_i.T_sn, AVGClassList_i.T_id, "", "", vgaca, 0)
			}
		}

	}

	// ----------恢复保留数据------------
	if Task_r.T_device_type != "X" {
		//c.SaveRetainData(Task_r, 部点终端_list, valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰, 温度控制范围最高值, 60)

	} else {
		c.SaveBWXRetainData(Task_r, 部点终端_list, BWXValueStrings, 结束时间, 趋势时间, 60)
	}
	// ----------恢复保留数据结束------------
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

// 绑定点与终端比对  （绑定点数据自检） 终端数据为参照物，绑定点数据在终端偏差±1℃，保温箱为±0.5℃
func (c *TaskDataHandleController) SSE_Comparison_between_binding_points_and_terminals() {

	T_task_id := c.GetString("T_task_id")            //  v26nplogbwt1
	T_deviation, _ := c.GetFloat("T_deviation", 1.0) //  误差绑定点数据与终端对比偏差

	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}

	// 保温箱偏差为0.5
	if Task_r.T_device_type == "X" {
		T_deviation = 0.5
	}

	// -------------------- 获取开始时间--------------------
	温度控制范围最高值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最高值")))
	if 温度控制范围最高值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围最高值 标签值不正确！"})
		return
	}
	温度控制范围最小值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最小值")))
	if 温度控制范围最小值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围最小值 标签值不正确！"})
		return
	}

	// 均匀性布点   产品存放区域测点
	部点终端_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "均匀性布点|产品存放区域测点")
	if len(部点终端_list) <= 2 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "均匀性布点，产品存放区域测点 太少了，至少两条以上！"})
		return
	}
	部点终端_sn_list := Device.JoinDeviceClassListSnToString(部点终端_list)

	var 开始时间, 结束时间, 趋势时间 string
	开始时间, 结束时间, 趋势时间 = c.GetStartTimeAndEndTime(Task_r, 部点终端_sn_list, 温度控制范围最高值)
	if 开始时间 == "" || 结束时间 == "" {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 开始时间 或 结束时间！"})
		return
	}

	// ----------获取保留数据------------
	var 开空开, 保空开 string
	var 开空开驼峰, 保空开驼峰 Task.CalculateHumps_R
	var BWXValueStrings []string
	var valueStrings1, valueStrings2 []string

	if Task_r.T_device_type != "X" {
		valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰 = c.GetRetainData(Task_r, 部点终端_list, true)
	} else {
		BWXValueStrings = c.GetBWXRetainData(Task_r, 部点终端_sn_list, 结束时间, 趋势时间)
	}
	// ----------获取保留数据结束------------

	监测终端_list := Device.Read_DeviceClassList_List_id_By_Terminal(Task_r.T_class, true)
	if len(监测终端_list) < 1 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 监测终端！"})
		return
	}
	温湿度绑定点_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "温湿度绑定点")
	if len(温湿度绑定点_list) < 1 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 温湿度绑定点！"})
		return
	}
	if len(监测终端_list) != len(温湿度绑定点_list) {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "监测终端 和 温湿度绑定点 数量不一致！"})
		return
	}
	var 监测终端01, 监测终端02, 温湿度绑定点1, 温湿度绑定点2 Device.DeviceClassList
	for _, list := range 监测终端_list {
		if strings.Contains(list.T_remark, "监测终端01") {
			监测终端01 = list
		}
		if strings.Contains(list.T_remark, "监测终端02") {
			监测终端02 = list
		}
	}
	for _, list := range 温湿度绑定点_list {
		if strings.Contains(list.T_remark, "温湿度绑定点1") {
			温湿度绑定点1 = list
		}
		if strings.Contains(list.T_remark, "温湿度绑定点2") {
			温湿度绑定点2 = list
		}
	}

	var 温湿度绑定点vga, 监测终端vga float64
	温湿度绑定点vga = Task.Read_TaskData_AVG(T_task_id, 温湿度绑定点1.T_sn, 温湿度绑定点1.T_id, 开始时间, 结束时间)
	if 温湿度绑定点vga == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点1 无数据！请先处理数据！"})
		return
	}
	监测终端vga = Task.Read_TaskData_AVG(T_task_id, 监测终端01.T_sn, 监测终端01.T_id, 开始时间, 结束时间)
	if 监测终端vga == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "监测终端01 无数据！请先处理数据！"})
		return
	}
	温湿度绑定点vga = RoundToDecimal(温湿度绑定点vga, 1)
	监测终端vga = RoundToDecimal(监测终端vga, 1)

	if len(监测终端_list) == 1 {
		// 只有一个终端，直接偏移到±T_deviation 区间内

		if (温湿度绑定点vga > 监测终端vga && 温湿度绑定点vga < 监测终端vga+T_deviation) || (温湿度绑定点vga < 监测终端vga && 温湿度绑定点vga > 监测终端vga-T_deviation) {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
				"温湿度绑定点1平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
				"监测终端01 平均值" + lib.To_string(监测终端vga) + "℃ " +
				"数据偏差： " + lib.To_string(RoundToDecimal(监测终端vga-温湿度绑定点vga, 1)) + "℃ " +
				"设置：" + lib.To_string(T_deviation) + "℃" +
				"符合要求！"})
		}
		T_max := Task.Read_TaskData_max(T_task_id, 温湿度绑定点1.T_sn, 温湿度绑定点1.T_id, 开始时间, 结束时间)
		T_min := Task.Read_TaskData_min(T_task_id, 温湿度绑定点1.T_sn, 温湿度绑定点1.T_id, 开始时间, 结束时间)

		// 平均值向下偏移
		if 温湿度绑定点vga > RoundToDecimal(监测终端vga+T_deviation, 1) {
			c.SetAverageShiftedDownward(T_task_id, 温湿度绑定点1.T_id, T_deviation, 监测终端vga, 温湿度绑定点vga, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
		}

		// 平均值向上偏移
		if 温湿度绑定点vga < RoundToDecimal(监测终端vga-T_deviation, 1) {
			c.SetAverageShiftedUpward(T_task_id, 温湿度绑定点1.T_id, T_deviation, 监测终端vga, 温湿度绑定点vga, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
		}

	} else if len(监测终端_list) == 2 {
		// 有两个终端，找出目标温湿度绑定点vga, 目标温湿度绑定点2vga最优解后偏移
		var 温湿度绑定点2vga, 监测终端2vga float64
		温湿度绑定点2vga = Task.Read_TaskData_AVG(T_task_id, 温湿度绑定点2.T_sn, 温湿度绑定点2.T_id, 开始时间, 结束时间)
		if 温湿度绑定点2vga == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点3 无数据！请先处理数据！"})
		}
		监测终端2vga = Task.Read_TaskData_AVG(T_task_id, 监测终端02.T_sn, 监测终端02.T_id, 开始时间, 结束时间)
		if 监测终端2vga == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "监测终端02 无数据！请先处理数据！"})
		}
		温湿度绑定点2vga = RoundToDecimal(温湿度绑定点2vga, 1)
		监测终端2vga = RoundToDecimal(监测终端2vga, 1)
		if math.Abs(监测终端2vga-监测终端vga) > 3 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "监测终端vga|监测终端2vga 绝对值相差超过3度，无法处理！"})
			return
		}
		// 获取监测终端和绑定点的平均值
		目标温湿度绑定点vga, 目标温湿度绑定点2vga, isExist := findBindingPointsOptimalAdjustment(监测终端vga, 监测终端2vga, 温湿度绑定点vga, 温湿度绑定点2vga, T_deviation)
		目标温湿度绑定点vga = RoundToDecimal(目标温湿度绑定点vga, 1)
		目标温湿度绑定点2vga = RoundToDecimal(目标温湿度绑定点2vga, 1)
		if !isExist {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "监测终端01 平均值" + lib.To_string(监测终端vga) + "℃ " +
				"监测终端02 平均值" + lib.To_string(监测终端2vga) + "℃ " +
				"未找到 最优 温湿度绑定点1、温湿度绑定点2 调整值！"})
			return
		}
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "监测终端01 平均值" + lib.To_string(监测终端vga) + "℃ " +
			"监测终端02 平均值" + lib.To_string(监测终端2vga) + "℃ " +
			"温湿度绑定点1 原始平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
			"温湿度绑定点2 原始平均值:" + lib.To_string(温湿度绑定点2vga) + "℃ " +
			"温湿度绑定点1 最优平均值:" + lib.To_string(目标温湿度绑定点vga) + "℃ " +
			"温湿度绑定点2 最优平均值:" + lib.To_string(目标温湿度绑定点2vga) + "℃ " +
			"设置：" + lib.To_string(T_deviation) + "℃"})
		if 目标温湿度绑定点vga != 温湿度绑定点vga {
			T_max := Task.Read_TaskData_max(T_task_id, 温湿度绑定点1.T_sn, 温湿度绑定点1.T_id, 开始时间, 结束时间)
			T_min := Task.Read_TaskData_min(T_task_id, 温湿度绑定点1.T_sn, 温湿度绑定点1.T_id, 开始时间, 结束时间)

			// 平均值向下偏移
			if 温湿度绑定点vga > RoundToDecimal(目标温湿度绑定点vga, 1) {
				c.SetAverageShiftedDownward(T_task_id, 温湿度绑定点1.T_id, 0, 目标温湿度绑定点vga, 温湿度绑定点vga, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
			}

			// 平均值向上偏移
			if 温湿度绑定点vga < RoundToDecimal(目标温湿度绑定点vga, 1) {
				c.SetAverageShiftedUpward(T_task_id, 温湿度绑定点1.T_id, 0, 目标温湿度绑定点vga, 温湿度绑定点vga, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
			}
		}

		if 目标温湿度绑定点2vga != 温湿度绑定点2vga {
			T_max := Task.Read_TaskData_max(T_task_id, 温湿度绑定点2.T_sn, 温湿度绑定点2.T_id, 开始时间, 结束时间)
			T_min := Task.Read_TaskData_min(T_task_id, 温湿度绑定点2.T_sn, 温湿度绑定点2.T_id, 开始时间, 结束时间)

			// 平均值向下偏移
			if 温湿度绑定点2vga > RoundToDecimal(目标温湿度绑定点2vga, 1) {
				c.SetAverageShiftedDownward(T_task_id, 温湿度绑定点2.T_id, 0, 目标温湿度绑定点2vga, 温湿度绑定点2vga, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
			}

			// 平均值向上偏移
			if 温湿度绑定点2vga < RoundToDecimal(目标温湿度绑定点2vga, 1) {
				c.SetAverageShiftedUpward(T_task_id, 温湿度绑定点2.T_id, 0, 目标温湿度绑定点2vga, 温湿度绑定点2vga, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
			}
		}

	}

	// ----------恢复保留数据------------
	if Task_r.T_device_type != "X" {
		c.SaveRetainData(Task_r, 部点终端_list, valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰, 温度控制范围最高值, 60)
	} else {
		c.SaveBWXRetainData(Task_r, 部点终端_list, BWXValueStrings, 结束时间, 趋势时间, 60)
	}
	// ----------恢复保留数据结束------------

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

/*
绑定点与冷热点比对  （冷热点数据自检）绑定点数据为参照物，绑定点数据在终端偏差±1℃，保温箱为±0.5℃
固定偏移一固定值，降到超限的平均复制，仍然超限再压缩
*/
func (c *TaskDataHandleController) SSE_Compare_binding_points_with_cold_and_hot_spots() {

	T_task_id := c.GetString("T_task_id")            //  v26nplogbwt1
	T_deviation, _ := c.GetFloat("T_deviation", 1.0) // 偏差时间

	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}
	// 保温箱偏差为0.5
	if Task_r.T_device_type == "X" {
		T_deviation = 0.5
	}

	温度控制范围最高值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最高值")))
	if 温度控制范围最高值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围最高值 标签值不正确！"})
		return
	}
	温度控制范围最小值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最小值")))
	if 温度控制范围最小值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围最小值 标签值不正确！"})
		return
	}

	// -------  温湿度绑定点
	温湿度绑定点1_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "温湿度绑定点1")
	if len(温湿度绑定点1_list) != 1 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点1 中找到 " + lib.To_string(len(温湿度绑定点1_list)) + "条，布点终端异常！"})
		return
	}
	温湿度绑定点2_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "温湿度绑定点2")
	if len(温湿度绑定点2_list) != 1 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "温湿度绑定点2 没找到，将 温湿度绑定点1 设置低点"})
		温湿度绑定点2_list = 温湿度绑定点1_list
	}

	// 均匀性布点   产品存放区域测点
	部点终端_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "均匀性布点|产品存放区域测点")
	if len(部点终端_list) == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "没有找到 均匀性布点，产品存放区域测点，作业出入口总测点 测点数据！"})
		return
	}
	部点终端_sn_list := Device.JoinDeviceClassListSnToString(部点终端_list)

	var 开始时间, 结束时间, 趋势时间 string
	开始时间, 结束时间, 趋势时间 = c.GetStartTimeAndEndTime(Task_r, 部点终端_sn_list, 温度控制范围最高值)
	if 开始时间 == "" || 结束时间 == "" {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 开始时间 或 结束时间！"})
		return
	}

	// ----------获取保留数据------------
	var 开空开, 保空开 string
	var 开空开驼峰, 保空开驼峰 Task.CalculateHumps_R
	var BWXValueStrings []string
	var valueStrings1, valueStrings2 []string

	if Task_r.T_device_type != "X" {
		valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰 = c.GetRetainData(Task_r, 部点终端_list, true)
	} else {
		BWXValueStrings = c.GetBWXRetainData(Task_r, 部点终端_sn_list, 结束时间, 趋势时间)
	}
	// ----------获取保留数据结束------------

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "--- 开始时间---  " + 开始时间})
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "--- 结束时间---  " + 结束时间})

	type AVGClassList struct {
		T_id  string
		T_vga float64
		T_max float64
		T_min float64
	}

	TaskData_Total_GroupBySnId := Task.Read_TaskData_Total_GroupBySnId(T_task_id, fmt.Sprintf("%s|%s", 温湿度绑定点1_list[0].T_sn, 温湿度绑定点2_list[0].T_sn), 开始时间, 结束时间)

	if len(TaskData_Total_GroupBySnId) == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点 无数据"})
		return
	}
	for _, v := range TaskData_Total_GroupBySnId {
		if v.Total == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点[" + v.T_id + "] 无数据"})
			return
		}
	}

	温湿度绑定点1vga := Task.Read_TaskData_AVG(T_task_id, 温湿度绑定点1_list[0].T_sn, 温湿度绑定点1_list[0].T_id, 开始时间, 结束时间)
	温湿度绑定点2vga := Task.Read_TaskData_AVG(T_task_id, 温湿度绑定点2_list[0].T_sn, 温湿度绑定点2_list[0].T_id, 开始时间, 结束时间)

	if 温湿度绑定点1vga == 温湿度绑定点2vga {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "温湿度绑定点1 与 温湿度绑定点2 平均值相等，将 进行上下偏移处理！"})
		温湿度绑定点1vga += T_deviation / 2
		温湿度绑定点2vga -= T_deviation / 2
		T_deviation /= 2
	}

	var 温湿度绑定点vga_H, 温湿度绑定点vga_L AVGClassList
	if 温湿度绑定点1vga > 温湿度绑定点2vga {
		温湿度绑定点vga_H.T_id = 温湿度绑定点1_list[0].T_id
		温湿度绑定点vga_H.T_vga = RoundToDecimal(温湿度绑定点1vga, 1)
		温湿度绑定点vga_L.T_id = 温湿度绑定点2_list[0].T_id
		温湿度绑定点vga_L.T_vga = RoundToDecimal(温湿度绑定点2vga, 1)
	} else {
		温湿度绑定点vga_L.T_id = 温湿度绑定点1_list[0].T_id
		温湿度绑定点vga_L.T_vga = RoundToDecimal(温湿度绑定点1vga, 1)
		温湿度绑定点vga_H.T_id = 温湿度绑定点2_list[0].T_id
		温湿度绑定点vga_H.T_vga = RoundToDecimal(温湿度绑定点2vga, 1)
	}

	fmt.Println("温湿度绑定点:", 温湿度绑定点vga_H, 温湿度绑定点vga_L)

	var Average_H_List []Task.TaskData_Average // 热点
	var Average_L_List []Task.TaskData_Average // 冷点
	snList := Device.JoinDeviceClassListSnToString(部点终端_list)
	TaskData_Average_GroupBySnId := Task.Read_TaskData_Average_GroupBySnId(Task_r.T_task_id, snList, 开始时间, 结束时间)
	if len(TaskData_Average_GroupBySnId) < 2 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "未找到 均匀性布点，产品存放区域测点 数据冷热点，平均值数据少于2条！"})
		return
	}

	for _, average := range TaskData_Average_GroupBySnId {

		if RoundToDecimal(average.Average, 1) > RoundToDecimal(温湿度绑定点vga_H.T_vga+T_deviation, 1) {
			average.Average = RoundToDecimal(average.Average, 1)
			Average_H_List = append(Average_H_List, average)
		}

		if RoundToDecimal(average.Average, 1) < RoundToDecimal(温湿度绑定点vga_L.T_vga-T_deviation, 1) {
			average.Average = RoundToDecimal(average.Average, 1)
			Average_L_List = append(Average_L_List, average)
		}
	}

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "------ 进行 高点处理 ------"})
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "------ 高点平均值 " + lib.To_string(温湿度绑定点vga_H.T_vga) + "------"})
	if len(Average_H_List) == 0 {
		Average_H := TaskData_Average_GroupBySnId[0]
		// 高点不在冷热点范围
		if RoundToDecimal(Average_H.Average, 1) < RoundToDecimal(温湿度绑定点vga_H.T_vga-T_deviation, 1) &&
			RoundToDecimal(Average_H.Average, 1) > RoundToDecimal(温湿度绑定点vga_L.T_vga+T_deviation, 1) {
			// 热点向上偏移到高点-T_deviation
			T_max := Task.Read_TaskData_max(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)
			T_min := Task.Read_TaskData_min(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)
			c.SetAverageShiftedUpward(T_task_id, Average_H.T_id, T_deviation, 温湿度绑定点vga_H.T_vga, Average_H.Average, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")

		}

	} else {
		for _, Average_H := range Average_H_List {
			// 热点向下偏移到高点范围
			T_max := Task.Read_TaskData_max(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)
			T_min := Task.Read_TaskData_min(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)
			c.SetAverageShiftedDownward(T_task_id, Average_H.T_id, T_deviation, 温湿度绑定点vga_H.T_vga, Average_H.Average, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")

		}

	}
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "------ 进行 低点处理 ------"})
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "------ 低点平均值 " + lib.To_string(温湿度绑定点vga_L.T_vga) + "------"})

	if len(Average_L_List) == 0 {
		Average_L := TaskData_Average_GroupBySnId[0]
		// 冷点不在冷热点范围
		if RoundToDecimal(Average_L.Average, 1) < RoundToDecimal(温湿度绑定点vga_L.T_vga+T_deviation, 1) &&
			RoundToDecimal(Average_L.Average, 1) < RoundToDecimal(温湿度绑定点vga_L.T_vga-T_deviation, 1) {
			// 热点向下偏移到高点范围
			T_max := Task.Read_TaskData_max(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
			T_min := Task.Read_TaskData_min(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
			c.SetAverageShiftedDownward(T_task_id, Average_L.T_id, T_deviation, 温湿度绑定点vga_H.T_vga, Average_L.Average, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")

		}

	} else {
		for _, Average_L := range Average_L_List {
			// 热点向下偏移到高点范围
			T_max := Task.Read_TaskData_max(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
			T_min := Task.Read_TaskData_min(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
			c.SetAverageShiftedUpward(T_task_id, Average_L.T_id, T_deviation, 温湿度绑定点vga_L.T_vga, Average_L.Average, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
		}

	}

	// ----------恢复保留数据------------
	if Task_r.T_device_type != "X" {
		c.SaveRetainData(Task_r, 部点终端_list, valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰, 温度控制范围最高值, 60)
	} else {
		c.SaveBWXRetainData(Task_r, 部点终端_list, BWXValueStrings, 结束时间, 趋势时间, 60)
	}
	// ----------恢复保留数据结束------------

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

// 获取相邻2个终端值平均复制到
func (c *TaskDataHandleController) SetAdjacentDeviceAVGTaskData(T_task_id string, device Device.DeviceClassList, StartTime, EndTime string) {

	DeviceClassListT_remark_r := Device.Read_DeviceClassList_List_id_T_remark(device.T_class, device.T_remark)
	// 分组统计每个sn的数据数量
	snList := Device.JoinDeviceClassListSnToString(DeviceClassListT_remark_r)
	TaskData_Total_GroupBySnId := Task.Read_TaskData_Total_GroupBySnId(T_task_id, snList, StartTime, EndTime)

	if len(TaskData_Total_GroupBySnId) < 2 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "[" + device.T_remark + "]中没有找到 至少2条 可用数据"})
		return
	}

	// 获取 备注 下面关联设备，数量
	DeviceClassListT_remark_r_list_MAX := TaskData_Total_GroupBySnId[0].Total

	TaskData_Total_GroupBySnId_Map := make(map[string]int64)
	for _, v := range TaskData_Total_GroupBySnId {
		TaskData_Total_GroupBySnId_Map[v.T_sn] = v.Total
	}

	var completeDataDeviceClassList []Device.DeviceClassList

	for _, v := range DeviceClassListT_remark_r {
		if TaskData_Total_GroupBySnId_Map[v.T_sn] == DeviceClassListT_remark_r_list_MAX || v.T_sn == device.T_sn {
			completeDataDeviceClassList = append(completeDataDeviceClassList, v)
		}
	}

	if len(completeDataDeviceClassList) < 3 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "[" + device.T_remark + "]中没有找到 至少2条 完整可用数据"})
		return
	}

	twoDevice := getBeforeAndAfter(device.T_id, completeDataDeviceClassList)
	sn1, id_str1 := twoDevice[0].T_sn, twoDevice[0].T_id
	sn2, id_str2 := twoDevice[1].T_sn, twoDevice[1].T_id
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: device.T_sn + "," + device.T_id + "开始平均复制到" +
		fmt.Sprintf("%s,%s|%s,%s", sn1, id_str1, sn2, id_str2)})

	List1, _ := Task.Read_TaskData_ById_List_AES(T_task_id, sn1, id_str1, StartTime, EndTime, 0, 9999)
	List2, _ := Task.Read_TaskData_ById_List_AES(T_task_id, sn2, id_str2, StartTime, EndTime, 0, 9999)
	num := len(List1)
	if len(List2) < len(List1) {
		num = len(List2)
	}
	if num == 0 {
		return
	}
	T_saveT := 60
	//var list []Task.TaskData_
	ct, _ := lib.TimeStrToTime(List1[0].T_time)
	var valueStrings []string
	for i := 0; i < num; i++ {
		if List1[i].T_time != List2[i].T_time {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: fmt.Sprintf("%s【%s】、%s【%s】时间不一致", List1[i].T_id, List1[i].T_time, List2[i].T_id, List2[i].T_time)})
			return

		}
		T_t := (List1[i].T_t + List2[i].T_t) / 2
		T_rh := (List1[i].T_rh + List2[i].T_rh) / 2
		valueStrings = append(valueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", device.T_sn, device.T_id, T_t, T_rh, ct.Format("2006-01-02 15:04:05")))
		ct = ct.Add(time.Second * time.Duration(T_saveT))

	}

	Task.DeleteTaskDataByTimeRange(T_task_id, device.T_sn, device.T_id, StartTime, EndTime)

	err := Task.Batch_Adds_TaskData(T_task_id, valueStrings)
	if err == nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("%d/%d", len(valueStrings), len(valueStrings))})
	}

}

func getBeforeAndAfter(T_id string, data []Device.DeviceClassList) []Device.DeviceClassList {
	var result []Device.DeviceClassList
	var index int
	for i, d := range data {
		if d.T_id == T_id {
			index = i
			break
		}
	}

	if index == 0 {
		result = append(result, data[len(data)-1], data[1])
	} else if index == len(data)-1 {
		result = append(result, data[len(data)-2], data[0])
	} else {
		result = append(result, data[index-1], data[index+1])
	}
	return result
}

func RoundToDecimal(num float64, decimal int) float64 {
	shift := math.Pow(10, float64(decimal))
	return math.Round(num*shift) / shift
}

/*
数据自检名称：平均值数据自检
测点：柜内所有测点|箱内所有测点|均匀性布点和产品存放区域测点、温湿度绑定点01、温湿度绑定点02
时间：采用“绑定点数据自检”的时间
策略：先执行完成“绑定点数据自检”后，温湿度绑定点01和温湿度绑定点02的数据就作为基准数据不变，温湿度绑定点01平均值与柜内所有测点平均值之间的差异在±0.5℃范围以内，温湿度绑定点02平均值与柜内所有测点平均值之间的差异在±0.5℃范围以内，若柜内所有测点的平均值较大或较小，则对柜内所有测点中平均值较大或较小的测点曲线进行偏移调整，使柜内所有测点的平均值靠近温湿度绑定点的平均值，从而达到绑定点与柜内所有测点的平均值差异在±0.5℃。以上“柜内所有测点”要分别换成“箱内所有测点”、“均匀性布点和产品存放区域测点”；“平均值数据自检”和“冷热点数据自检”可以同时勾选（冷库和冷车会用到）进行自检也可以单独执行（冷柜和保温箱只执行“平均值数据自检”）
*/
func (c *TaskDataHandleController) SSE_Compare_binding_points_with_Average() {

	T_task_id := c.GetString("T_task_id") //  v26nplogbwt1
	T_deviation := 0.5                    // 偏差时间

	c.Ctx.ResponseWriter.Header().Set("Content-Type", "text/event-stream")
	c.Ctx.ResponseWriter.Header().Set("Cache-Control", "no-cache")
	c.Ctx.ResponseWriter.Header().Set("Connection", "keep-alive")

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "任务开始......"})

	Task_r, err := Task.Read_Task(T_task_id)
	if err != nil {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取任务信息失败!"})
		return
	}
	// 保温箱偏差为0.5
	if Task_r.T_device_type == "X" {
		T_deviation = 0.5
	}

	温度控制范围最高值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最高值")))
	if 温度控制范围最高值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围最高值 标签值不正确！"})
		return
	}
	温度控制范围最小值 := float64(lib.To_float32(VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "温度控制范围最小值")))
	if 温度控制范围最小值 == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温度控制范围最小值 标签值不正确！"})
		return
	}

	// 均匀性布点   产品存放区域测点
	部点终端_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "均匀性布点|柜内所有测点|箱内所有测点|产品存放区域测点")
	部点终端_sn_list := Device.JoinDeviceClassListSnToString(部点终端_list)

	var 开始时间, 结束时间, 趋势时间 string
	开始时间, 结束时间, 趋势时间 = c.GetStartTimeAndEndTime(Task_r, 部点终端_sn_list, 温度控制范围最高值)
	if 开始时间 == "" || 结束时间 == "" {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 开始时间 或 结束时间！"})
		return
	}

	// ----------获取保留数据------------
	var 开空开, 保空开 string
	var 开空开驼峰, 保空开驼峰 Task.CalculateHumps_R
	var BWXValueStrings []string
	var valueStrings1, valueStrings2 []string

	if Task_r.T_device_type != "X" {
		valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰 = c.GetRetainData(Task_r, 部点终端_list, true)
	} else {
		BWXValueStrings = c.GetBWXRetainData(Task_r, 部点终端_sn_list, 结束时间, 趋势时间)
	}
	// ----------获取保留数据结束------------

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "--- 开始时间---  " + 开始时间})
	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "--- 结束时间---  " + 结束时间})

	if len(部点终端_list) == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "没有找到 柜内所有测点|箱内所有测点|均匀性布点|产品存放区域测点 测点数据！"})
		return
	}
	snList := Device.JoinDeviceClassListSnToString(部点终端_list)
	originalAvg := Task.Read_TaskData_Average(Task_r.T_task_id, snList, 开始时间, 结束时间)
	originalAvg = RoundToDecimal(originalAvg, 1)

	温湿度绑定点_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "温湿度绑定点")
	if len(温湿度绑定点_list) < 1 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 温湿度绑定点！"})
		return
	}

	var 温湿度绑定点1, 温湿度绑定点2 Device.DeviceClassList
	for _, list := range 温湿度绑定点_list {
		if strings.Contains(list.T_remark, "温湿度绑定点1") {
			温湿度绑定点1 = list
		}
		if strings.Contains(list.T_remark, "温湿度绑定点2") {
			温湿度绑定点2 = list
		}
	}
	温湿度绑定点vga := Task.Read_TaskData_AVG(T_task_id, 温湿度绑定点1.T_sn, 温湿度绑定点1.T_id, 开始时间, 结束时间)
	温湿度绑定点vga = RoundToDecimal(温湿度绑定点vga, 1)
	if 温湿度绑定点vga == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点1 无数据！请先处理数据！"})
		return
	}
	if len(温湿度绑定点_list) == 1 {

		温湿度绑定点vga = RoundToDecimal(温湿度绑定点vga, 1)
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "------ 进行 柜内所有测点|箱内所有测点|均匀性布点|产品存放区域测点 平均值处理 ------"})
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
			"温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
			"测点 平均值" + lib.To_string(originalAvg) + "℃ " +
			"数据偏差： " + lib.To_string(RoundToDecimal(math.Abs(originalAvg-温湿度绑定点vga), 1)) + "℃ " +
			"设置：" + lib.To_string(T_deviation) + "℃"})
		if (originalAvg >= 温湿度绑定点vga && originalAvg <= 温湿度绑定点vga+T_deviation) || (originalAvg <= 温湿度绑定点vga && originalAvg >= 温湿度绑定点vga-T_deviation) {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
				"温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
				"测点 平均值" + lib.To_string(originalAvg) + "℃ " +
				"数据偏差： " + lib.To_string(RoundToDecimal(math.Abs(originalAvg-温湿度绑定点vga), 1)) + "℃ " +
				"设置：" + lib.To_string(T_deviation) + "℃" +
				"符合要求！"})
		} else {
			// 平均值最低点向上偏移
			if originalAvg < 温湿度绑定点vga-T_deviation {
				k := 0
				for originalAvg < 温湿度绑定点vga-T_deviation {
					if k == len(部点终端_sn_list) {
						// 调整所有平均值都不符合要求，直接停止
						lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "调整所有测点平均值后均不符合要求！"})
						break
					}
					TaskData_Average_GroupBySnId := Task.Read_TaskData_Average_GroupBySnId(Task_r.T_task_id, snList, 开始时间, 结束时间)
					if len(TaskData_Average_GroupBySnId) == 0 {
						break
					}
					Average_L := TaskData_Average_GroupBySnId[len(TaskData_Average_GroupBySnId)-1]
					T_max := Task.Read_TaskData_max(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
					T_min := Task.Read_TaskData_min(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
					c.SetAverageShiftedUpward(T_task_id, Average_L.T_id, T_deviation, RoundToDecimal(温湿度绑定点vga-T_deviation, 1), RoundToDecimal(Average_L.Average, 1), T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
						"温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
						"测点 平均值" + lib.To_string(originalAvg) + "℃ " +
						"数据偏差： " + lib.To_string(RoundToDecimal(温湿度绑定点vga-T_deviation-originalAvg, 1)) + "℃ " +
						"设置：" + lib.To_string(T_deviation) + "℃" +
						"平均值向上偏移0.1℃"})
					originalAvg = Task.Read_TaskData_Average(Task_r.T_task_id, snList, 开始时间, 结束时间)
					originalAvg = RoundToDecimal(originalAvg, 1)
					k++

				}
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
					"温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
					"测点 平均值" + lib.To_string(originalAvg) + "℃ " +
					"数据偏差： " + lib.To_string(RoundToDecimal(温湿度绑定点vga-T_deviation-originalAvg, 1)) + "℃ " +
					"设置：" + lib.To_string(T_deviation) + "℃" +
					"符合要求！"})
			}

			// 向下偏移
			if originalAvg > 温湿度绑定点vga+T_deviation {
				l := 0
				for originalAvg > 温湿度绑定点vga+T_deviation {
					if l == len(部点终端_sn_list) {
						// 调整所有平均值都不符合要求，直接停止
						lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "调整所有测点平均值后均不符合要求！"})
						break
					}
					// 热点向下偏移到高点范围
					TaskData_Average_GroupBySnId := Task.Read_TaskData_Average_GroupBySnId(Task_r.T_task_id, snList, 开始时间, 结束时间)
					if len(TaskData_Average_GroupBySnId) == 0 {
						break
					}

					Average_H := TaskData_Average_GroupBySnId[0]
					T_max := Task.Read_TaskData_max(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)
					T_min := Task.Read_TaskData_min(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)

					c.SetAverageShiftedDownward(T_task_id, Average_H.T_id, T_deviation, RoundToDecimal(温湿度绑定点vga+T_deviation, 1), Average_H.Average, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")

					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
						"温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
						"测点 平均值" + lib.To_string(originalAvg) + "℃ " +
						"数据偏差： " + lib.To_string(RoundToDecimal(originalAvg-(温湿度绑定点vga+T_deviation), 1)) + "℃ " +
						"设置：" + lib.To_string(T_deviation) + "℃"})
					originalAvg = Task.Read_TaskData_Average(Task_r.T_task_id, snList, 开始时间, 结束时间)
					originalAvg = RoundToDecimal(originalAvg, 1)
					l++
				}
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
					"温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
					"测点 平均值" + lib.To_string(originalAvg) + "℃ " +
					"数据偏差： " + lib.To_string(RoundToDecimal(originalAvg-(温湿度绑定点vga+T_deviation), 1)) + "℃ " +
					"设置：" + lib.To_string(T_deviation) + "℃" +
					"符合要求！"})
			}

		}

	} else if len(温湿度绑定点_list) == 2 {
		温湿度绑定点2vga := Task.Read_TaskData_AVG(T_task_id, 温湿度绑定点2.T_sn, 温湿度绑定点2.T_id, 开始时间, 结束时间)
		温湿度绑定点2vga = RoundToDecimal(温湿度绑定点2vga, 1)
		if 温湿度绑定点2vga == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "温湿度绑定点2 无数据！请先处理数据！"})
			return
		}

		// 获取最优平均值
		目标originalAvg := findAverageOptimalAdjustment(温湿度绑定点vga, 温湿度绑定点2vga, originalAvg)
		目标originalAvg = RoundToDecimal(目标originalAvg, 1)

		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
			" 温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
			" 温湿度绑定点2 平均值:" + lib.To_string(温湿度绑定点2vga) + "℃ " +
			" 测点 平均值" + lib.To_string(originalAvg) + "℃ " +
			" 最优 平均值" + lib.To_string(目标originalAvg) + "℃ " +
			" 数据偏差： " + lib.To_string(RoundToDecimal(RoundToDecimal(originalAvg-目标originalAvg, 1), 1)) + "℃ " +
			" 设置：" + lib.To_string(T_deviation) + "℃"})

		if 目标originalAvg != originalAvg {
			// 平均值最低点向上偏移

			if originalAvg < 目标originalAvg {
				j := 0
				for originalAvg < 目标originalAvg {
					if j == len(部点终端_sn_list) {
						// 调整所有平均值都不符合要求，直接停止
						lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "调整所有测点平均值后均不符合要求！"})
						break
					}
					TaskData_Average_GroupBySnId := Task.Read_TaskData_Average_GroupBySnId(Task_r.T_task_id, snList, 开始时间, 结束时间)
					if len(TaskData_Average_GroupBySnId) == 0 {
						break
					}
					Average_L := TaskData_Average_GroupBySnId[len(TaskData_Average_GroupBySnId)-1]
					T_max := Task.Read_TaskData_max(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
					T_min := Task.Read_TaskData_min(T_task_id, Average_L.T_sn, Average_L.T_id, 开始时间, 结束时间)
					c.SetAverageShiftedUpward(T_task_id, Average_L.T_id, T_deviation, 目标originalAvg, RoundToDecimal(Average_L.Average, 1), T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
						" 温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
						" 温湿度绑定点2 平均值:" + lib.To_string(温湿度绑定点2vga) + "℃ " +
						" 测点 平均值" + lib.To_string(originalAvg) + "℃ " +
						" 测点 最优平均值" + lib.To_string(目标originalAvg) + "℃ " +
						" 数据偏差： " + lib.To_string(math.Abs(originalAvg-目标originalAvg)) + "℃ " +
						" 设置：" + lib.To_string(T_deviation) + "℃" +
						" 冷点：" + Average_L.T_id})
					originalAvg = Task.Read_TaskData_Average(Task_r.T_task_id, snList, 开始时间, 结束时间)
					originalAvg = RoundToDecimal(originalAvg, 1)
					j++
				}
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
					" 温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
					" 温湿度绑定点2 平均值:" + lib.To_string(温湿度绑定点2vga) + "℃ " +
					" 测点 平均值" + lib.To_string(originalAvg) + "℃ " +
					" 测点 最优平均值" + lib.To_string(目标originalAvg) + "℃ " +
					" 数据偏差： " + lib.To_string(RoundToDecimal(math.Abs(originalAvg-温湿度绑定点vga), 1)) + "℃ " +
					" 设置：" + lib.To_string(T_deviation) + "℃" +
					" 符合要求！"})

			}

			// 向下偏移

			if originalAvg > 目标originalAvg {

				i := 0
				for originalAvg > 目标originalAvg {
					if i == len(部点终端_sn_list) {
						// 调整所有平均值都不符合要求，直接停止
						lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "调整所有测点平均值后均不符合要求！"})
						break
					}
					// 热点向下偏移到高点范围
					TaskData_Average_GroupBySnId := Task.Read_TaskData_Average_GroupBySnId(Task_r.T_task_id, snList, 开始时间, 结束时间)
					if len(TaskData_Average_GroupBySnId) == 0 {
						break
					}

					Average_H := TaskData_Average_GroupBySnId[0]
					T_max := Task.Read_TaskData_max(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)
					T_min := Task.Read_TaskData_min(T_task_id, Average_H.T_sn, Average_H.T_id, 开始时间, 结束时间)

					c.SetAverageShiftedDownward(T_task_id, Average_H.T_id, T_deviation, 目标originalAvg, RoundToDecimal(Average_H.Average, 1), T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")
					lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
						" 温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
						" 温湿度绑定点2 平均值:" + lib.To_string(温湿度绑定点2vga) + "℃ " +
						" 测点 平均值" + lib.To_string(originalAvg) + "℃ " +
						" 测点 最优平均值" + lib.To_string(目标originalAvg) + "℃ " +
						" 数据偏差： " + lib.To_string(RoundToDecimal(originalAvg-目标originalAvg, 1)) + "℃ " +
						" 设置：" + lib.To_string(T_deviation) + "℃" +
						" 冷点：" + Average_H.T_id})
					originalAvg = Task.Read_TaskData_Average(Task_r.T_task_id, snList, 开始时间, 结束时间)
					originalAvg = RoundToDecimal(originalAvg, 1)
					i++
				}
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "" +
					" 温湿度绑定点1 平均值:" + lib.To_string(温湿度绑定点vga) + "℃ " +
					" 温湿度绑定点2 平均值:" + lib.To_string(温湿度绑定点2vga) + "℃ " +
					" 测点 平均值" + lib.To_string(originalAvg) + "℃ " +
					" 数据偏差： " + lib.To_string(RoundToDecimal(RoundToDecimal(originalAvg-目标originalAvg, 1), 1)) + "℃ " +
					" 设置：" + lib.To_string(T_deviation) + "℃" +
					" 符合要求！"})

			}
		}

	}

	// 温控传感器绑定点 与 柜内所有测点|箱内所有测点|均匀性布点和产品存放区域测点 对比 ±1
	T_deviation = 1
	温控传感器绑定点_list := Device.Read_DeviceClassList_List_id_T_remark(Task_r.T_class, "温控传感器绑定点1|温控传感器绑定点2")
	if len(温控传感器绑定点_list) == 0 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "没有找到温控传感器绑定点！"})
	}

	for i, classList := range 温控传感器绑定点_list {
		温控传感器绑定点OriginalAvg := Task.Read_TaskData_Average(Task_r.T_task_id, classList.T_sn, 开始时间, 结束时间)
		温控传感器绑定点OriginalAvg = RoundToDecimal(温控传感器绑定点OriginalAvg, 1)
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "------ 进行 温控传感器绑定点" + strconv.Itoa(i+1) + " 平均值处理 ------"})

		if (温控传感器绑定点OriginalAvg >= originalAvg && 温控传感器绑定点OriginalAvg <= originalAvg+T_deviation) || (温控传感器绑定点OriginalAvg <= originalAvg && 温控传感器绑定点OriginalAvg >= originalAvg-T_deviation) {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: " 温控传感器绑定点" + lib.To_string(i+1) + " 平均值:" + lib.To_string(温控传感器绑定点OriginalAvg) + "℃ " +
				" 测点平均值" + lib.To_string(originalAvg) + "℃ " +
				" 数据偏差： " + lib.To_string(RoundToDecimal(math.Abs(温控传感器绑定点OriginalAvg-originalAvg), 1)) + "℃ " +
				" 设置：" + lib.To_string(T_deviation) + "℃" +
				" 符合要求！"})
		} else {
			// 平均值最低点向上偏移
			if 温控传感器绑定点OriginalAvg < originalAvg-T_deviation {

				T_max := Task.Read_TaskData_max(T_task_id, classList.T_sn, classList.T_id, 开始时间, 结束时间)
				T_min := Task.Read_TaskData_min(T_task_id, classList.T_sn, classList.T_id, 开始时间, 结束时间)
				c.SetAverageShiftedUpward(T_task_id, classList.T_id, T_deviation, RoundToDecimal(originalAvg-T_deviation, 1), 温控传感器绑定点OriginalAvg, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")

				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "温控传感器绑定点" + lib.To_string(i+1) + "" +
					" 平均值:" + lib.To_string(温控传感器绑定点OriginalAvg) + "℃ " +
					"测点平均值" + lib.To_string(originalAvg) + "℃ " +
					"数据偏差： " + lib.To_string(RoundToDecimal(温控传感器绑定点OriginalAvg-(originalAvg-T_deviation), 1)) + "℃ " +
					"设置：" + lib.To_string(T_deviation) + "℃" +
					"温控传感器绑定点平均值向上偏移"})

				continue
			}

			// 向下偏移
			if 温控传感器绑定点OriginalAvg > originalAvg+T_deviation {

				// 热点向下偏移到高点范围
				T_max := Task.Read_TaskData_max(T_task_id, classList.T_sn, classList.T_id, 开始时间, 结束时间)
				T_min := Task.Read_TaskData_min(T_task_id, classList.T_sn, classList.T_id, 开始时间, 结束时间)

				c.SetAverageShiftedDownward(T_task_id, classList.T_id, T_deviation, RoundToDecimal(originalAvg+T_deviation, 1), 温控传感器绑定点OriginalAvg, T_min, T_max, 温度控制范围最小值, 温度控制范围最高值, "", "")

				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "温控传感器绑定点" + lib.To_string(i+1) + "" +
					" 平均值:" + lib.To_string(温控传感器绑定点OriginalAvg) + "℃ " +
					"测点平均值" + lib.To_string(originalAvg) + "℃ " +
					"数据偏差： " + lib.To_string(RoundToDecimal(温控传感器绑定点OriginalAvg-(originalAvg+T_deviation), 1)) + "℃ " +
					"设置：" + lib.To_string(T_deviation) + "℃" +
					"温控传感器绑定点平均值向下偏移"})

			}

		}
	}

	// ----------恢复保留数据------------
	if Task_r.T_device_type != "X" {
		c.SaveRetainData(Task_r, 部点终端_list, valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰, 温度控制范围最高值, 60)
	} else {
		c.SaveBWXRetainData(Task_r, 部点终端_list, BWXValueStrings, 结束时间, 趋势时间, 60)
	}
	// ----------恢复保留数据结束------------

	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 1, Msg: "完成！"})

	// Close the connection
	c.Ctx.ResponseWriter.WriteHeader(http.StatusOK)
}

/*
平均值下移
targetAvg 参考平均值
originalAvg 原始平均值
T_min 整段数据最小值
T_max 整段数据最大值
minLimit 温度控制范围最小值
maxLimit 温度控制范围最大值
*/
func (c *TaskDataHandleController) SetAverageShiftedDownward(T_task_id, T_id string, T_deviation, targetAvg, originalAvg, T_min, T_max, minLimit, maxLimit float64, StartTime, EndTime string) {

	目标avg := RoundToDecimal(targetAvg+T_deviation, 1)

	vgaca := RoundToDecimal(originalAvg-目标avg, 1)

	//向下偏移后整段数据最小值大于温度控制范围最小值
	if RoundToDecimal(T_min-vgaca, 1) > RoundToDecimal(minLimit, 1) && RoundToDecimal(T_max-vgaca, 1) < RoundToDecimal(maxLimit, 1) {
		Task.UpdateTaskDataTemperatureAndHumidityByGeometricAVG(T_task_id, T_id, "", "", -vgaca)
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" +
			" 测点 " + lib.To_string(T_id) +
			" 最大值:" + lib.To_string(T_max) + "℃  " +
			" 最小值:" + lib.To_string(T_min) + "℃  " +
			" 参考平均值:" + lib.To_string(RoundToDecimal(targetAvg, 1)) + "℃  " +
			" 原始平均值:" + lib.To_string(RoundToDecimal(originalAvg, 1)) + "℃  " +
			" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
			" 向下偏移：" + lib.To_string(vgaca) + "℃ "})
	} else {
		var err error
		var compress float64
		compress, vgaca, err = GetLinearTransformationValue(目标avg, originalAvg, T_min, T_max, minLimit, maxLimit)
		if err != nil {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "进行处理 数据！" +
				" 测点" + lib.To_string(T_id) +
				" 最大值:" + lib.To_string(T_max) + "℃  " +
				" 最小值:" + lib.To_string(T_min) + "℃  " +
				" 参考平均值:" + lib.To_string(RoundToDecimal(targetAvg, 1)) + "℃  " +
				" 原始平均值:" + lib.To_string(RoundToDecimal(originalAvg, 1)) + "℃  " +
				" 无合法解: 无法满足所有约束条件!"})
			return
		}

		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" +
			" 测点" + lib.To_string(T_id) +
			" 最大值:" + lib.To_string(T_max) + "℃  " +
			" 最小值:" + lib.To_string(T_min) + "℃  " +
			" 参考平均值:" + lib.To_string(RoundToDecimal(targetAvg, 1)) + "℃  " +
			" 原始平均值:" + lib.To_string(RoundToDecimal(originalAvg, 1)) + "℃  " +
			" 缩放：" + lib.To_string(compress) +
			" 缩放后最大值:" + lib.To_string(RoundToDecimal(T_max*compress, 1)) + "℃  " +
			" 缩放后最小值:" + lib.To_string(RoundToDecimal(T_min*compress, 1)) + "℃  " +
			" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
			" 偏移：" + lib.To_string(vgaca) + "℃ "})
		// 压缩
		Task.UpdateTaskDataTemperatureAndHumidityByGeometric_id(T_task_id, T_id, "", "", compress)
		// 偏移
		if vgaca != 0 {
			Task.UpdateTaskDataTemperatureAndHumidityByGeometricAVG(T_task_id, T_id, "", "", vgaca)
		}
	}

}

func GetLinearTransformationValue(targetAvg, originalAvg, T_min, T_max, minLimit, maxLimit float64) (float64, float64, error) {

	//var vgaca float64
	//compress1 := RoundToDecimal((minLimit+0.3-targetAvg)/(T_min-originalAvg), 1)
	//compress2 := RoundToDecimal((maxLimit-0.3-targetAvg)/(T_max-originalAvg), 1)
	//compress := math.Min(compress1, compress2)
	//if RoundToDecimal(T_max*compress, 1) < RoundToDecimal(maxLimit, 1) && RoundToDecimal(T_min*compress, 1) > RoundToDecimal(minLimit, 1) {
	//	vgaca = 0
	//} else {
	//	vgaca = RoundToDecimal(targetAvg-compress*originalAvg, 1)
	//}
	//for _, x := range []float64{T_min, T_max} {
	//	newVal := RoundToDecimal(compress*x+vgaca, 1)
	//	if newVal < minLimit || newVal > maxLimit {
	//		return 0, 0, errors.New("无合法解: 无法满足所有约束条件")
	//	}
	//}
	//
	//return compress, vgaca, nil

	minLimit = RoundToDecimal(minLimit+0.2, 1)
	maxLimit = RoundToDecimal(maxLimit-0.2, 1)
	// 策略1: 使用最小边界值 (使变换后最小值 = minBound)
	k1 := (targetAvg - minLimit) / (originalAvg - T_min)
	b1 := minLimit - k1*T_min
	valid1 := true
	for _, x := range []float64{T_min, T_max} {
		newVal := RoundToDecimal(k1*x+b1, 1)
		if newVal < minLimit || newVal > maxLimit {
			valid1 = false
			break
		}
	}

	// 策略2: 使用最大边界值 (使变换后最大值 = maxBound)，策略1失败时使用
	k2 := float64(0)
	b2 := float64(0)
	valid2 := false
	if !valid1 {
		k2 = (maxLimit - targetAvg) / (T_max - originalAvg)
		b2 = targetAvg - k2*originalAvg
		valid2 = true
		for _, x := range []float64{T_min, T_max} {
			newVal := RoundToDecimal(k2*x+b2, 1)
			if newVal < minLimit || newVal > maxLimit {
				valid2 = false
				break
			}
		}
	}

	// 根据结果选择有效策略
	switch {
	case valid1:
		return k1, b1, nil
	case valid2:
		return k2, b2, nil
	default:
		return 0, 0, errors.New("无合法解: 无法满足所有约束条件")
	}
}

/*
平均值上移
targetAvg 参考平均值
originalAvg 原始平均值
T_min 整段数据最小值
T_max 整段数据最大值
minLimit 温度控制范围最小值
maxLimit 温度控制范围最大值
*/
func (c *TaskDataHandleController) SetAverageShiftedUpward(T_task_id, T_id string, T_deviation, targetAvg, originalAvg, T_min, T_max, minLimit, maxLimit float64, StartTime, EndTime string) {
	目标avg := RoundToDecimal(targetAvg-T_deviation, 1)
	vgaca := RoundToDecimal(目标avg-originalAvg, 1)

	if RoundToDecimal(T_max+vgaca, 1) < RoundToDecimal(maxLimit, 1) && RoundToDecimal(T_min+vgaca, 1) > RoundToDecimal(minLimit, 1) {
		Task.UpdateTaskDataTemperatureAndHumidityByGeometricAVG(T_task_id, T_id, "", "", vgaca)
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" +
			" 测点 " + lib.To_string(T_id) +
			" 最大值:" + lib.To_string(T_max) + "℃  " +
			" 最小值:" + lib.To_string(T_min) + "℃  " +
			" 参考平均值:" + lib.To_string(RoundToDecimal(targetAvg, 1)) + "℃  " +
			" 原始平均值:" + lib.To_string(RoundToDecimal(originalAvg, 1)) + "℃  " +
			" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
			" 向上偏移：" + lib.To_string(vgaca) + "℃ "})
	} else {
		var err error
		var compress float64
		compress, vgaca, err = GetLinearTransformationValue(目标avg, originalAvg, T_min, T_max, minLimit, maxLimit)
		if err != nil {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "进行处理 数据！" +
				" 测点" + lib.To_string(T_id) +
				" 最大值:" + lib.To_string(T_max) + "℃  " +
				" 最小值:" + lib.To_string(T_min) + "℃  " +
				" 参考平均值:" + lib.To_string(RoundToDecimal(targetAvg, 1)) + "℃  " +
				" 原始平均值:" + lib.To_string(RoundToDecimal(originalAvg, 1)) + "℃  " +
				" 无合法解: 无法满足所有约束条件!"})
			return
		}

		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "进行处理 数据！" +
			" 测点" + lib.To_string(T_id) +
			" 最大值:" + lib.To_string(T_max) + "℃  " +
			" 最小值:" + lib.To_string(T_min) + "℃  " +
			" 参考平均值:" + lib.To_string(RoundToDecimal(targetAvg, 1)) + "℃  " +
			" 原始平均值:" + lib.To_string(RoundToDecimal(originalAvg, 1)) + "℃  " +
			" 缩放：" + lib.To_string(compress) +
			" 缩放后最大值:" + lib.To_string(RoundToDecimal(T_max*compress, 1)) + "℃  " +
			" 缩放后最小值:" + lib.To_string(RoundToDecimal(T_min*compress, 1)) + "℃  " +
			" 数据偏差：" + lib.To_string(vgaca) + "℃ " +
			" 偏移：" + lib.To_string(vgaca) + "℃ "})
		// 压缩
		Task.UpdateTaskDataTemperatureAndHumidityByGeometric_id(T_task_id, T_id, "", "", compress)
		// 偏移
		if vgaca != 0 {
			Task.UpdateTaskDataTemperatureAndHumidityByGeometricAVG(T_task_id, T_id, "", "", vgaca)
		}
	}

}

func (c *TaskDataHandleController) GetCalculateHumps(T_task_id string, SN_list []Device.DeviceClassList, startTime, types string) (calculateHumps Task.CalculateHumps_R) {
	SN := Device.JoinDeviceClassListSnToString(SN_list)
	var is bool
	calculateHumps, is = Task.Redis_CalculateHumps_Get(T_task_id + types)
	if !is {
		list := Task.Read_TaskData_ById_AVG(T_task_id, SN, startTime, "")
		// 获取第一个驼峰结束时间点
		CalculateHumps_list := Task.CalculateHumpsByThreeDots(list)
		if len(CalculateHumps_list) < 1 {
			return
		}
		calculateHumps = CalculateHumps_list[0]
		Task.Redis_CalculateHumps_Set(T_task_id+types, calculateHumps)
	}
	return calculateHumps

}

func (c *TaskDataHandleController) GetMetadata(T_task_id string, SN_list []Device.DeviceClassList, startTime, types string) (valueStrings []string, calculateHumps Task.CalculateHumps_R) {
	SN := Device.JoinDeviceClassListSnToString(SN_list)
	var is bool
	calculateHumps, is = Task.Redis_CalculateHumps_Get(T_task_id + types)
	if !is {
		return
	}

	endTime := calculateHumps.End.T_time

	data1, _ := Task.Read_TaskData_ById_List_AES(T_task_id, SN, "", startTime, endTime, 0, 9999)
	for _, v := range data1 {
		valueStrings = append(valueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", v.T_sn, v.T_id, v.T_t, v.T_rh, v.T_time))
	}

	return valueStrings, calculateHumps

}

func (c *TaskDataHandleController) GetCalculateHumpsMaps(T_task_id string, SN_list []Device.DeviceClassList, startTime string) map[string]Task.CalculateHumps_R {
	var CalculateHumpsMaps = make(map[string]Task.CalculateHumps_R)

	SN := Device.JoinDeviceClassListSnToString(SN_list)
	allList := Task.Read_TaskData_ById_AVG(T_task_id, SN, startTime, "")
	// 获取第一个驼峰结束时间点
	CalculateHumps_list := Task.CalculateHumpsByThreeDots(allList)
	if len(CalculateHumps_list) < 1 {
		return CalculateHumpsMaps
	}

	for _, device := range SN_list {
		list := Task.Read_TaskData_ById_AVG(T_task_id, device.T_sn, startTime, "")
		//获取第一个驼峰结束时间点
		CalculateHumps := Task.CalculateHumpsByThreeDots(list)

		if len(CalculateHumps) < 1 {
			CalculateHumpsMaps[device.T_sn] = CalculateHumps_list[0]
			continue
		}
		if CalculateHumps[0].Peak.T_time != CalculateHumps_list[0].Peak.T_time {
			CalculateHumps[0].Peak.T_time = CalculateHumps_list[0].Peak.T_time
		}
		et1, _ := lib.TimeStrToTime(CalculateHumps_list[0].End.T_time)
		et2, _ := lib.TimeStrToTime(CalculateHumps[0].End.T_time)
		if et1.Before(et2) {
			CalculateHumps[0].End.T_time = CalculateHumps_list[0].End.T_time
		}
		CalculateHumpsMaps[device.T_sn] = CalculateHumps[0]
	}

	return CalculateHumpsMaps
}

// 获取保留数据
func (c *TaskDataHandleController) GetRetainData(Task_r Task.Task, SN_list []Device.DeviceClassList, useCache bool) (valueStrings1, valueStrings2 []string,
	kkkStartTime, bkkStartTime string, kkkCalculateHumps, bkkCalculateHumps Task.CalculateHumps_R) {
	var 开空开, 保空开 string
	var 开空开驼峰, 保空开驼峰 Task.CalculateHumps_R
	if Task_r.T_device_type != "X" {
		开空开 = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "开空开")
		if len(开空开) == 0 {
			开空开 = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "开满开")
		}
		if len(开空开) == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 开空开/开满开 时间 标签！"})
			return
		}
		//保空开 = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "保空开")
		//if len(保空开) == 0 {
		//	保空开 = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "保满开")
		//}
		//if len(保空开) == 0 {
		//	lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "未找到 保空开/保满开 时间 标签！"})
		//	return
		//}
		//if !useCache {
		//	开空开驼峰 = c.GetCalculateHumps(Task_r.T_task_id, SN_list, 开空开, "kkk")
		//	保空开驼峰 = c.GetCalculateHumps(Task_r.T_task_id, SN_list, 保空开, "bkk")
		//}
		//
		//valueStrings1, 开空开驼峰 = c.GetMetadata(Task_r.T_task_id, SN_list, 开空开, "kkk")
		//valueStrings2, 保空开驼峰 = c.GetMetadata(Task_r.T_task_id, SN_list, 保空开, "bkk")

		SN := Device.JoinDeviceClassListSnToString(SN_list)
		data1, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, SN, "", 开空开, "", 0, 9999)
		for _, v := range data1 {
			valueStrings1 = append(valueStrings1, fmt.Sprintf("('%s','%s',%v,%v,'%s')", v.T_sn, v.T_id, v.T_t, v.T_rh, v.T_time))
		}

	}
	return valueStrings1, valueStrings2, 开空开, 保空开, 开空开驼峰, 保空开驼峰
}

// 获取保留数据
func (c *TaskDataHandleController) SaveRetainData(Task_r Task.Task, SN_list []Device.DeviceClassList, valueStrings1, valueStrings2 []string,
	kkkStartTime, bkkStartTime string, kkkCalculateHumps, bkkCalculateHumps Task.CalculateHumps_R, maxLimit float64, saveTime int) {
	var err error
	if len(valueStrings1) > 0 {

		//将开空开/开满开时间开始第一个驼峰 写入原始数据
		//将 开空开/开满开时间 后时间写入原始数据
		SN := Device.JoinDeviceClassListSnToString(SN_list)
		Task.DeleteTaskAllDataByTimeRange(Task_r.T_task_id, SN, kkkStartTime, kkkCalculateHumps.End.T_time)
		err = Task.Batch_Adds_TaskData(Task_r.T_task_id, valueStrings1)
		if err == nil {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("恢复 开空开/开满开 时间点后驼峰 %s ~ %s 数据 %d/%d", kkkStartTime, kkkCalculateHumps.End.T_time, len(valueStrings1), len(valueStrings1))})
		}
	}
	if len(valueStrings2) > 0 {
		//将开空开/开满开时间开始第一个驼峰 写入原始数据
		SN := Device.JoinDeviceClassListSnToString(SN_list)
		Task.DeleteTaskAllDataByTimeRange(Task_r.T_task_id, SN, bkkStartTime, bkkCalculateHumps.End.T_time)
		err = Task.Batch_Adds_TaskData(Task_r.T_task_id, valueStrings2)
		if err == nil {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("恢复 保空开/保满开 时间点后驼峰 %s ~ %s 数据 %d/%d", kkkStartTime, kkkCalculateHumps.End.T_time, len(valueStrings1), len(valueStrings1))})
		}
	}

	//c.SaveRetainDataAndTrend(Task_r, SN_list, valueStrings1, kkkStartTime, kkkCalculateHumps, maxLimit, saveTime)
	//c.SaveRetainDataAndTrend(Task_r, SN_list, valueStrings2, bkkStartTime, bkkCalculateHumps, maxLimit, saveTime)

}

// 获取保留数据
func (c *TaskDataHandleController) SaveRetainDataTrend(Task_r Task.Task, SN_list []Device.DeviceClassList, valueStrings1, valueStrings2 []string,
	kkkStartTime, bkkStartTime string, kkkCalculateHumps, bkkCalculateHumps Task.CalculateHumps_R, maxLimit float64, saveTime int) {
	c.SaveRetainDataAndTrend(Task_r, SN_list, valueStrings1, kkkStartTime, kkkCalculateHumps, maxLimit, saveTime)
	c.SaveRetainDataAndTrend(Task_r, SN_list, valueStrings2, bkkStartTime, bkkCalculateHumps, maxLimit, saveTime)
}

func (c *TaskDataHandleController) SaveRetainDataAndTrend(Task_r Task.Task, SN_list []Device.DeviceClassList, valueStrings1 []string,
	kkkStartTime string, kkkCalculateHumps Task.CalculateHumps_R, maxLimit float64, saveTime int) {
	var err error
	if len(valueStrings1) > 0 {
		//将开空开/开满开时间开始第一个驼峰 写入原始数据
		SN := Device.JoinDeviceClassListSnToString(SN_list)
		Task.DeleteTaskAllDataByTimeRange(Task_r.T_task_id, SN, kkkStartTime, kkkCalculateHumps.End.T_time)
		err = Task.Batch_Adds_TaskData(Task_r.T_task_id, valueStrings1)
		if err == nil {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("恢复 开空开/开满开 保空开/保满开 时间点后驼峰 %s ~ %s 数据 %d/%d", kkkStartTime, kkkCalculateHumps.End.T_time, len(valueStrings1), len(valueStrings1))})
		}

		startTimeT, _ := lib.TimeStrToTime(kkkStartTime)
		startTime := startTimeT.Add(-time.Minute).Format("2006-01-02 15:04")
		endTimeT, _ := lib.TimeStrToTime(kkkCalculateHumps.End.T_time)
		endTime := endTimeT.Add(time.Minute).Format("2006-01-02 15:04")
		// 执行数据平滑
		for _, v := range SN_list {

			sn := v.T_sn
			id_str := v.T_id
			//AllList, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, sn, id_str, startTime, kkkCalculateHumps.End.T_time, 0, 9999)
			var trendTime, declineTrendTime string
			trendTime = kkkCalculateHumps.Peak.T_time
			declineTrendTime = kkkCalculateHumps.Peak.T_time

			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "执行数据上升趋势"})

			if len(trendTime) > 0 {
				list, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, sn, id_str, startTime, trendTime, 0, 9999)
				if len(list) <= 2 {
					continue
				}
				first := list[0]
				var last Task.TaskData_
				if len(list) > 10 {
					last = list[len(list)-2]
				} else {
					last = list[len(list)-1]
				}

				current, _ := time.Parse("2006-01-02 15:04", first.T_time)
				next, _ := time.Parse("2006-01-02 15:04", last.T_time)
				interval := next.Sub(current).Seconds() / float64(saveTime)

				//ttInterval := (last.T_t - first.T_t) / float32(interval)
				trhInterval := (last.T_rh - first.T_rh) / float32(interval)
				ttList := generateRisingCurve(float64(first.T_t), float64(last.T_t), int(interval+1))
				//tt := first.T_t
				ttrh := first.T_rh

				var valueStrings []string
				for i := 0; i <= int(interval); i++ {
					//tt += ttInterval
					ttrh += trhInterval
					ttime := current.Format("2006-01-02 15:04")
					valueStrings = append(valueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", first.T_sn, id_str, ttList[i], ttrh, ttime))
					current = current.Add(time.Second * time.Duration(saveTime))
				}

				//for current.Unix() <= next.Unix() {
				//
				//	tt += ttInterval
				//	ttrh += trhInterval
				//	ttime := current.Format("2006-01-02 15:04")
				//	valueStrings = append(valueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", first.T_sn, id_str, tt, ttrh, ttime))
				//	current = current.Add(time.Second * time.Duration(saveTime))
				//}
				Task.DeleteTaskDataByTimeRange(Task_r.T_task_id, sn, id_str, first.T_time, last.T_time)

				Task.Batch_Adds_TaskData(Task_r.T_task_id, valueStrings)
			}

			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: "执行数据下降趋势"})
			if len(declineTrendTime) > 0 {
				list, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, sn, id_str, declineTrendTime, endTime, 0, 9999)
				if len(list) <= 2 {
					continue
				}
				if list[len(list)-1].T_t > list[len(list)-2].T_t {
					continue
				}

				var first Task.TaskData_
				if len(list) > 10 {
					first = list[1]
				} else {
					first = list[0]
				}

				last := list[len(list)-1]

				current, _ := time.Parse("2006-01-02 15:04", first.T_time)
				next, _ := time.Parse("2006-01-02 15:04", last.T_time)
				interval := next.Sub(current).Seconds() / float64(saveTime)

				//ttInterval := (last.T_t - first.T_t) / float32(interval)
				trhInterval := (last.T_rh - first.T_rh) / float32(interval)

				ttList := generateTemperatureCurve(float64(first.T_t), float64(last.T_t), int(interval+1))

				//tt := first.T_t
				ttrh := first.T_rh
				var valueStrings []string
				for i := 0; i <= int(interval); i++ {
					//tt += ttInterval
					ttrh += trhInterval
					ttime := current.Format("2006-01-02 15:04")
					valueStrings = append(valueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", first.T_sn, id_str, ttList[i], ttrh, ttime))
					current = current.Add(time.Second * time.Duration(saveTime))
				}

				Task.DeleteTaskDataByTimeRange(Task_r.T_task_id, sn, id_str, first.T_time, last.T_time)
				Task.Batch_Adds_TaskData(Task_r.T_task_id, valueStrings)

			}
		}

	}
}

// 获取保留数据
func (c *TaskDataHandleController) GetBWXRetainData(Task_r Task.Task, SN string, endTime, trendTime string) (BWXValueStrings []string) {
	if endTime != trendTime {
		data1, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, SN, "", trendTime, "", 0, 9999)
		for _, v := range data1 {
			BWXValueStrings = append(BWXValueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", v.T_sn, v.T_id, v.T_t, v.T_rh, v.T_time))
		}
	}
	return
}

func (c *TaskDataHandleController) SaveBWXRetainData(Task_r Task.Task, SN_list []Device.DeviceClassList,
	BWXValueStrings []string, endTime, trendTime string, saveTime int) {
	var err error
	if endTime != trendTime && len(BWXValueStrings) > 0 {
		//将开空开/开满开时间开始第一个驼峰 写入原始数据
		for _, device := range SN_list {
			Task.DeleteTaskDataByTimeRange(Task_r.T_task_id, device.T_sn, device.T_id, trendTime, "")

		}
		err = Task.Batch_Adds_TaskData(Task_r.T_task_id, BWXValueStrings)
		if err == nil {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("恢复 保温箱 原始数据 %d/%d", len(BWXValueStrings), len(BWXValueStrings))})
		}

		var startTime string
		startTime = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "静态开箱作业开箱结束时间")
		if len(startTime) == 0 {
			startTime = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "动态开箱作业开箱结束时间")
		}
		if len(startTime) == 0 {
			startTime = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "开箱作业开箱结束时间")
		}
		if len(startTime) == 0 {
			lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "获取 静态开箱作业开箱结束时间|动态开箱作业开箱结束时间|开箱作业开箱结束时间 失败"})
			return
		}
		startTimeT, _ := lib.TimeStrToTime(startTime)
		startTime = startTimeT.Add(30 * time.Minute).Format("2006-01-02 15:04")
		// 执行数据平滑
		for _, v := range SN_list {
			sn := v.T_sn
			id_str := v.T_id
			list, _ := Task.Read_TaskData_ById_List_AES(Task_r.T_task_id, sn, id_str, startTime, trendTime, 0, 9999)
			first := list[0]
			last := list[len(list)-1]

			current, _ := time.Parse("2006-01-02 15:04", first.T_time)
			next, _ := time.Parse("2006-01-02 15:04", last.T_time)
			interval := next.Sub(current).Seconds() / float64(saveTime)
			ttInterval := (last.T_t - first.T_t) / float32(interval)
			trhInterval := (last.T_rh - first.T_rh) / float32(interval)
			tt := first.T_t
			ttrh := first.T_rh

			var valueStrings []string
			for current.Unix() <= next.Unix() {
				tt += ttInterval
				ttrh += trhInterval
				ttime := current.Format("2006-01-02 15:04")
				valueStrings = append(valueStrings, fmt.Sprintf("('%s','%s',%v,%v,'%s')", first.T_sn, id_str, tt, ttrh, ttime))
				current = current.Add(time.Second * time.Duration(saveTime))
			}
			Task.DeleteTaskDataByTimeRange(Task_r.T_task_id, sn, id_str, startTime, trendTime)

			err = Task.Batch_Adds_TaskData(Task_r.T_task_id, valueStrings)
			if err == nil {
				lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 0, Msg: fmt.Sprintf("保温箱数据趋势 %d/%d", len(valueStrings), len(valueStrings))})

			}
		}
	}
}

// 获取开始结束时间
func (c *TaskDataHandleController) GetStartTimeAndEndTime(Task_r Task.Task, SN string, maxLimit float64) (startTime, endTime, trendTime string) {

	// 开始时间 获取温度下降到第二个低点时间
	// 1. 获取温度平均值
	list := Task.Read_TaskData_ById_AVG(Task_r.T_task_id, SN, "", "")
	if len(list) < 2 {
		lib.SseWriteJSON(c.Ctx.ResponseWriter, lib.JSONSSE{State: 2, Msg: "均匀性布点+产品存放区域测点+作业出入口总测点 数据平均值 少于2条!"})
		return
	}
	// 找平均值低于温度控制范围最高值的第二个最低点
	lowPoint := 0
	for i := 1; i <= len(list)-2; i++ {
		if list[i].T_t < list[i-1].T_t && list[i].T_t < list[i+1].T_t && list[i].T_t < float32(maxLimit) {
			lowPoint += 1
		}

		if lowPoint == 2 {
			startTime = list[i].T_time
			break
		}

	}

	// 保温箱没有开空开，开满开
	// 结束时间温度超限取没有超限时间点，结束时间温度未超限取最后一个时间点
	if Task_r.T_device_type == "X" {
		list2 := Task.Read_TaskData_ById_AVG_DESC(Task_r.T_task_id, SN, "", "")
		if len(list) == 0 {
			return
		}
		if float64(list2[0].T_t) < maxLimit {
			endTime = list2[0].T_time
			trendTime = list2[0].T_time
			return
		}

		for i, avg := range list2 {
			if RoundToDecimal(float64(avg.T_t), 1) < maxLimit {
				endTime = avg.T_time
				trendTime = list2[i-1].T_time
				break
			}
		}
	} else {
		endTime = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "开空开")
		if len(endTime) == 0 {
			endTime = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "开满开")
		}
		if len(endTime) == 0 {
			endTime = VerifyTemplate.Read_VerifyTemplateMapData_T_name(Task_r.T_task_id, Task_r.T_VerifyTemplate_id, "现场测试结束时间")
		}
		if len(endTime) == 0 {
			return
		}
	}

	return

}

/*
计算绑定点的值
A 监测终端01平均值
B 监测终端02平均值
C 温湿度绑定点01平均值
D 温湿度绑定点02平均值
*/
func findBindingPointsOptimalAdjustment(A, B, C, D, T_deviation float64) (float64, float64, bool) {

	type DataPair struct {
		A1 float64
		B1 float64
	}
	T_deviation_half := 0.1
	var dataPairs []DataPair

	var A1, B1 float64 = -100, -100

	// 生成A1和B1的可能值
	for a1 := A - T_deviation; a1 <= A+T_deviation; a1 += T_deviation_half {
		for b1 := B - T_deviation; b1 <= B+T_deviation; b1 += T_deviation_half {
			// 检查是否存在满足条件的C值
			minC := math.Max(a1-T_deviation_half, b1-T_deviation_half)
			maxC := math.Min(a1+T_deviation_half, b1+T_deviation_half)

			if minC <= maxC {
				dataPairs = append(dataPairs, DataPair{A1: a1, B1: b1})
			}
		}
	}

	if len(dataPairs) == 1 {
		A1 = dataPairs[0].A1
		B1 = dataPairs[0].B1
	}
	if len(dataPairs) > 1 {
		// 寻找最优解
		minAdjustment := math.MaxFloat64
		var optimalPair DataPair

		for _, pair := range dataPairs {
			adjustment := math.Abs(pair.A1-C) + math.Abs(pair.B1-D)
			if adjustment < minAdjustment {
				minAdjustment = adjustment
				optimalPair = pair
			}
		}
		A1 = optimalPair.A1
		B1 = optimalPair.B1
	}
	if A1 == -100 || B1 == -100 {
		return A1, B1, false
	}
	return A1, B1, true
}

/*
计算绑定点的值
A 温湿度绑定点1平均值
B 温湿度绑定点2平均值
C 测点平均值
*/
func findAverageOptimalAdjustment(A, B, C float64) float64 {

	// 计算有效区间
	minA := A - 0.5
	maxA := A + 0.5
	minB := B - 0.5
	maxB := B + 0.5

	// 求交集范围
	lowerBound := math.Max(minA, minB)
	upperBound := math.Min(maxA, maxB)

	// 确定最优调整值
	var optimalC float64
	if C < lowerBound {
		optimalC = lowerBound // 取区间下限
	} else if C > upperBound {
		optimalC = upperBound // 取区间上限
	} else {
		optimalC = C // 已在区间内无需调整
	}
	return optimalC
}

func generateTemperatureCurve(startTemp, minTemp float64, steps int) []float64 {
	if steps <= 0 {
		return []float64{}
	}

	// 计算动态参数确保严格递减且高于最低温度
	base := minTemp + 0.1 // 确保所有值高于minTemp
	decayFactor := math.Log((startTemp-base)/(minTemp+0.5-base)) / float64(steps-1)

	temperatures := make([]float64, steps)
	prevTemp := startTemp

	for i := 0; i < steps; i++ {
		// 使用指数衰减模型确保严格单调递减
		temp := base + (startTemp-base)*math.Exp(-decayFactor*float64(i))

		// 确保严格递减且高于最低温度
		if temp >= prevTemp {
			temp = prevTemp - 0.1
		}
		if temp <= minTemp {
			temp = minTemp + 0.01 + 0.05*float64(steps-i)/float64(steps)
		}

		// 保持精度并确保唯一性
		temp = math.Round(temp*100) / 100
		temperatures[i] = temp
		prevTemp = temp
	}

	return temperatures
}

func generateRisingCurve(minTemp, maxTemp float64, steps int) []float64 {
	if steps <= 0 {
		return []float64{}
	}

	// 计算动态参数确保严格递增且低于最高温度
	growthFactor := math.Log((maxTemp-minTemp-0.1)/0.1) / float64(steps-1)

	temperatures := make([]float64, steps)
	prevTemp := minTemp

	for i := 0; i < steps; i++ {
		// 使用指数增长模型确保严格单调递增
		temp := minTemp + (maxTemp-minTemp)*(1-math.Exp(-growthFactor*float64(i)))

		// 确保严格递增且低于最高温度
		if temp <= prevTemp {
			temp = prevTemp + 0.1
		}
		if temp >= maxTemp {
			temp = maxTemp - 0.01 - 0.05*float64(steps-i-1)/float64(steps)
		}

		// 保持精度并确保唯一性
		temp = math.Round(temp*100) / 100
		temperatures[i] = temp
		prevTemp = temp
	}

	return temperatures
}
