package processor import ( "github.com/shirou/gopsutil/cpu" "strconv" "path/filepath" "fmt" "errors" "time" "sync" "path" "amuz.es/src/infra/cpu_ctrl/logger" "io/ioutil" "strings" "amuz.es/src/infra/cpu_ctrl/pid" "amuz.es/src/infra/cpu_ctrl/util" ) type processor struct { handler util.Handler id int tempeturePath string tempeture float64 tempetureChanged chan TempetureInfo sampleDuration time.Duration fanController pid.Controller fanSpeed int fanMaxSpeed int fanMinSpeed int fanSpeedChanged chan FanspeedInfo } type TempetureInfo struct { Id int Tempeture float64 } type FanspeedInfo struct { Id int FanSpeed int } type TempetureChanged = <-chan TempetureInfo type FanspeedChanged = <-chan FanspeedInfo type Processor interface { Id() int Tempeture() float64 FanSpeed() int FanMaxSpeed() int FanMinSpeed() int StartMonitoring() TempetureNotifier() TempetureChanged FanSpeedNotifier() FanspeedChanged getMaxTempetureOnProcessor(string) (float64) readTempeture(string, chan<- float64, *sync.WaitGroup) normalizeFanspeed(float64) (int) } var ( log = logger.NewLogger("processor") ) func GetProcessorCount() (maxcpu int) { stat, err := cpu.Info() if err != nil { panic(err) } for _, info := range stat { physicalId, err := strconv.Atoi(info.PhysicalID) if err != nil { return } else if maxcpu < physicalId { maxcpu = physicalId } } maxcpu += 1 return } func NewProcessorInfo( handler util.Handler, processorId int, sampleDuration time.Duration, P, I, D, setPoint float64, maxNoob, minNoob int, ) (Processor, error) { if matches, err := filepath.Glob(fmt.Sprintf("/sys/devices/platform/coretemp.%d/hwmon/hwmon?", processorId)); err != nil { return nil, err } else if matches == nil { return nil, errors.New("hwmon not found!") } else { windupGuard := float64(maxNoob - minNoob) controller := pid.New(P, I, D) controller.SetSetPoint(setPoint) controller.SetSampleTime(sampleDuration) controller.SetWindupGuard(windupGuard) return &processor{ handler: handler, id: processorId, tempeturePath: matches[0], tempetureChanged: make(chan TempetureInfo), sampleDuration: sampleDuration, fanController: controller, fanSpeedChanged: make(chan FanspeedInfo), fanMaxSpeed: maxNoob, fanMinSpeed: minNoob, }, nil } } func (p *processor) Id() int { return p.id } func (p *processor) Tempeture() float64 { return p.tempeture } func (p *processor) FanSpeed() int { return p.fanSpeed } func (p *processor) FanMaxSpeed() int { return p.fanMaxSpeed } func (p *processor) FanMinSpeed() int { return p.fanMinSpeed } func (p *processor) TempetureNotifier() TempetureChanged { return p.tempetureChanged } func (p *processor) FanSpeedNotifier() FanspeedChanged { return p.fanSpeedChanged } func (p *processor) StartMonitoring() { p.handler.IncreaseWait() defer p.handler.DecreaseWait() defer func() { if err := recover(); err != nil { p.handler.NotifyError(err.(error)) } }() defer close(p.tempetureChanged) defer close(p.fanSpeedChanged) tempeturePathGlob := path.Join(p.tempeturePath, "temp?_input") ticker := time.Tick(p.sampleDuration) log.Infof("Processor %d monitor started with %s", p.id, p.sampleDuration) for { select { case <-ticker: var ( highestTemp float64 fanspeed int ) highestTemp = p.getMaxTempetureOnProcessor(tempeturePathGlob) if highestTemp != p.tempeture { p.tempetureChanged <- TempetureInfo{ Id: p.id, Tempeture: highestTemp, } p.tempeture = highestTemp } fanspeed = p.normalizeFanspeed(p.fanController.Update(highestTemp)) if fanspeed != p.fanSpeed { p.fanSpeedChanged <- FanspeedInfo{ Id:p.id, FanSpeed:fanspeed, } p.fanSpeed = fanspeed } case <-p.handler.Done(): return } } } func (p *processor) getMaxTempetureOnProcessor(tempeturePathGlob string) (maxTempeture float64) { matches, err := filepath.Glob(tempeturePathGlob) if err != nil { panic(err) } tempetureReadWaiter := &sync.WaitGroup{} queue := make(chan float64, len(matches)) defer close(queue) // exclude package temp for _, tempetureFilePath := range matches[1:] { tempetureReadWaiter.Add(1) go p.readTempeture(tempetureFilePath, queue, tempetureReadWaiter) } tempetureReadWaiter.Wait() for sense := range queue { if maxTempeture < sense { maxTempeture = sense } } return } func (p *processor) readTempeture(path string, senseChan chan<- float64, waiter *sync.WaitGroup) { defer waiter.Done() if dat, err := ioutil.ReadFile(path); err != nil { p.handler.NotifyError(err) } else if tempetureSense, err := strconv.Atoi(strings.TrimSpace(string(dat))); err != nil { p.handler.NotifyError(err) } else { senseChan <- float64(tempetureSense) / 1000.0 } } func (p *processor) normalizeFanspeed(response float64) (adjusted int) { adjusted = int(-response) if adjusted < p.fanMinSpeed { adjusted = p.fanMinSpeed } else if adjusted > p.fanMaxSpeed { adjusted = p.fanMaxSpeed } return }