func()

in sort/pid_controller.go [269:386]


func (p *PIDController) Compute(itemOrExpId string, ctx *context.RecommendContext) (float64, float64) {
	if !p.online {
		ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] offline",
			p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name))
		return 0, 0
	}
	var status = p.getPIDStatus(itemOrExpId)
	status.mu.Lock()
	defer status.mu.Unlock()

	isPercentageTask := p.task.ControlType == constants.TrafficControlTaskControlTypePercent
	measure := status.GetMeasurement()
	if isPercentageTask && measure > 1.0 {
		ctx.LogError(fmt.Sprintf("module=PIDController\tinvalid traffic percentage <taskId:%s/targetId:%s>[targetName:%s] value=%f",
			p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name, measure))
		return 0, 0
	}
	if measure == 0 && !p.runWithZeroInput {
		ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] input value is 0",
			p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name))
		return 0, 0
	}

	var setValue float64
	if isPercentageTask || time.Now().Sub(status.lastTime) < time.Duration(30)*time.Second {
		setValue = status.setValue
	} else {
		value, enabled := p.getTargetSetValue()
		if !enabled {
			ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] disable",
				p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name))
			return 0, value
		}
		if value < 1 {
			setValue = 1
		} else {
			setValue = value
		}
	}

	if p.task.ControlLogic == constants.TrafficControlTaskControlLogicGuaranteed {
		// 调控类型为保量,并且当前时刻目标已达成的情况下,直接返回 0
		if isPercentageTask {
			if measure >= (setValue / 100) {
				ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] item_or_exp=%s, measure=%.6f, achieved",
					p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name, itemOrExpId, measure))
				return 0, setValue
			}
		} else {
			if measure >= setValue {
				ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] item_or_exp=%s, measure=%.2f, achieved",
					p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name, itemOrExpId, measure))
				return 0, setValue
			}
		}
	}
	// 处理死控区域
	if isPercentageTask {
		delta := float64(p.target.ToleranceValue) / 100
		if delta == 0 { // 添加死区控制:微小误差时不调整
			delta = 0.001
		}
		if BetweenSlackInterval(measure, setValue/100, delta) {
			// when current input is between `setValue` and `setValue+SetVale Range`, turn off controller
			ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] item_or_exp=%s, between slack interval",
				p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name, itemOrExpId))
			return 0, setValue
		}
	} else {
		delta := float64(p.target.ToleranceValue)
		if delta == 0 { // 添加死区控制:微小误差时不调整
			delta = setValue * 0.01
		}
		if BetweenSlackInterval(measure, setValue, delta) {
			ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] item_or_exp=%s, between slack interval",
				p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name, itemOrExpId))
			return 0, setValue
		}
	}

	if p.freezeMinutes > 0 {
		// 流量占比型任务凌晨刚开始的时候流量占比统计值不置信,直接输出前一天最后一次的调控信号
		location, err := time.LoadLocation("Asia/Shanghai") // 北京采用Asia/Shanghai时区
		if err != nil {                                     // 如果无法加载时区,默认使用本地时区
			location = time.Local
		}
		// 获取当前时间
		now := time.Now().In(location)
		// 获取当天的零点时间
		startOfDay := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, location)
		// 计算当前时间与零点的差值
		durationSinceStartOfDay := int(now.Sub(startOfDay).Minutes())
		if durationSinceStartOfDay < p.freezeMinutes {
			ctx.LogDebug(fmt.Sprintf("module=PIDController\titemIdOrExpId=%s\texit within the freezing time", itemOrExpId))
			return status.lastOutput, setValue
		}
	}

	var currentError float64
	if isPercentageTask {
		currentError = setValue/100.0 - measure
	} else {
		currentError = 1.0 - measure/setValue
	}

	pTerm := p.kp * currentError
	dTerm := p.kd * status.derivative
	var iTerm float64
	if status.integralActive { // 积分项(根据激活状态决定是否使用)
		iTerm = p.ki * status.integral
	}
	status.lastOutput = pTerm + iTerm + dTerm
	if status.lastOutput == 0 {
		ctx.LogDebug(fmt.Sprintf("module=PIDController\t<taskId:%s/targetId:%s>[targetName:%s] item_or_exp=%s, measure=%f, err=%.6f, output=0",
			p.task.TrafficControlTaskId, p.target.TrafficControlTargetId, p.target.Name, itemOrExpId, measure, currentError))
	}
	return status.lastOutput, setValue
}