diff --git a/.gitignore b/.gitignore index 8fc1bf0..480ef35 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ test/ +temp/ test.py # ---> Python diff --git a/config.ini b/config.ini index 2bb6e19..fc2d479 100644 --- a/config.ini +++ b/config.ini @@ -2,4 +2,4 @@ device_hwid = PID=1A86:7523 baudrate = 9600 debug = false -controller_capacity = 18 \ No newline at end of file +controller_capacity = 18 diff --git a/handlers.py b/handlers.py new file mode 100644 index 0000000..8790c87 --- /dev/null +++ b/handlers.py @@ -0,0 +1,224 @@ +import asyncio +import socket +import time +import logging +import datetime + +from aiogram import Router, F +from aiogram import Bot, Dispatcher, types +from aiogram.filters.command import Command +from aiogram.types import FSInputFile +from aiogram.fsm.context import FSMContext +from aiogram.fsm.storage.memory import MemoryStorage +from aiogram.fsm.state import StatesGroup, State +from aiogram.utils.chat_action import ChatActionSender +#from aiogram.types import BufferedInputFile + +import configparser +from asyncpg_lite import DatabaseManager + +from os import listdir +from os.path import isfile, join + +import lib.OCer as OCer +import serial + +router = Router() + +async def get_user_list(db_url, deletion_password, table_name): + try: + db_manager = DatabaseManager(db_url=db_url, deletion_password=deletion_password) + data = [] + async with db_manager as manager: + one_data = await manager.select_data(table_name=table_name) + for i in one_data: + data.append(i) + logging.info(f"Fetched {len(data)} records from the database.") + return data + + except Exception as err: + logging.error(err) + pass + +def init(Bot): + global config, bot, logger, some_open_ports, zxc + + bot = Bot + #consts&confs + config = configparser.ConfigParser() + #bc_calculator = lomka.BCCCalculator() + some_open_ports = serial.tools.list_ports.comports() + + #load config file + config.read('config.ini') + # device_hwid = config['DEFAULT']['device_hwid'] + # baudrate = config['DEFAULT']['baudrate'] + + #log section + logging.basicConfig( + level=logging.INFO if config['DEFAULT']['debug'] == "false" else logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('lastest.log'), + logging.StreamHandler()]) + logger = logging.getLogger(socket.gethostname()) + + zxc = OCer.caser(device_hwid="PID=1A86:7523", baudrate=9600, + config=config, logger=logger) + zxc.set_controller_number(1) + zxc.set_port_device() + + +main_kb = [ + [types.KeyboardButton(text="Выбрать контроллер")], + [types.KeyboardButton(text="Открыть секцию")], + [types.KeyboardButton(text="Статус секции")], + [types.KeyboardButton(text="Статус ВСЕХ секций")], + [types.KeyboardButton(text="Открыть ВСЕ секции")], + [types.KeyboardButton(text="Контакты")]] + +keyboardos = types.ReplyKeyboardMarkup(keyboard=main_kb, resize_keyboard=True, + input_field_placeholder="amogus life420") + +class Form(StatesGroup): + input_controller = State() + input_section = State() + input_status_section = State() + input_status_section_all = State() + input_approve = State() + +@router.message(Command("start")) +async def cmd_start(message: types.Message): + + keyboard = types.ReplyKeyboardMarkup(keyboard=main_kb, resize_keyboard=True, + input_field_placeholder="Выберите действие...", is_persistent=True) + + await message.answer(f"{message.from_user.id}\n", + reply_markup=keyboard) + +@router.message(F.text.lower() == "выбрать контроллер") +async def choose_controller(message: types.Message, state: FSMContext): + kb = [ + [types.KeyboardButton(text="Домой")] + ] + keyboard = types.ReplyKeyboardMarkup(keyboard=kb, resize_keyboard=True, + input_field_placeholder="amogus life420") + + await message.reply("Определите номер управляемого контроллера", reply_markup=keyboard) + + await state.set_state(Form.input_controller) + +@router.message(F.text, Form.input_controller) +async def set_controller_number(message: types.Message, state: FSMContext): + await state.update_data(confirm=message.text) + user_data = await state.get_data() + + try: + await zxc.set_controller_capacity(int(user_data["confirm"])) + await message.reply(f"Контроллер ({user_data["confirm"]}) выбран успешно!", + reply_markup=keyboardos) + except Exception as e: + await message.reply(f"Возникло исключение(ошибка): {e}", reply_markup=keyboardos) + await state.clear() + +@router.message(F.text.lower() == "статус всех секций") +async def status_all_sections(message: types.Message, state: FSMContext): + + cool_data = await zxc.READ_ALL_CELLS() + + await message.reply(f"{cool_data}", reply_markup=keyboardos) + await state.clear() + +@router.message(F.text.lower() == "открыть все секции") +async def open_all_sections(message: types.Message, state: FSMContext): + kb = [ + [types.KeyboardButton(text="Продолжить")], + [types.KeyboardButton(text="Домой")] + ] + keyboard = types.ReplyKeyboardMarkup(keyboard=kb, resize_keyboard=True, + input_field_placeholder="amogus life420") + + await message.reply("Данная операция откроет ВСЕ секции доступные контроллеру.\nПродолжить?", reply_markup=keyboard) + + await state.set_state(Form.input_approve) + +@router.message(F.text, Form.input_approve) +async def open_all_sections_confirm(message: types.Message, state: FSMContext): + await state.update_data(confirm=message.text) + user_data = await state.get_data() + + if str(user_data["confirm"]).lower() == "продолжить": + try: + cool_data = await zxc.OPEN_ALL_CELLS_LEGACY(True) + await message.reply(f"Статус ячеек:\n{cool_data}", + reply_markup=keyboardos) + except Exception as e: + await message.reply(f"Возникло исключение(ошибка): {e}", reply_markup=keyboardos) + await state.clear() + +@router.message(F.text.lower() == "контакты") +async def contact_info(message: types.Message): + keyboard = types.ReplyKeyboardMarkup(keyboard=main_kb, resize_keyboard=True, + input_field_placeholder="amogus life420") + await message.reply("Настучать админу: \n" \ + "Discord: Valo#1234 | @valoker\n" \ + "GitHub: @Valoker\n" \ + "Telegram: @Valoka", reply_markup=keyboard) + +@router.message(F.text.lower() == "домой") +async def home(message: types.Message, state: FSMContext): + await message.answer(reply_markup=keyboardos, + text="Меню") + await state.clear() + +@router.message(F.text.lower() == "открыть секцию") +async def open_section(message: types.Message, state: FSMContext): + kb = [ + [types.KeyboardButton(text="Домой")] + ] + keyboard = types.ReplyKeyboardMarkup(keyboard=kb, resize_keyboard=True, + input_field_placeholder="amogus life420") + + await message.reply("Отправьте номер секции для открытия..", reply_markup=keyboard) + await state.set_state(Form.input_section) + +@router.message(F.text, Form.input_section) +async def open_section_number(message: types.Message, state: FSMContext): + await state.update_data(confirm=message.text) + user_data = await state.get_data() + + try: + await zxc.open_cell(int(user_data["confirm"])) + await message.reply(f"Секция ({user_data["confirm"]}) контроллера " \ + f"({zxc.controller_number}) открыта.", + reply_markup=keyboardos) + except Exception as e: + await message.reply(f"Возникло исключение(ошибка): {e}", reply_markup=keyboardos) + await state.clear() + +@router.message(F.text.lower() == "статус секции") +async def get_section_status(message: types.Message, state: FSMContext): + kb = [ + [types.KeyboardButton(text="Домой")] + ] + keyboard = types.ReplyKeyboardMarkup(keyboard=kb, resize_keyboard=True, + input_field_placeholder="amogus life420") + + await message.reply("Отправьте номер желаемой секции..", reply_markup=keyboard) + + await state.set_state(Form.input_status_section) + +@router.message(F.text, Form.input_status_section) +async def get_section_status_fin(message: types.Message, state: FSMContext): + await state.update_data(confirm=message.text) + user_data = await state.get_data() + + try: + controller_data = await zxc.read_controller(cell_number=int(user_data["confirm"])) + await message.reply(f"Секция ({user_data["confirm"]}) контроллера " \ + f"({zxc.controller_number}) со статусом: {controller_data[0]["CURRENT_STATUS"]}. \n{controller_data}", + reply_markup=keyboardos) + except Exception as e: + await message.reply(f"Возникло исключение(ошибка): {e}", reply_markup=keyboardos) + await state.clear() + diff --git a/lib/OCer.py b/lib/OCer.py index 24e2fc3..cd0e1b7 100644 --- a/lib/OCer.py +++ b/lib/OCer.py @@ -1,6 +1,4 @@ import serial.tools.list_ports -import time -import configparser import logging import socket import lib.lomka as lomka @@ -28,7 +26,7 @@ class caser: config.read(config_name) self.device_hwid = config['DEFAULT']['device_hwid'] self.baudrate = config['DEFAULT']['baudrate'] - self.controller_capacity = config['DEFAULT']['controller_capacity'] + self.controller_capacity = int(config['DEFAULT']['controller_capacity']) # узнать какое исключение вызывается конкретно, ибо нужно проверять каждое по отдельности except: @@ -41,8 +39,9 @@ class caser: self.comport: str # только адрес self.active_comport = serial.tools.list_ports.comports()[0] # объект порта (dummy) self.controller_number: bytes = 1 - self.connection : serial.Serial + #self.connection : serial.Serial self.async_connection : tuple + self.instance_error_count : int = 0 # Определение базовых констант/команд self.default_consts = {True: 17, @@ -61,14 +60,6 @@ class caser: if self.device_hwid in port.hwid: self.logger.info(f"Found the correct port: {port.device}") return str(port.device) - - def set_controller_capacity(self, capacity: int): - '''Устанавливает количество доступных замков''' - #сделать автоматом - if capacity: - self.controller_capacity = capacity - else: - self.controller_capacity = 18 def set_port_device(self, port_device=None): '''Устанавливает com port''' @@ -84,13 +75,13 @@ class caser: self.controller_number = hex(controller_number) async def send_command(self, cell_number : int, command: int, - bytes_count=15, header=138): + bytes_count=5, header=138): try: # active_comport = serial.Serial(self.comport, baudrate=self.baudrate, timeout=2) reader, writer = await serial_asyncio.open_serial_connection( url=self.comport, - baudrate=self.baudrate - ) + baudrate=self.baudrate, + timeout=2) self.logger.info(f"Connected to {self.comport}") @@ -111,91 +102,124 @@ class caser: writer.write(data_string) return await reader.read(bytes_count) + except Exception as error: + raise error + + async def read_controller(self, cell_number : int, command=51, + header=128, bytes_count=5): + try: + # active_comport = serial.Serial(self.comport, baudrate=self.baudrate, timeout=2) + reader, writer = await serial_asyncio.open_serial_connection( + url=self.comport, + baudrate=self.baudrate + ) + + self.logger.info(f"Connected to {self.comport}") + + data = [header.to_bytes(1, 'little'), + int(self.controller_number, 16).to_bytes(1, 'little'), + cell_number.to_bytes(1, 'little'), + command.to_bytes(1, 'little')] + + data_string = b'' + for i in data: + data_string += i + + checksum = self.bc_calculator.xor_bcc(data_string) + + data_string += checksum.to_bytes(1, 'little') + writer.write(data_string) + return await reader.read(bytes_count) + except Exception as error: + raise error + + async def open_cell(self, cell_number): + '''открывает замок по номеру''' + try: + result = [] + answer = await self.send_command(cell_number=cell_number, + command=self.default_consts[True], + bytes_count=self.default_consts["RESPONSE_LENGHT"], + header=self.default_consts["MANIPULATE_HEADER"]) + result.append({"CELL_NUMBER": cell_number, + "CURRENT_STATUS": True if answer[-2] == 17 else False if answer[-2] == 0 else None, + "FULL_ANSWER": answer}) + return result except Exception as error: self.logger.error(f"{error}") + self.instance_error_count += 1 + return str(error) - def read_controller(self, cell_number : int, command=51, header=128): - active_comport = serial.Serial(self.comport, baudrate=self.baudrate, timeout=2) - self.logger.info(f"Connected to {active_comport.name }") - data = [header.to_bytes(1, 'little'), - int(self.controller_number, 16).to_bytes(1, 'little'), - cell_number.to_bytes(1, 'little'), - command.to_bytes(1, 'little')] - - data_string = b'' - for i in data: - data_string += i - - checksum = self.bc_calculator.xor_bcc(data_string) - - data_string += checksum.to_bytes(1, 'little') - active_comport.write(data_string) - return active_comport.read(5) - - def READ_ALL_CELLS(self): + async def READ_ALL_CELLS(self): '''и возвращает состояние всех замков''' try: result = [] - for i in range(0, self.controller_capacity - 1): - answer = self.send_command(cell_number=i, - command=self.default_consts["INFO"], - bytes_count=self.default_consts["RESPONSE_LENGHT"], - header=self.default_consts["READ_HEADER"]) + for i in range(1, self.controller_capacity + 1): + answer = await self.send_command(cell_number=i, + command=self.default_consts["INFO"], + bytes_count=self.default_consts["RESPONSE_LENGHT"], + header=self.default_consts["READ_HEADER"]) result.append({"CELL_NUMBER": i, - "CURRENT_STATUS": True if answer[-2] == 17 else False, - "FULL_ANSWER": answer}) + "CURRENT_STATUS": True if answer[-2] == 17 else False if answer[-2] == 0 else None, + "FULL_ANSWER": answer}) return result except Exception as error: + self.logger.error(f"{error}") + self.instance_error_count += 1 return str(error) - - def OPEN_ALL_CELLS_LEGACY(self, confirm : bool): + + async def set_controller_capacity_unsafe(self, capacity: int = 0): + '''Устанавливает количество доступных замков''' + if capacity >= 0: + self.controller_capacity = capacity + # else: + # # target max capatity + # self.controller_capacity = 128 + # result = await self.READ_ALL_CELLS() + # if isinstance(result, list): + # self.controller_capacity = len(result) + # self.logger.info(f"Controller capacity set to {self.controller_capacity}") + # else: + # self.logger.error("READ_ALL_CELLS() failed") + # return False + + async def OPEN_ALL_CELLS_LEGACY(self, confirm : bool): '''Открывает все замки вручную и возвращает состояние всех замков Confirm - подтверждение операции''' if confirm: try: result = [] for i in range(1, self.controller_capacity + 1): - answer = self.send_command(cell_number=i, + answer = await self.send_command(cell_number=i, command=self.default_consts[True], bytes_count=self.default_consts["RESPONSE_LENGHT"], header=self.default_consts["MANIPULATE_HEADER"]) result.append({"CELL_NUMBER": i, - "CURRENT_STATUS": True if answer[-2] == 17 else False, + "CURRENT_STATUS": True if answer[-2] == 17 else False if answer[-2] == 0 else None, "FULL_ANSWER": answer}) return result except Exception as error: + self.logger.error(f"{error}") + self.instance_error_count += 1 return str(error) else: return 1 - def set_controller_capacity(self, capacity=0) -> str: + async def set_controller_capacity(self, capacity=0) -> str: '''Устанавливает количество доступных замков''' if capacity != 0: self.controller_capacity = capacity return self.controller_capacity else: # тут должно быть автоматическое определение кол-ва замков на контроллере - # try: - # return self.send_command(0, self.default_consts[True], - # self.default_consts["MANIPULATE_HEADER"], - # bytes_count=self.controller_capacity) - # except Exception as error: - # return str(error) - pass + try: + # написать + count = await self.send_command(0, self.default_consts[True], + self.default_consts["MANIPULATE_HEADER"], + bytes_count=self.controller_capacity) + except Exception as error: + return str(error) - # async def create_async_connection(self): - - # try: - # self.async_connection = await serial_asyncio.open_serial_connection( - # url=self.comport, - # baudrate=self.baudrate - # ) - # self.logger.info(f"Async connection to {self.comport}") - # except Exception as error: - # self.logger.error(f"Async connection error: {error}") - # pass - - #хочу асинхронности async def watchdog(self): '''Слушает контролер в реальном времени и возвращает bytearray с событием''' try: @@ -212,9 +236,11 @@ class caser: except Exception as error: self.logger.error(f"{error}") + self.instance_error_count = self.instance_error_count + 1 writer.close() await writer.wait_closed() except Exception as error: self.logger.critical(f"{error}") + self.instance_error_count = self.instance_error_count + 1 diff --git a/requirements.txt b/requirements.txt index f6c1a1f..d3bd1b6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,4 @@ pyserial +asyncpg_lite +pyserial-asyncio +aiogram \ No newline at end of file diff --git a/start-tg.py b/start-tg.py new file mode 100644 index 0000000..083fc7b --- /dev/null +++ b/start-tg.py @@ -0,0 +1,23 @@ +import asyncio +from aiogram import Router, F +from aiogram import Bot, Dispatcher + +import configparser +import os +from asyncpg_lite import DatabaseManager + +import handlers + +token = configparser.ConfigParser() +token.read('temp/token.ini', encoding='utf-8-sig') +bot = Bot(token=token.get("Main", "token")) +handlers.init(bot) + +async def main(): + dp = Dispatcher() + dp.include_routers(handlers.router) + + await dp.start_polling(bot) + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/main.py b/test-main.py similarity index 71% rename from main.py rename to test-main.py index 8a47527..05bb2a0 100644 --- a/main.py +++ b/test-main.py @@ -4,7 +4,6 @@ import configparser import serial import socket import asyncio -import time if __name__ == "__main__": #consts&confs @@ -30,20 +29,22 @@ if __name__ == "__main__": config=config, logger=logger) zxc.set_controller_number(1) zxc.set_port_device() - zxc.set_controller_capacity(18) # добавь *aargs - #print(zxc.READ_ALL_CELLS()) - #print(zxc.OPEN_ALL_CELLS_LEGACY(True)) - - # print(zxc.read_controller(1)) - # print(zxc.read_controller(1)) async def main(zxc): + # await zxc.set_controller_capacity(18) - print(await zxc.send_command(1, 17)) - exit() + # print(await zxc.watchdog()) + # print(await zxc.OPEN_ALL_CELLS_LEGACY(True)) + #print(await zxc.READ_ALL_CELLS()) + # print(await zxc.read_controller(1)) + # print(await zxc.send_command(1, 17)) + # print(await zxc.open_cell(1)) + # print(await zxc.set_controller_capacity_unsafe(18)) + # print(await zxc.READ_ALL_CELLS()) + pass while True: asyncio.run(main(zxc)) \ No newline at end of file