Скрипт контроля частот и вентиляторов видеокарт AMD Radeon: различия между версиями

Материал из Linux Wiki
Перейти к навигацииПерейти к поиску
 
Строка 2: Строка 2:


{{ToDo|Дописать}}
{{ToDo|Дописать}}
{{ToDo|В setfreqs $i лишняя. Убрать и/или переделать на возможность указания определенной карты}}


----
----

Текущая версия на 11:50, 11 июля 2016

Данный скрипт предназначен для управления частотами и вентиляторами в ригах на базе видеокарт AMD Radeon. Помимо управления, скрипт выводит текущее состояние карт. Если доступно API sgminer (или подобного майнера с такой же структурой вывода данных), то используются его данные и управление отдается ему, а скрипт просто выводит статистику.

ToDo: Дописать

#!/bin/bash
# By Rain ;)

###################################

# IDLE ZONE - [hyst]-[tt1]-[hyst] - ZONE 1 - [hyst]-[tt2]-[hyst] - ZONE 2 - [hyst]-[toh] - OVERHEAT ZONE [Cut-off] - CUT-OFF ZONE
# For example, for 65 / 70 / 80 / 85 and hysteresis level = 1; and fan min=15; target=60 and max=100:
#  .............. |  64-66   | ............... |  69-71   | .............. |  79-80   | ..........................  | 85 .................
#        idle     |          |      zone1      |          |     zone2      |          |           overheat          |       cutoff
# fan down to 15  | do noth. |  fan up to 60   | do noth. | fan up to 100  | do noth. | fan to 100; clock step down | halt all; fan to 100
#
# For idle zone fan decremental step is 1%
# For zone 1 fan incremental step is 2%
# For zone 2 fan incremental step is 5%
#
# If temperature goes lower than "$toh-(double hysteresis)" - <=77 C - core clock will be restored to config values
#
# TODO: looks like, no need to use hysteresis in between zone2 and overheat zones anymore

###################################

tt1='65' # First target temperature. Fan will be between fmin and tfan
tt2='70' # Second target temperatune. Fan will be up to fmax
toh='80' # Overheat temperature. Reducing frequencies, fan to fmax
tco='85' # Cut-off temperature. Shutting down all mining processes
hyst='1' # Hysteresis

fmin='15' # Minimal fan speed
tfan='60' # Maximum fan speed for first zone
fmax='100' # Maximum fan speed

ohfs='5' # Overheat frequency reducing step
fups='50' # Clock restore step
trst='75' # Clock restore temperature

sleep='5' # internal timer

corelow='300'
memlow='1000' # not used

conf='/home/rain/config/cards.conf' # cgminer/sgminer config file for getting core/mem frequencies
rrdb='/home/rain/.gpustat.rrd'
site='/tmp/site'

###################################


if [ "${1}" != 'single' ]
	then

mkdir -p -m 755 "${site}"

if [ -z "${RRD}" ]
	then
		if [ -x '/usr/bin/rrdtool' ]
			then
			rrd='nice -n 19 /usr/bin/rrdtool'
		fi
	else
		rrd=': '
fi

[ ! -e "${rrdb}" ] && ${rrd} create "${rrdb}" \
--step 60 \
DS:gpu0temp:GAUGE:120:U:U \
DS:gpu1temp:GAUGE:120:U:U \
DS:gpu2temp:GAUGE:120:U:U \
DS:gpu3temp:GAUGE:120:U:U \
DS:gpu4temp:GAUGE:120:U:U \
DS:gpu5temp:GAUGE:120:U:U \
DS:gpu6temp:GAUGE:120:U:U \
DS:gpu7temp:GAUGE:120:U:U \
DS:gpu8temp:GAUGE:120:U:U \
DS:gpu9temp:GAUGE:120:U:U \
RRA:MIN:0.5:1:80  \
RRA:MIN:0.5:8:210  \
RRA:MIN:0.5:80:600  \
RRA:MIN:0.5:600:1100 \
RRA:AVERAGE:0.5:1:80  \
RRA:AVERAGE:0.5:8:210  \
RRA:AVERAGE:0.5:80:600  \
RRA:AVERAGE:0.5:600:1100 \
RRA:MAX:0.5:1:80 \
RRA:MAX:0.5:8:210 \
RRA:MAX:0.5:80:600 \
RRA:MAX:0.5:600:1100

