Вы сейчас просматриваете Упрощение контроля параметров электросети или АСКУЭ в Telegram. Часть 1

Упрощение контроля параметров электросети или АСКУЭ в Telegram. Часть 1

Предисловие: в июле 2024 года я закончил обучение в университете, стал инженером-энергетиком и попал на завод. Одной из моих задач было контролирование потребляемой мощности предприятием. При превышении значения N необходимо трубить в группу в Viber и просить, чтобы пока не включали энергоемкое оборудование, а также раз в полчаса писать текущую трехминутную мощность. Так как 99% рабочего дня я на месте не сижу, такой контроль затруднителен. Контроль мощности осуществляется через старенькую АСКУЭ с минимальным функционалом. Поэтому спустя месяц работы было принято решение упросить процесс контроля.

Вводные: 1) Старенькая АСКУЭ, открывающаяся только через IE, единственная функциональность которой — опрос коммерческих счетчиков и небольшие расчеты. 2) Желание упростить себе жизнь.

Первым делом необходимо было определиться с языком программирования. Выбор пал на Python. Причин несколько: есть небольшой опыт, простота языка, большое количество готовых библиотек.

Для получения значения мощности был выбран самый простой вариант — телеграм-бот. Значения из АСКУЭ выдираются простым парсингом. Для бота была выбрана библиотека TeleBot, для парсинга — BeautifulSoup.

Разбор кода

Первоначально необходимо произвести авторизацию бота. Для этого необходимо произвести получение токена. Процесс получения токена можно посмотреть, например, тут. После этого необходимо создать объект для работы с ботом.

bot = telebot.TeleBot("------Telegram Token----------")

После этого можно переходить к непосредственному описанию сценариев для бота. Первоначально необходимо создать стартовую функцию, которая будет вызываться при первом запуске бота и которая позволит добавить некоторые интерактивные элементы.

@bot.message_handler(commands=['start'])
def start(message):
	markup = types.ReplyKeyboardMarkup(resize_keyboard=True)
	btn1 = types.KeyboardButton("Узнать мощность завода")
	btn2 = types.KeyboardButton('Отбито с 08:00 до 11:00')
	markup.add(btn1)
	markup.add(btn2)
	bot.send_message(message.chat.id, text="Привет, {0.first_name}!".format(
		message.from_user), reply_markup=markup)

Здесь мы определяем, что функция вызывает при помощи введении команды «/start» (1 строка), добавляем две кнопки btn1 «Узнать мощность завода» для получения последних 3-минутных мощностей и btn2 «Отбито с 08:00 до 11:00» для определения максимальной получасовой нагрузки в утренний пик, а также приветственной сообщение (последняя строка).

После создания интерактивных элементов можно переходить к основным функциям. Сначала необходимо создать функции для парсинга страницы с 3-минутными мощностями . Определить шаблон париснга можно при помощи инструмента разработчика, встроенного в браузер. Подробно останавливаться на описании процессе парсинга я не буду , так как здесь все довольно индивидуально. Сначало немного расскажу о функции получения 3-минутных мощностей.

Сначала определим текущую дату для встраивания ее в ссылку запроса:

date1 = str(datetime.datetime.now().day)+'.' + \
			str(datetime.datetime.now().month) + \
			'.'+str(datetime.datetime.now().year)

Определение временных параметров для встраивания в запрос:

hr1 = str(datetime.datetime.now().hour+1) if datetime.datetime.now().hour + \
			1 >= 10 else "0"+str(datetime.datetime.now().hour+1)

Далее параметры, полученные выше, вставляем в запрос, устаналиваем заголовки для запроса, делаем запрос на сервер АСКУЭ и получаем веб-страницу для парсинга:

url = "http://IP_ASKUE/arm/3min_p.php?tab=0&dc="+date1+"+"+hr1 + \
			"%3A30&disp=120&iname=%E2%E2%EE%E4%E0-%F1%F3%E1.-%F1%F2%EE%EB%EE%E2%E0%FF+&id=48&pid=34&lid=3&node=1&nobj=1001&adr=1004&frameName=3min_p"

headers = {"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"}

response = requests.get(url, headers=headers)

soup = BeautifulSoup(response.text, 'html.parser')

После этого можно переходить к непосредственному парсингу по элементам веб-страницы. Полный текст функции:

def askue_req():
	date1 = str(datetime.datetime.now().day)+'.' + \
		str(datetime.datetime.now().month) + \
		'.'+str(datetime.datetime.now().year)
	hr1 = str(datetime.datetime.now().hour+1) if datetime.datetime.now().hour + \
		1 >= 10 else "0"+str(datetime.datetime.now().hour+1)
	url = "http://IP_ASKUE/arm/3min_p.php?tab=0&dc="+date1+"+"+hr1 + \
		"%3A30&disp=120&iname=%E2%E2%EE%E4%E0-%F1%F3%E1.-%F1%F2%EE%EB%EE%E2%E0%FF+&id=48&pid=34&lid=3&node=1&nobj=1001&adr=1004&frameName=3min_p"
	headers = {
		"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
	}
	response = requests.get(url, headers=headers)
	soup = BeautifulSoup(response.text, 'html.parser')
	table = soup.find_all('table')[3]
	time = [table.find_all('tr')[8].find_all('td')[1].text, table.find_all('tr')[7].find_all('td')[
		1].text, table.find_all('tr')[6].find_all('td')[1].text, table.find_all('tr')[5].find_all('td')[1].text]
	P = [table.find_all('tr')[8].find_all('td')[2].text, table.find_all('tr')[7].find_all('td')[
		2].text, table.find_all('tr')[6].find_all('td')[2].text, table.find_all('tr')[5].find_all('td')[2].text]
	return P, time

Аналогично определяется функция для получения получасовых утренних пиков:

def pik():
	date1 = str(datetime.datetime.now().day)+'.' + \
		str(datetime.datetime.now().month) + \
		'.'+str(datetime.datetime.now().year)
	url = "http://IP_ASKUE/arm/form1.php?tab=1&disp=1&maxp=0&dc="+date1 + \
		"&iname=%E2%E2%EE%E4%E0-%F1%F3%E1.-%F1%F2%EE%EB%EE%E2%E0%FF+&id=48&pid=34&lid=3&node=1&nobj=1001&adr=1004&frameName=form1"
	headers = {
		"User-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"
	}
	response = requests.get(url, headers=headers)
	soup = BeautifulSoup(response.text, 'html.parser')
	P_p = soup.find_all('tr', attrs={'bgcolor': 'salmon'})[
		1].find_all('td')[1].text
	time_p = soup.find_all('tr', attrs={'bgcolor': 'salmon'})[
		0].find_all('td')[1].text
	return P_p, time_p

На этом первая часть подходит к концу. В следующей и заверщающей части я покажу непосредственное определение функций для общения с телеграм-ботом.