package controllers import ( "ColdP_server/controllers/lib" "ColdP_server/logs" "ColdP_server/models/Company" "ColdP_server/models/Device" "encoding/json" "fmt" "github.com/beego/beego/v2/adapter/orm" beego "github.com/beego/beego/v2/server/web" "github.com/xuri/excelize/v2" "io" "log" "math/rand" "strconv" "strings" "time" ) type DataGeneratorController struct { beego.Controller } // GeneratorHtml 获取页面 func (c *DataGeneratorController) GeneratorHtml() { b_, admin := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } classList := Company.Read_CompanyClass_All(admin.T_pid, "") c.Data["Class_List"] = classList //确认状态为登录状态后 c.TplName = "Data/GeneratorData2.html" } // DeviceSensorData 获取对应设备探头数据 func (c *DataGeneratorController) DeviceSensorData() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } startTime := c.GetString("startTime") endTime := c.GetString("endTime") sns := make([][]string, 0) json.Unmarshal([]byte(c.GetString("sns")), &sns) type Temp struct { Sn []string `json:"sn"` Data []Device.DeviceSensorData `json:"data"` } datas := make([]Temp, 0) for _, sn := range sns { //sn = [sn,探头id] id, _ := strconv.ParseInt(sn[1], 10, 63) v, err := Device.ReadDeviceSensorByTsnTidTimeRange(sn[0], int(id), startTime, endTime) if err != nil { c.Data["json"] = lib.JSONS{Code: 500, Msg: "读取设备探头错误!"} c.ServeJSON() return } datas = append(datas, Temp{sn, v}) } c.Data["json"] = lib.JSONS{Code: 200, Msg: "获取数据成功", Data: datas} c.ServeJSON() } // UpdateFix 更新固定值 func (c *DataGeneratorController) UpdateFix() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } //1.解析数据 var body = c.Ctx.Request.Body defer body.Close() type T struct { FixTemperature string `json:"fixTemperature"` FixHumidity string `json:"fixHumidity"` Sns [][2]string `json:"sns"` Data []int64 `json:"data"` } var temp = T{} bytes, _ := io.ReadAll(body) json.Unmarshal(bytes, &temp) fmt.Println("解析后:", temp.Data[0]) //2.得到数据进行统一设置访问修改 humidity, _ := strconv.ParseFloat(temp.FixHumidity, 64) temperature, _ := strconv.ParseFloat(temp.FixTemperature, 64) //开始时间到结束时间 startTime := time.UnixMilli(temp.Data[0]).Format("2006-01-02 15:04:05") endTime := time.UnixMilli(temp.Data[1]).Format("2006-01-02 15:04:05") //3.循环更新数据 for _, v := range temp.Sns { sn := v[0] tId := v[1] Device.UpdateDeviceSensorDataTemperatureAndHumidity(sn, tId, startTime, endTime, temperature, humidity) } //4.反馈成功 c.Data["json"] = lib.JSONS{200, "调整固定偏移值成功!", nil} c.ServeJSON() } // Delete 删除 func (c *DataGeneratorController) Delete() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } //1.解析数据 var body = c.Ctx.Request.Body defer body.Close() type T struct { Sns [][2]string `json:"sns"` Data []int64 `json:"data"` } var temp = T{} bytes, _ := io.ReadAll(body) json.Unmarshal(bytes, &temp) fmt.Println("解析后:", temp.Data[0]) //开始时间到结束时间 startTime := time.UnixMilli(temp.Data[0]).Format("2006-01-02 15:04:05") endTime := time.UnixMilli(temp.Data[1]).Format("2006-01-02 15:04:05") //3.循环更新数据 for _, v := range temp.Sns { sn := v[0] tId := v[1] Device.DeleteDeviceSensorDataByTimeRange(sn, tId, startTime, endTime) } //4.反馈成功 c.Data["json"] = lib.JSONS{200, "调整固定偏移值成功!", nil} c.ServeJSON() } // UpdateRand 更新随机值 func (c *DataGeneratorController) UpdateRand() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } //解析请求参数 body := c.Ctx.Request.Body defer body.Close() bytes, _ := io.ReadAll(body) type T struct { TemperatureMin int `json:"temperatureMin"` TemperatureMax int `json:"temperatureMax"` HumidityMax int `json:"humidityMax"` HumidityMin int `json:"humidityMin"` Sns [][2]string `json:"sns"` Data []int64 `json:"data"` } var t = T{} json.Unmarshal(bytes, &t) fmt.Println("requestJSON: ", t, "原始json:", string(bytes)) //开始时间到结束时间 startTime := time.UnixMilli(t.Data[0]).Format("2006-01-02 15:04:05") endTime := time.UnixMilli(t.Data[1]).Format("2006-01-02 15:04:05") for _, v := range t.Sns { sn := v[0] tId := v[1] Device.UpdateDeviceSensorDataTemperatureAndHumidityRandom(sn, tId, startTime, endTime, t.TemperatureMax, t.TemperatureMin, t.HumidityMax, t.HumidityMin) } //反馈成功 c.Data["json"] = lib.JSONS{200, "调整随机偏移值成功!", nil} c.ServeJSON() return } // CopyFromPosition 数据拷贝 func (c *DataGeneratorController) CopyFromPosition() { b_, admin := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } var body = c.Ctx.Request.Body defer body.Close() bytes, _ := io.ReadAll(body) type T struct { CopyPosition string `json:"copyPosition"` Sns [][2]string `json:"sns"` Data []int64 `json:"data"` } t := T{} json.Unmarshal(bytes, &t) //开始时间 和结束时间,插入的时间范围 fmt.Println(t) copyTime, _ := time.Parse("2006-01-02 15:04:05", t.CopyPosition) startTime := time.UnixMilli(t.Data[0]).Format("2006-01-02 15:04:05") endTime := time.UnixMilli(t.Data[1]).Format("2006-01-02 15:04:05") for _, v := range t.Sns { sn := v[0] tId := v[1] list := Device.SelectDeviceSensorDataListByTimeRange(sn, tId, startTime, endTime) saveTime := Device.ReadDeviceParameterByTsn(sn).T_saveT ct := copyTime go func(list []Device.DeviceSensorData, sn string, saveTime int) { for _, d := range list { d.T_time = ct.Format("2006-01-02 15:04:05") d.CreateTime = ct.Format("2006-01-02 15:04:05") Device.InsertDeviceSensorData(sn, d, admin) ct = ct.Add(time.Second * time.Duration(saveTime)) } }(list, sn, saveTime) } c.Data["json"] = lib.JSONS{200, "数据复制已提交后台处理!", nil} c.ServeJSON() return } // RepairSensorData 数据补漏 func (c *DataGeneratorController) RepairSensorData() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } sns := make([][2]string, 0) timeRange := make([]int64, 0) json.Unmarshal([]byte(c.GetString("sns")), &sns) json.Unmarshal([]byte(c.GetString("data")), &timeRange) start := time.UnixMilli(timeRange[0]).Format("2006-01-02 15:04:05") end := time.UnixMilli(timeRange[1]).Format("2006-01-02 15:04:05") num := 0 for _, v := range sns { listdevices, lists := Listdevices(v[0], v[1], start, end) num = normal(listdevices, lists.T_save_t, lists.T_warn, lists.Sn, lists.T_tlower, lists.T_tupper, lists.T_r_hlower, lists.T_r_hupper) } //for _, v := range sns { // sn := v[0] // tId := v[1] // saveTime := Device.ReadDeviceParameterByTsn(sn).T_saveT // list := Device.SelectDeviceSensorDataListByTimeRange(sn, tId, start, end) // // 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 +0800 CST", current) // //format := cts.Format("2006-01-02 15:04:05") // //ct, _ := time.Parse("2006-01-02 15:04:05", format) // // n, _ := time.Parse("2006-01-02 15:04:05 +0800 CST", next) // //nsfmt := ns.Format("2006-01-02 15:04:05") // //n, _ := time.Parse("2006-01-02 15:04:05", nsfmt) // interval := n.Unix() - ct.Unix() // logs.Debug("时间间隔:", interval) // fmt.Println("当前:", current, "下一个:", next) // if int(interval) > saveTime { // ttInterval := list[i+1].T_t - list[i].T_t // ttt := list[i].T_t // 温度临时变量 // trhInterval := list[i+1].T_rh - list[i].T_rh // trht := list[i].T_rh //湿度临时变量 // count := int(interval) / saveTime // num += count // for k := 0; k < count; k++ { // t := ct.Format("2006-01-02 15:04:05") //时间临时变量 // ttt += ttInterval / float64(count) // trht += trhInterval / float64(count) // Device.InsertDeviceSensorData(sn, Device.DeviceSensorData{ // list[i].T_id, // list[i].T_sp, // t, // lib.Decimal(ttt), // lib.Decimal(trht), // list[i].T_site, // list[i].CreateTime, // }, admin) // ct = ct.Add(time.Second * time.Duration(saveTime)) // } // } // } // //} c.Data["json"] = lib.JSONS{200, fmt.Sprintf("补漏完成!共补漏%d条数据", num), nil} c.ServeJSON() return } func Listdevices(sn, t_id, startTime, endTime string) ([]Device.DeviceData, Device.DeviceLists) { var deviceslist Device.DeviceLists sql := fmt.Sprintf("SELECT * FROM z_device_data_%s WHERE t_id=%s and t_time BETWEEN '%s' AND '%s';", sn, t_id, startTime, endTime) deviceslist.Sn = sn var devices []Device.DeviceData o := orm.NewOrm() queryRows, err := o.Raw(sql).QueryRows(&devices) if err != nil { log.Fatal(queryRows, err) return devices, deviceslist } sqls := fmt.Sprintf("SELECT t_save_t,t_warn FROM device_parameter WHERE t_sn='%s' ORDER BY update_time DESC LIMIT 1", sn) deviceSensorParameter := fmt.Sprintf("SELECT t__tlower,t__tupper,t__r_hlower,t__r_hupper FROM device_sensor_parameter WHERE t_sn='%s' and t__state=1 and t_id = '%s' ORDER BY update_time DESC LIMIT 1", sn, t_id) var save_t string var t_warn string err = o.Raw(sqls).QueryRow(&save_t, &t_warn) deviceslist.T_save_t = save_t deviceslist.T_warn = t_warn if err != nil { log.Fatal(err) return devices, deviceslist } err = o.Raw(deviceSensorParameter).QueryRow(&deviceslist.T_tlower, &deviceslist.T_tupper, &deviceslist.T_r_hlower, &deviceslist.T_r_hupper) if err != nil { log.Fatal(err) return devices, deviceslist } return devices, deviceslist } // 正常情况下补漏数据 func normal(devices []Device.DeviceData, t_save_t, t_warn, sn string, t_tlower, t_tupper, t_r_hlower, t_r_hupper float64) int { num := 0 timeFormat := "2006-01-02 15:04:05 -0700 MST" Format := "2006-01-02 15:04:05" floatSave, _ := strconv.ParseFloat(t_save_t, 64) newOrm := orm.NewOrm() for i := 0; i < len(devices)-1; i++ { t1, err := time.Parse(timeFormat, devices[i].T_time) if err != nil { log.Println("解析时间失败:", err) continue } t2, err := time.Parse(timeFormat, devices[i+1].T_time) if err != nil { log.Println("解析时间失败:", err) continue } timeDiff := t2.Sub(t1).Seconds() if timeDiff > floatSave { fmt.Printf("时间差大于系统设置时间:%s 和 %s\n", devices[i].T_time, devices[i+1].T_time) numInserts := int(timeDiff / floatSave) for j := 1; j <= numInserts-1; j++ { t := t1.Truncate(time.Minute) newTime := t.Add(time.Duration(j*int(floatSave)) * time.Second) T_T, T_Rh := IsNotWarn(devices, t_tlower, t_tupper, t_r_hlower, t_r_hupper) devi := Device.DeviceData{ T_id: devices[i].T_id, T_sp: devices[i].T_sp, T_time: newTime.Format(Format), T_t: T_T, T_rh: T_Rh, T_site: devices[i].T_site, Create_time: newTime.Format(Format), } insertSql := fmt.Sprintf("INSERT INTO z_device_data_%s (t_id, t_sp, t_time, t_t, t_rh, t_site, create_time) VALUES (?, ?, ?, ?, ?, ?, ?)", sn) log.Println(insertSql) _, err := newOrm.Raw(insertSql, devi.T_id, devi.T_sp, devi.T_time, devi.T_t, devi.T_rh, devi.T_site.String, devi.Create_time).Exec() if err != nil { log.Println("插入新时间点失败:", err) return 0 } num++ log.Println("sn:", sn, "补漏数据:", devi, " 插入时间点:", newTime.Format(timeFormat)) } } } return num } // 判断是否在预警范围内,并且返回在预警内的值 func IsNotWarn(deviceDatas []Device.DeviceData, t_tlower, t_tupper, t_r_hlower, t_r_hupper float64) (T_T, T_Rh float64) { for i, _ := range deviceDatas { if !(deviceDatas[i].T_t < t_tlower || deviceDatas[i].T_t > t_tupper || deviceDatas[i].T_rh < t_r_hlower || deviceDatas[i].T_rh > t_r_hupper) { T_T = deviceDatas[i].T_t + rand.Float64()*0.4 - 0.2 T_Rh = deviceDatas[i].T_rh + rand.Float64()*0.4 - 0.2 return T_T, T_Rh } } return 0, 0 } func (c *DataGeneratorController) RepairAllSensorData() { b_, admin := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { // 用户验证逻辑省略 } o := orm.NewOrm() deviceList := []Device.Device{} var err error _, err = o.QueryTable(new(Device.Device)). Filter("T_model__in", []string{"BX200GSE", "MD100", "MD200G", "BX100", "KF100", "MD300G"}). Filter("T_state", 1). //Filter("T_pid", 80). Filter("T_sn", "kf861693223539284"). All(&deviceList) if err != nil { logs.Error("获取设备列表失败", err) return } start := "2024.05.14 00:00:00" end := time.Now().Format("2006-01-02 15:04:05") // 初始化Excel文件 f := excelize.NewFile() // 创建工作表 sheetName := "RepairLog" f.NewSheet(sheetName) // 设置列标题 f.SetCellValue(sheetName, "A1", "设备序列号") f.SetCellValue(sheetName, "B1", "传感器ID") f.SetCellValue(sheetName, "C1", "补漏起始时间") f.SetCellValue(sheetName, "D1", "补漏结束时间") f.SetCellValue(sheetName, "E1", "补漏数据量") row := 2 // 从第二行开始记录数据 totalRows := 0 // 总的补漏条数 for _, device := range deviceList { deviceSensorList := []Device.DeviceSensor{} _, err = o.QueryTable(new(Device.DeviceSensor)).Filter("T_sn", device.T_sn).All(&deviceSensorList) if err != nil { logs.Error(lib.FuncName(), err) continue } num := map[int]int{} // 用于存储每个传感器ID补漏的数量 startTime := map[int]string{} // 用于存储每个传感器ID补漏的起始时间 endTime := map[int]string{} // 用于存储每个传感器ID补漏的结束时间 for _, v := range deviceSensorList { sn := v.T_sn itoa := strconv.Itoa(v.T_id) saveTime := Device.ReadDeviceParameterByTsn(sn).T_saveT list := Device.SelectDeviceSensorDataListByTimeRange(sn, itoa, start, end) // 确定每个传感器ID补漏的起始时间和结束时间 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 +0800 CST", current) n, _ := time.Parse("2006-01-02 15:04:05 +0800 CST", next) interval := n.Unix() - ct.Unix() if int(interval) > saveTime { ttInterval := list[i+1].T_t - list[i].T_t ttt := list[i].T_t // 温度临时变量 trhInterval := list[i+1].T_rh - list[i].T_rh trht := list[i].T_rh //湿度临时变量 count := int(interval) / saveTime num[v.T_id] += count for k := 0; k < count; k++ { t := ct.Add(time.Duration(k) * time.Duration(saveTime) * time.Second).Format("2006-01-02 15:04:05") //时间临时变量 ttt += ttInterval / float64(count) trht += trhInterval / float64(count) Device.InsertDeviceSensorData(sn, Device.DeviceSensorData{ list[i].T_id, list[i].T_sp, t, lib.Decimal(ttt), lib.Decimal(trht), list[i].T_site, list[i].CreateTime, }, admin) logs.Println(t) } // 更新起始时间和结束时间 if _, ok := startTime[v.T_id]; !ok { startTime[v.T_id] = ct.Format("2006-01-02 15:04:05") } endTime[v.T_id] = ct.Add(time.Duration(count-1) * time.Duration(saveTime) * time.Second).Format("2006-01-02 15:04:05") } } } // 记录每个设备及其每个传感器ID的数据 for sensorID, count := range num { if count > 0 { // 只记录补漏数量大于0的数据 f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), device.T_sn) f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), sensorID) f.SetCellValue(sheetName, fmt.Sprintf("C%d", row), startTime[sensorID]) f.SetCellValue(sheetName, fmt.Sprintf("D%d", row), endTime[sensorID]) f.SetCellValue(sheetName, fmt.Sprintf("E%d", row), count) row++ totalRows += count } } //time.Sleep(time.Second * 1) } // 在最后一行记录总的补漏条数 f.SetCellValue(sheetName, fmt.Sprintf("A%d", row), "总补漏条数") f.SetCellValue(sheetName, fmt.Sprintf("B%d", row), totalRows) // 保存Excel文件 if err := f.SaveAs("repair_log.xlsx"); err != nil { logs.Error("保存Excel文件失败", err) return } c.Data["json"] = lib.JSONS{200, fmt.Sprintf("所有设备补漏完成!"), nil} c.ServeJSON() return } // DataSensorDataSmooth 数据平滑 func (c *DataGeneratorController) DataSensorDataSmooth() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } var ( sns [][2]string timeRange []int64 tRange float64 hRange float64 ) json.Unmarshal([]byte(c.GetString("sns")), &sns) json.Unmarshal([]byte(c.GetString("data")), &timeRange) tRange, _ = c.GetFloat("tRange") hRange, _ = c.GetFloat("hRange") start := time.UnixMilli(timeRange[0]).Format("2006-01-02 15:04:05") end := time.UnixMilli(timeRange[1]).Format("2006-01-02 15:04:05") fmt.Println(tRange, hRange) var count int for _, v := range sns { sn := v[0] id := v[1] list := Device.SelectDeviceSensorDataListByTimeRange(sn, id, start, end) for i := 1; i < len(list); i++ { n := list[i-1] old := list[i] newO := list[i] //变化差 var tInterval = old.T_t - n.T_t var hInterval = old.T_rh - n.T_rh fmt.Println("温度:", n.T_t, "温度next:", old.T_t, "差值:", n.T_t-old.T_t) if tRange != 0 { if tInterval > tRange { newO.T_t = n.T_t + tRange } else if tInterval < -tRange { newO.T_t = n.T_t - tRange } } if hRange != 0 { if hInterval > hRange { newO.T_rh = n.T_rh + hRange } else if hInterval < -hRange { newO.T_rh = n.T_rh - hRange } } if old != newO { fmt.Println("原始数据:", old, "新数据:", newO) list[i] = newO newO.T_t = lib.Decimal(newO.T_t) newO.T_rh = lib.Decimal(newO.T_rh) Device.UpdateDeviceSensorData(sn, id, old, newO) count++ } } } c.Data["json"] = lib.JSONS{200, "操作成功处理" + fmt.Sprint(count) + "条数据", nil} c.ServeJSON() } // DataSensorDataTrend 数据趋势 func (c *DataGeneratorController) DataSensorDataTrend() { b_, _ := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } //获取数据 timeRange := make([]int64, 0) sns := make([][2]string, 0) json.Unmarshal([]byte(c.GetString("data")), &timeRange) json.Unmarshal([]byte(c.GetString("sns")), &sns) start := time.UnixMilli(timeRange[0]).Format("2006-01-02 15:04:05") end := time.UnixMilli(timeRange[1]).Format("2006-01-02 15:04:05") for _, v := range sns { sn := v[0] tId := v[1] list := Device.SelectDeviceSensorDataListByTimeRange(sn, tId, start, end) first := list[0] last := list[len(list)-1] ttInterval := (last.T_t - first.T_t) / float64(len(list)-2) trhInterval := (last.T_rh - first.T_rh) / float64(len(list)-2) if len(list) < 3 { continue } for i, d := range list[1 : len(list)-1] { old := list[i] if ttInterval != 0 { d.T_t = list[0].T_t + float64(i+1)*ttInterval } if trhInterval != 0 { d.T_rh = list[0].T_rh + float64(i+1)*trhInterval } if d != old { d.T_t = lib.Decimal(d.T_t) d.T_rh = lib.Decimal(d.T_rh) Device.UpdateDeviceSensorData(sn, tId, old, d) } } } c.Data["json"] = lib.JSONS{200, "设置平滑操作成功", nil} c.ServeJSON() return } // ImportSensorData 导入数据 func (c *DataGeneratorController) ImportSensorData() { b_, admin := lib.Verification(c.Ctx.GetCookie("User_tokey"), c.GetString("User_tokey")) if !b_ { c.Data["json"] = lib.JSONS{Code: 201, Msg: "User_tokey Err!"} c.ServeJSON() return } //读取文件 file, _, err := c.GetFile("file") if err != nil { fmt.Println("读取form文件失败", err.Error()) } //解析文件 read, err := excelize.OpenReader(file) if err != nil { fmt.Println("解析错误:", err.Error()) } rows, err := read.GetRows("data") if err != nil { fmt.Println("解析excel错误", err.Error()) } values := make([][]string, 0) for _, row := range rows[1:] { //t_id t_sp t_time t_t t_rh t_site create_time values = append(values, row) } //添加操作 sns := strings.Split(c.GetString("sn"), "|") for _, v := range sns { for _, ev := range values { temperature, _ := strconv.ParseFloat(ev[4], 64) humidty, _ := strconv.ParseFloat(ev[5], 64) temperature = lib.Decimal(temperature) humidty = lib.Decimal(humidty) data := Device.ToSensorData(strings.Split(v, ",")[1], ev[1], ev[2], ev[3], temperature, humidty, ev[6]) Device.InsertDeviceSensorData(strings.Split(v, ",")[0], data, admin) } } c.Data["json"] = lib.JSONS{200, "导入数据成功!", nil} c.ServeJSON() return }