mkgraph() {
umask 022
if [ "$3" == 'normal' ];then
        size='--width 400 --height 200'
elif [ "$3" == 'large' ]; then
        size='--width 900 --height 450'
fi
$rrd graph "${2}" --imgformat PNG \
DEF:gpu0tempmi="${1}":gpu0temp:MIN \
DEF:gpu1tempmi="${1}":gpu1temp:MIN \
DEF:gpu2tempmi="${1}":gpu2temp:MIN \
DEF:gpu3tempmi="${1}":gpu3temp:MIN \
DEF:gpu4tempmi="${1}":gpu4temp:MIN \
DEF:gpu5tempmi="${1}":gpu5temp:MIN \
DEF:gpu6tempmi="${1}":gpu6temp:MIN \
DEF:gpu7tempmi="${1}":gpu7temp:MIN \
DEF:gpu8tempmi="${1}":gpu8temp:MIN \
DEF:gpu9tempmi="${1}":gpu9temp:MIN \
								   \
DEF:gpu0tempav="${1}":gpu0temp:AVERAGE \
DEF:gpu1tempav="${1}":gpu1temp:AVERAGE \
DEF:gpu2tempav="${1}":gpu2temp:AVERAGE \
DEF:gpu3tempav="${1}":gpu3temp:AVERAGE \
DEF:gpu4tempav="${1}":gpu4temp:AVERAGE \
DEF:gpu5tempav="${1}":gpu5temp:AVERAGE \
DEF:gpu6tempav="${1}":gpu6temp:AVERAGE \
DEF:gpu7tempav="${1}":gpu7temp:AVERAGE \
DEF:gpu8tempav="${1}":gpu8temp:AVERAGE \
DEF:gpu9tempav="${1}":gpu9temp:AVERAGE \
								   \
DEF:gpu0tempma="${1}":gpu0temp:MAX \
DEF:gpu1tempma="${1}":gpu1temp:MAX \
DEF:gpu2tempma="${1}":gpu2temp:MAX \
DEF:gpu3tempma="${1}":gpu3temp:MAX \
DEF:gpu4tempma="${1}":gpu4temp:MAX \
DEF:gpu5tempma="${1}":gpu5temp:MAX \
DEF:gpu6tempma="${1}":gpu6temp:MAX \
DEF:gpu7tempma="${1}":gpu7temp:MAX \
DEF:gpu8tempma="${1}":gpu8temp:MAX \
DEF:gpu9tempma="${1}":gpu9temp:MAX \
								   \
CDEF:cdefmida=gpu0tempav,gpu0tempmi,- \
CDEF:cdefmidb=gpu1tempav,gpu1tempmi,- \
CDEF:cdefmidc=gpu2tempav,gpu2tempmi,- \
CDEF:cdefmidd=gpu3tempav,gpu3tempmi,- \
CDEF:cdefmide=gpu4tempav,gpu4tempmi,- \
CDEF:cdefmidf=gpu5tempav,gpu5tempmi,- \
CDEF:cdefmidg=gpu6tempav,gpu6tempmi,- \
CDEF:cdefmidh=gpu7tempav,gpu7tempmi,- \
CDEF:cdefmidi=gpu8tempav,gpu8tempmi,- \
CDEF:cdefmidj=gpu9tempav,gpu9tempmi,- \
									  \
CDEF:cdefmada=gpu0tempma,gpu0tempav,- \
CDEF:cdefmadb=gpu1tempma,gpu1tempav,- \
CDEF:cdefmadc=gpu2tempma,gpu2tempav,- \
CDEF:cdefmadd=gpu3tempma,gpu3tempav,- \
CDEF:cdefmade=gpu4tempma,gpu4tempav,- \
CDEF:cdefmadf=gpu5tempma,gpu5tempav,- \
CDEF:cdefmadg=gpu6tempma,gpu6tempav,- \
CDEF:cdefmadh=gpu7tempma,gpu7tempav,- \
CDEF:cdefmadi=gpu8tempma,gpu8tempav,- \
CDEF:cdefmadj=gpu9tempma,gpu9tempav,- \
									  \
LINE2:gpu0tempav#ff0000:'GPU 0' \
LINE0:gpu0tempmi#ff0000: \
AREA:cdefmida#ff000044::STACK \
AREA:cdefmada#ff000044::STACK \
GPRINT:gpu0tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu0tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu0tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu1tempav#ffa800:"GPU 1" \
LINE0:gpu1tempmi#ffa800: \
AREA:cdefmidb#ffa80044::STACK \
AREA:cdefmadb#ffa80044::STACK \
GPRINT:gpu1tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu1tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu1tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu2tempav#fff600:"GPU 2" \
LINE0:gpu2tempmi#fff600: \
AREA:cdefmidc#fff60044::STACK \
AREA:cdefmadc#fff60044::STACK \
GPRINT:gpu2tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu2tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu2tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu3tempav#6cff00:"GPU 3" \
LINE0:gpu3tempmi#6cff00: \
AREA:cdefmidd#6cff0044::STACK \
AREA:cdefmadd#6cff0044::STACK \
GPRINT:gpu3tempmi:MIN:"(Max\: %3.1lf°C \t/" \
GPRINT:gpu3tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu3tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu4tempav#00ffc0:'GPU 4' \
LINE0:gpu4tempmi#00ffc0: \
AREA:cdefmide#00ffc044::STACK \
AREA:cdefmade#00ffc044::STACK \
GPRINT:gpu4tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu4tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu4tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu5tempav#009cff:'GPU 5' \
LINE0:gpu5tempmi#009cff: \
AREA:cdefmidf#009cff44::STACK \
AREA:cdefmadf#009cff44::STACK \
GPRINT:gpu5tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu5tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu5tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu6tempav#0030ff:'GPU 6' \
LINE0:gpu6tempmi#0030ff: \
AREA:cdefmidg#0030ff44::STACK \
AREA:cdefmadg#0030ff44::STACK \
GPRINT:gpu6tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu6tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu6tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu7tempav#c000ff:'GPU 7' \
LINE0:gpu7tempmi#c000ff: \
AREA:cdefmidh#c000ff44::STACK \
AREA:cdefmadh#c000ff44::STACK \
GPRINT:gpu7tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu7tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu7tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu8tempav#d4d2d5:'GPU 8' \
LINE0:gpu8tempmi#d4d2d5: \
AREA:cdefmidi#d4d2d544::STACK \
AREA:cdefmadi#d4d2d544::STACK \
GPRINT:gpu8tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu8tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu8tempma:MAX:"Max\: %3.1lf°C)\n" \
											\
LINE2:gpu9tempav#000000:'GPU 9' \
LINE0:gpu9tempmi#000000: \
AREA:cdefmidj#00000044::STACK \
AREA:cdefmadj#00000044::STACK \
GPRINT:gpu9tempmi:MIN:"(Min\: %3.1lf°C \t/" \
GPRINT:gpu9tempav:LAST:"Curr\: %3.1lf°C \t/" \
GPRINT:gpu9tempma:MAX:"Max\: %3.1lf°C)\n" \
${size} \
--title "${4}" \
-s ${5} \
--watermark="${HOSTNAME}" \
--slope-mode \
--lazy 1>/dev/null &
}

