Как создать эффективный счетчик на Python?

Добрый день, уважаемые знатоки.

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

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

Вот пример метода, который я использую:

<pre><code class="python">IP12 = '192.168.0.100'
IP13 = '192.168.0.101'
RODOS12 = '192.168.1.100'
RODOS13 = '192.168.1.101'

def cameras_checker(self):
    while True:
        r_dict = {'Купол 12': IP12, 'Купол 13': IP13}
        for key, val in r_dict.items():
            cmd = f'ping {val} -n 1 -w 100'
            response = subprocess.call(cmd, stdout=subprocess.DEVNULL)
            if not response:
                print(f'Camera {key} - OK')
                счетчик неудач 12 или 13 купола = 0 # Обнуляем при успешном пинге
            else:
                if key == 'Купол 12':
                    счетчик неудач 12 купола += 1 # Увеличиваем счетчик для 12 купола
                    print(f'Camera {key} - Died')
                    if счетчик неудач 12 купола == 5:
                        self.cameras_reboot(RODOS12)
                else:
                    счетчик неудач 13 купола += 1 # Увеличиваем счетчик для 13 купола
                    print(f'Camera {key} - Died')
</code></pre> 

Как можно организовать эту логику без использования глобальных переменных?
  • 20 сентября 2024 г. 19:53
Ответы на вопрос 3
Для решения вашей задачи без использования глобальных переменных вы можете использовать класс для инкапсуляции состояния счетчиков и логики работы с камерами. Таким образом, состояние (например, количество неудачных попыток и количество перезагрузок) будет связано с экземпляром класса.

Вот пример, как это можно реализовать:

```python
import subprocess
import time

class CameraManager:
    def __init__(self, camera_ips, reboot_ips):
        self.camera_ips = camera_ips  # Словарь с именами камер и их IP
        self.reboot_ips = reboot_ips    # Словарь с именами камер и IP для перезагрузки
        self.failure_counters = {key: 0 for key in camera_ips.keys()}  # Счетчики неудачных попыток
        self.reboot_count = 0  # Общее количество перезагрузок

    def ping_camera(self, ip):
        cmd = f'ping {ip} -n 1 -w 100'
        return subprocess.call(cmd, stdout=subprocess.DEVNULL) == 0

    def cameras_checker(self):
        while True:
            for key, val in self.camera_ips.items():
                if self.ping_camera(val):
                    print(f'Camera {key} - OK')
                    self.failure_counters[key] = 0  # Обнуляем счетчик при успешном пинге
                else:
                    print(f'Camera {key} - Died')
                    self.failure_counters[key] += 1  # Увеличиваем счетчик для камеры
                    if self.failure_counters[key] == 5:
                        self.reboot_camera(key)

            time.sleep(1)  # Задержка между проверками

    def reboot_camera(self, camera_name):
        if camera_name in self.reboot_ips:
            print(f'Rebooting {camera_name}...')
            # Здесь можно добавить код для отправки команды на перезагрузку
            self.reboot_count += 1  # Увеличиваем общий счетчик перезагрузок
            print(f'Total reboots: {self.reboot_count}')
            self.failure_counters[camera_name] = 0  # Обнуляем счетчик неудач после перезагрузки

if __name__ == "__main__":
    camera_ips = {
        'Купол 12': '192.168.0.100',
        'Купол 13': '192.168.0.101',
    }
    reboot_ips = {
        'Купол 12': '192.168.1.100',
        'Купол 13': '192.168.1.101',
    }

    manager = CameraManager(camera_ips, reboot_ips)
    manager.cameras_checker()
```

### Объяснение реализации:

1. **Класс `CameraManager`**:
    - Инициализируется с двумя словарями: один для IP камер и другой для IP реле перезагрузки.
    - Содержит `failure_counters` для отслеживания неудачных попыток пинга каждой камеры.
    - Также содержит `reboot_count` для подсчета общего количества перезагрузок.

2. **Метод `ping_camera`**:
    - Определяет, доступна ли камера, возвращая `True` или `False`.

3. **Метод `cameras_checker`**:
    - Работает в бесконечном цикле. Пингует каждую камеру и обновляет соответствующий счетчик.
    - Если после 5 неудачных попыток, инициируется перезагрузка с помощью метода `reboot_camera`.

4. **Метод `reboot_camera`**:
    - Здесь можно добавить логику для фактической перезагрузки камеры и обновления счетчиков.

### Преимущества данного подхода:
- Избегая глобальных переменных, вы улучшаете читаемость и поддержку кода.
- Инкапсуляция состояния в классе позволяет лучше управлять состоянием приложения.
- Код легче тестировать и расширять в будущем.
Как-то так 
def cameras_checker(self):
    cameras = [
        {'name': 'Купол 12', 'ip': IP12, 'rebootIp': RODOS12, 'fails': 0},
        {'name': 'Купол 13', 'ip': IP13, 'rebootIp': RODOS13, 'fails': 0},
    ]
    while True:
        for camera in cameras:
            cmd = f"ping {camera['ip']} -n 1 -w 100"
            response = subprocesscall(cmd, stdout=subprocess.DEVNUL)
            if not response:
                print(f'Camera {camera['name']} ok')
                camera['fails'] = 0
            else:
                camera['fails'] += 1
                print(f"Camera {camera['name']} died")
                if camera['fails'] = 5:
                    self.cameras_reboot(camera['rebootIp'])
        sleep(5)
По феншую и правильно по хорошему бы реализовать класс, в котором будешь хранить состояние счётчиков 

import subprocess
from time import sleep

class CameraMonitor:
    def __init__(self):
        self.IP12 = '192.168.0.100'
        self.IP13 = '192.168.0.101'
        self.RODOS12 = '192.168.1.100'
        self.RODOS13 = '192.168.1.101'
        
        self.fail_count = {'Купол 12': 0, 'Купол 13': 0}
        self.reboot_count = {'Купол 12': 0, 'Купол 13': 0}

    def ping_camera(self, ip):
        cmd = f'ping {ip} -n 1 -w 100'
        response = subprocess.call(cmd, stdout=subprocess.DEVNULL)
        return response == 0

    def cameras_checker(self):
        r_dict = {'Купол 12': self.IP12, 'Купол 13': self.IP13}
        while True:
            for camera, ip in r_dict.items():
                if self.ping_camera(ip):
                    print(f'Camera {camera} - OK')
                    self.fail_count[camera] = 0
                else:
                    self.fail_count[camera] += 1
                    print(f'Camera {camera} - Died')

                    if self.fail_count[camera] >= 5:
                        self.cameras_reboot(camera)

            sleep(5)

    def cameras_reboot(self, camera):
        if camera == 'Купол 12':
            ip = self.RODOS12
        else:
            ip = self.RODOS13
        
        self.reboot_count[camera] += 1 
        print(f'Rebooting {camera} at IP {ip}')

    def info(self):
        for camera, count in self.reboot_count.items():
            print(f'Перезагрузок {camera}: {count}')


if __name__ == "__main__":
    monitor = CameraMonitor()
    monitor.cameras_checker()
Похожие вопросы