# http://localhost/index.html generator

(echo -n "<html><head><style> body {font-family: Courier, monospace;}</style></head><body><b>"
hostname
echo "</b><br><br>"
/home/rain/bin/sysinfo.sh | sed 's/$/<br>/g' | sed 's/ /\&nbsp\;/g'
echo "<br><li><a href=/gputemp.html>GPU Temp</a>"
echo "<br><iframe src=/dyn.html frameborder=0 height=50% width=100%></iframe></body></html>") > /tmp/index.html

touch '/tmp/dyn.html'
chmod 644 '/tmp/index.html' '/tmp/dyn.html'
fi

# Never use sgminer API if NOAPI is set

[ ! -z "${NOAPI}" ] && cmd="false :"

showdate() {
    echo -ne "\e[0;36m[\e[0;35m"$(date +%Y-%m-%d" "%H:%M:%S)"\e[0;36m]\e[0;0m "
}

# Allow log for debugging purposes

debugme() {
[ "${DEBUG}" == '1' ] && {
	showdate
	echo "${1}"
} >> /tmp/ffc.log
}

# Halt all GPU activity. Change it if needed

do_halt() {
	/home/rain/bin/gpuoff 1>/dev/null 2>/dev/null
	/home/rain/bin/ethminerctl suspend
	pkill -9 Hashcat
}

# XMPP notifications

#if [ "${1}" != 'single' ]
#	then
#	rm -f /tmp/sendxmpp 2>/dev/null
#	touch /tmp/sendxmpp
#	( tail -qF /tmp/sendxmpp 2>/dev/null | sendxmpp -c -r $(hostname) -i -t rigs@conference.linuxoid.in ) &
#fi

sayto() {
	(cat - | tr -d '\n' ; echo -n " on $(hostname)") | sendxmpp -t -r $(hostname) -c rigs@conference.linuxoid.in
#	cat - >> /tmp/sendxmpp
}

# cgminer/sgminer core/mem clocks config parser

getcoreconf(){
	awk '/gpu-engine/{
			gsub(/[0-9]+\-|\"/, "", $3)
			split($3, core, ",")
			c='$1'+1
			print core[c]
		}' "${conf}"
}

getmemconf(){
	awk '/gpu-memclock/{
			gsub(/[0-9]+\-|\"/, "", $3)
			split($3, mem, ",")
			c='$1'+1
			print mem[c]
		}' "${conf}"
}

# Function for setting GPU clocks. 1-st parameter is a card number. If parameter is not set - all cards are used

setfreqs() {
[ -z "${1}" ] && card="$(seq 0 $(($(aticonfig --lsa | grep -c '[0-9]\.')-1)))" || card="${1}"
for i in ${card}
	do
# Allowing overclocking
		aticonfig --od-enable 1>/dev/null

# Getting core and memory clocks. If getcoreconf/getmemconf returns nothing - using GPU 0 clocks
# (sgminer behavior - if one parameter is set - it used for all cards)
		core="$(getcoreconf "${i}")"
		[ -z "${core}" ] && core="$(getcoreconf 0)"

		mem="$(getmemconf "${i}")"
		[ -z "${mem}" ] && core="$(getmemconf 0)"

# Setting GPU clocks
		[[ ! -z "${core}" && ! -z "${mem}" ]] && {
			aticonfig --od-setclocks=$core,$mem --adapter=$i 1>/dev/null
			debugme "Setting freqs for GPU $i"
# Clocks state flag. 1 = clocks set from config file, 2 = clocks is reduced.
			freqs[$i]='1'
		}
	done
# Setting Powertune to 120% via atitweak utility
	[ -x /home/rain/bin/atitweak ] && /home/rain/bin/atitweak -p 20 > /dev/null
	debugme "Setting powertune"
}

###################################

case "${1}" in
	freqs)
		setfreqs
	;;
	*)

# [ "${1}" != 'single' ] && echo "Hello, i'm online" | sayto

# Main loop
	while :
		do
# If NOAPI is not set - checking API availibility
	$cmd nc -w 1 -z localhost 4028 && {
	api='1' # API detected flag
	ffci='0' # $fan array initialisation flag
	debugme "API detected"
# Fetching API data
   	devdata="$(/home/rain/bin/api-example.py devs | sed -r 's/, |\{|\}/\n/g;s/(\n)u|'\''/\1/g')"
	} || {
	api='0' # API not detected

# sgminer process detection '
# ffci must be set to 0 and $fan array must be filled with real data every time when sgminer started or stopped
	if pidof sgminer 1>/dev/null
		then
			if [ -z "${sgdetect}" ]
				then
					debugme "sgminer detected"
					ffci='0'
					sgdetect='1'
		fi
	else
		if [ ! -z "${sgdetect}" ]
			then
			debugme "sgminer disabled"
			unset sgdetect
			ffci='0'
		fi
	fi

	debugme "sgminer=$sgdetect"

	[ "${ffci}" != '1' ] && {
# init fan array
	for j in $(seq 0 $(($(aticonfig --lsa | grep -c '[0-9]\.')-1)))
		do
		fan[$j]="$(DISPLAY=:0.$j aticonfig --pplib-cmd "get fanspeed 0" | awk '/Result/{gsub(/%/, "", $4); print $4}')"
		debugme "Writing array data for GPU $j"
	done

# Setting clocks for all cards
	if [ "${1}" != 'single' ]
		then
			setfreqs
	fi

	ffci='1'
	}

# Fetching data from aticonfig utility
			atidata="$(aticonfig --odgt --odgc --adapter=all 2>/dev/null)"
			debugme "Getting atidata"

#### fans and core frequency control section ####

# Parsing aticonfig output and extracting card number, temperature, core and memory clocks
# TODO: next time use this for all aticonfig data processing. For now we will see core clock updates only in next cycle

			for cdat in $(echo "${atidata}" | tr '\n' ' ' | sed 's/Adapter/\n&/g'| sort | sed -nr '/Adapter/N;s/\n/ /gp' | sed -r 's/^Adapter ([0-9]+) - .*Current Peak : *([0-9]+) +([0-9]+).*Temperature - ([0-9]{1,3}).[0-9]+ C.*/\1:\4:\2:\3/g')
				do

# Separating values between different variables
					cnt="${cdat%:*:*}"
					cn="${cnt%:*}" # Card number
					#ct="${cnt#*:}" # Card temperature # TODO: use array in the future and remove this variable
					ct[$cn]="${cnt#*:}" # Card temperature array

					ccmf="${cdat#*:*:}"
					ccf="${ccmf%:*}" # Card core clock
					cmf="${ccmf#*:}" # Card memory clock

					[ "${freqs[$cn]}" == '1' ] && {
						cmax[$cn]=${ccf} # max core freq array
						mmax[$cn]=${cmf} # max mem  freq array
					}

					debugme "Processing GPU $cn -> ${ct[$cn]} C"
					if [ "${1}" != 'single' ]
						then
# If card temperature is over $tco - halting mining, fans to $fmax (usually 100%)
					[ "${ct[$cn]}" -ge "${tco}" ] && {
						# halt mining
						debugme "Cutoff temp on $cn"
						fan[$cn]=${fmax}
						DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fmax}" 1>/dev/null
#						showdate  >> /tmp/ffc.log
						echo "ALERT! Critical temperature (${ct[$cn]}C) on GPU ${cn}" | sayto
						do_halt
					}

# If card temperature great than $toh, but still less than $tco (80-85 degr. zone) - set fans to max speed and reduce core clocks
					[[ "${ct[$cn]}" -ge "${toh}" && "${ct[$cn]}" -lt "${tco}" ]] && {
						ftalk='25'
						# reduce freqs
						debugme "Overheat on ${cn}: ${freqs[$cn]}"
#						[ "${freqs[$cn]}" == '1' ] && {
#							showdate >> /tmp/ffc.log
#							echo "GPU $cn overheat, reducing clocks" >> /tmp/ffc.log

							debugme "Setting max fan speed on ${cn}"
							fan[$cn]=${fmax}
							DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fmax}" 1>/dev/null

							[ "${ccf}" -gt "$((${corelow}+${ohfs}))" ] && {
								ccf=$((${ccf}-${ohfs}))

								[ "${freqs[$cn]}" == '1' ] && {
									echo "GPU $cn overheat (${ct[$cn]} C), starting core clock reducing" | sayto
								}

								freqs[$cn]='2'
								debugme "Reducing freqs on $cn to ${ccf}/${cmf}"
								aticonfig --od-setclocks=${ccf},${cmf} --adapter=$cn 1>/dev/null
							} || {

								echo "No more space in frequency range on card ${cn} for clock reducing" | sayto
								debugme "No more space in frequency range on card ${cn} for clock reducing"
							}
#						}
					}

# If card temperature goes below overheat level - report about new frequencies

					[[ "${ct[$cn]}" -lt "$((${toh}-${hyst}))" ]] && {
						[ ${freqs[$cn]} == '2' ] && {
							[ ${ftalk} -gt '0' ] && let ftalk-=1
							[ ${ftalk} == '1' ] && {
								echo "GPU $cn clocks reduced to ${ccf}/${cmf}" | sayto
								ftalk='0'
							}
						}
					}

# If card temperature decreased to safe level - $trst - restore core clocks

					[[ "${ct[$cn]}" -le "${trst}" ]] && {

						[ "${freqs[$cn]}" == '2' ] && {
							[ "$((${cmax[$cn]}-${ccf}))" -gt "${fups}"  ] && {
								aticonfig --od-setclocks=$((${ccf}+${fups})),$cmf --adapter=$cn 1>/dev/null
							} || {
								setfreqs "${cn}"
								debugme "Clocks restored on GPU $cn"
								echo "Clocks restored on GPU $cn" | sayto
							}
						}
					}

# If card temperature in second zone (typically 70-80 degr) - allowing fan speed to be up to $fmax

					[[ "${ct[$cn]}" -gt "$((${tt2}+${hyst}))" && "${ct[$cn]}" -lt "$((${toh}-${hyst}))" ]] && {
						debugme "2-nd fan zone on $cn"
						# allow fans to 100%

						[ "${fan[$cn]}" -lt "${fmax}" ] && {
#							old="${fan[$cn]}"
							let fan[$cn]+=5

							[ "${fan[$cn]}" -lt "${tfan}" ] && {
								fan[$cn]=${tfan}
								debugme "Fast heating on $cn"
							}

							[ "${fan[$cn]}" -gt "${fmax}" ] && fan[$cn]="${fmax}"
							debugme "Setting fan to ${fan[$cn]} on GPU $cn"
#							showdate  >> /tmp/ffc.log
#							echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}"  >> /tmp/ffc.log
							DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>/dev/null
						}
					}

# If card temperature in 1-st zone (65-70 degr) - fan speed can be up to $tfan

					[[ "${ct[$cn]}" -gt "$((${tt1}+${hyst}))" && "${ct[$cn]}" -lt "$((${tt2}-${hyst}))" ]] && {
						# allow fans to $tfan
						debugme "1-nd fan zone on $cn"
						[ "${freqs[$cn]}" == '2' ] && {
							setfreqs "${cn}"
						}

						[ "${fan[$cn]}" -lt "${tfan}" ] && {
#							old="${fan[$cn]}"
							let fan[$cn]+=2
							[ "${fan[$cn]}" -gt "${tfan}" ] && fan[$cn]="${tfan}"
#							showdate  >> /tmp/ffc.log
#							echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}"  >> /tmp/ffc.log
							debugme "Setting fan to ${fan[$cn]} on GPU $cn"
							DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>/dev/null
						}
					}

# If card temerature less than first target - fan goes idle

					[ "${ct[$cn]}" -lt "$((${tt1}-${hyst}))" ] && {
						# decrease fan speed
						debugme "Low temp on $cn"
#						old="${fan[$cn]}"
						let fan[$cn]-=1
						[ "${fan[$cn]}" -lt "${fmin}" ] && fan[$cn]="${fmin}"
#						showdate  >> /tmp/ffc.log
#						echo "GPU ${cn} ${ct[$cn]}: setting fan $old -> ${fan[$cn]}"  >> /tmp/ffc.log
						DISPLAY=:0.$cn aticonfig --pplib-cmd "set fanspeed 0 ${fan[$cn]}" 1>>/dev/null
					}

					fi

			done

# TODO: сделать это все как-то покрасивее; возможно - вынести 60 тут и выше (создание базы) в переменную
			[ -z "${st}" ] && st='60'
			let st-=$((60/$sleep))
			[ "${st}" -le '0' ] && {
			st='60'
			$rrd update "${rrdb}" N$(
				s=10
				for t in ${!ct[@]}
					do
					echo -n ":${ct[$t]}"
					let s-=1
				done

				for i in $(seq 1 $s)
					do
						echo -n :U
				done
			)

			mkgraph "${rrdb}" "${site}"/gpustat.h.png               'normal' "GPU Temp, час" -60min
			mkgraph "${rrdb}" "${site}"/gpustat.h.large.png         'large'  "GPU Temp, час" -60min

			mkgraph "${rrdb}" "${site}"/gpustat.d.png               'normal' "GPU Temp, день" -24h
			mkgraph "${rrdb}" "${site}"/gpustat.d.large.png         'large'  "GPU Temp, день" -24h

			mkgraph "${rrdb}" "${site}"/gpustat.m.png               'normal' "GPU Temp, месяц" -30d
			mkgraph "${rrdb}" "${site}"/gpustat.m.large.png         'large'  "GPU Temp, месяц" -30d

			mkgraph "${rrdb}" "${site}"/gpustat.y.png               'normal' "GPU Temp, год" -12mon
			mkgraph "${rrdb}" "${site}"/gpustat.y.large.png         'large'  "GPU Temp, год" -12mon

			}

			devdata="$((

# Exporting fan speed for 2-nd part of this script
			for f in ${!fan[@]}
				do
					echo "Fan Speed $f ${fan[$f]}"
			done

# ...and exporting frequency state
			for c in ${!freqs[@]}
				do
					echo "Freq State $c ${freqs[$c]}"
			done

# Transforming aticonfig data for sgminer API compatible format... With some additions
			echo "${atidata}"

			) | awk '

				/^Adapter/{
					a=$2
					name=$0
					gsub(/^Adapter.* - /, "", name)
				}

				/Temp/{
					OFMT="%.f"
					temp[a]=$5+0
				}

				/Speed/{
					speed[$3]=$4
				}

				/Freq State/{
					freqs[$3]=$4
				}

				/Clocks/{
					core[a]=$4
					mem[a]=$5
				}

				/load/{
					load[a]=$4
					gsub(/%/, "", load[a])

					print "GPU: "a
					print "Devname: "name
					print "Temperature: " temp[a]
					print "Fan Percent: " speed[a]
					print "Freq State: " freqs[a]
					print "GPU Clock: " core[a]
					print "Memory Clock: " mem[a]
					print "GPU Activity: " load[a]
					print "Status: uAlive"
					print "Enabled: U"
					print "Utility"
					}
			')"
	}

# Indication part

			data="$(echo -n "${devdata}" | gawk '

					function pbar(prog, min, max)
					{
						p=""
						if (min=="") min=10
						if (max=="") max=100
						inc=((max-min)/10)+0.1
						for (i=min; i<=max; i+=inc) {
							if (i>prog) p=p" "
								else p=p"|"
						}
						return p
					}

					/Temperature/{
						OFMT="%.f"
						temp=$2+0
						if (temp>79) ctemp="\\e[5;91m"
							else
								if (temp>69) ctemp="\\e[0;33m"
									else
										ctemp="\\e[0;32m"
					}

					/Fan Percent/{
						OFMT="%.f"
						fan=$3+0
						if (fan>75) cfan="\\e[31m"
							else
								if (fan>45) cfan="\\e[33m"
									else
										cfan="\\e[32m"
					}

					/Freq State/{
						if ($3=="1") fstate="\\e[32m"
						if ($3=="2") fstate="\\e[91m"
					}

					/GPU Activity/{
						act=$3
						if (act<80) cact="\\e[0;31m"
							else
								if (act<90) cact="\\e[0;33m"
									else
										cact="\\e[0;32m"
					}

					/GPU Clock/{
						core=$3
					}

					/Devname/{
						name=$0
						gsub(/Devname: /, " | ", name)
					}

					/Status/{
						stat=$2
						if (stat=="uAlive") stat="\\e[36m"
							else stat="\\e[5;31m"
					}

					/Memory Clock/{
						mem=$3
					}

					/Enabled/{
						en=$2
						if (en=="uN") en="X"
							else
						if (en=="U") en="U"
							else en="Y"
					}

					/^GPU:/{
						gnum=$2
					}

					/Utility/{
						printf "%-67s %-48s %-22s %-61s %s\n",
							stat en " GPU "gnum": \\e[0;0m["ctemp pbar(temp, 30, 85) "\\e[0m] " ctemp temp"C\\e[0m",
							"| FAN: ["cfan pbar(fan)"\\e[0m] " cfan fan"% \\e[0m",
							"| "fstate core"/"mem"\\e[0m",
							"| Load: \\e[0;0m["cact pbar(act, 50, 99) "\\e[0m] " cact act"%\\e[0;0m",
							name
					}'
					)"

		[ "${api}" == '1' ] &&\
			data="\t\t\t=========== sgminer stat ===========\n${data}" ||\
			data="\t\t\t=========== aticonfig stat ===========\n${data}"

		[ "${1}" == 'single' ] && {
			echo -e "${data}"
			break
			} || {
			clear
			echo -e "${data}"

# http://localhost/dyn.html generator
			(echo -ne "\n* "
			echo $(/home/rain/bin/ethspeed single) @ $(date +%H:%M:%S)
			echo -ne "\n"
			echo -e "${data}") | LANG=C awk '
				BEGIN {
				        print "<html>"
						print "<title>"strftime()"</title>"
				        print "<meta http-equiv=\"refresh\" content=\"10;\">"
				        print "<head><style> body '{'font-family: Courier, monospace;'}'</style></head><body>"
				}

				{

				gsub(/ /, "\\&nbsp;", $0)
				gsub(/\t/, "\\&emsp;", $0)
				gsub(/$/, "<br>", $0)
				gsub(/\[([0-9];)?31m/, "</font><font color=red>", $0)
				gsub(/\[([0-9];)?91m/, "</font><font color=red><span class=redcode>", $0)
				gsub(/\[([0-9];)?33m/, "</font><font color=darkorange><span class=yellowcode>", $0)
				gsub(/\[([0-9];)?32m/, "</font><font color=green><span class=greencode>", $0)
				gsub(/\[36m/, "<font color=blue>", $0)
				gsub(/\[(0;)?0m/, "</font></span>", $0)
				print $0

				}

				END {
				print "</body></html>"
				}' > /tmp/dyn.html
			}

		debugme "Sleeping..."
		sleep "${sleep}"
	done

	;;
esac