Protector Sistemas — Referência técnica para desenvolvedores e integradores Versão 4.0.0 | Março 2026
- Arquitetura
- Stack Tecnológica
- Módulos do Sistema
- Cliente ISAPI — API Reference
- Sistema de Backup v4.0
- Sistema de Sessões
- Auto-Update
- Modelos de Dados
- Build e Deploy
- Configurações e Diretórios
- Parâmetros de Clonagem
O sistema segue uma arquitetura monolítica desktop com separação em camadas:
┌─────────────────────────────────────────────┐
│ GUI Layer │
│ app.py (CustomTkinter) │
│ Screen*: Clientes, Cadastros, Regras, │
│ Backup, Auditoria, Eventos, Manutenção, │
│ Relatórios │
├─────────────────────────────────────────────┤
│ Core Layer │
│ isapi_client.py │ backup.py │ session.py │
│ models.py │ reports.py│ updater.py │
├─────────────────────────────────────────────┤
│ Communication Layer │
│ HTTP/HTTPS + Digest Auth │
│ Protocolo ISAPI │
├─────────────────────────────────────────────┤
│ Hikvision Terminals │
│ Face Recognition Ultra Series │
└─────────────────────────────────────────────┘
Princípios:
- GUI e lógica de negócio no mesmo processo (thread principal para UI, threads auxiliares para operações longas)
- Cada operação pesada roda em thread separada para não travar a interface
- Dados persistidos em JSON (clientes/terminais) e ZIP (backups)
- Sessões persistidas em disco para recover de crash
| Componente | Tecnologia | Versão |
|---|---|---|
| Linguagem | Python | 3.8+ |
| GUI | CustomTkinter | 5.x |
| HTTP Client | requests | 2.x |
| Auth | requests.auth.HTTPDigestAuth | — |
| Excel | openpyxl | 3.x |
| reportlab | 4.x | |
| Empacotamento | PyInstaller | 6.x |
| Instalador | Inno Setup | 6.x |
| Versionamento | packaging | 23.x |
| Testes | pytest | 7.x |
Arquivo monolítico que contém toda a GUI e lógica de apresentação. Organizado por classes de tela:
| Classe | Responsabilidade |
|---|---|
ProtectorISAPIManager |
Classe principal, gerencia navegação e dados globais |
ScreenClientes |
Gestão de clientes e terminais |
ScreenCadastros |
Cadastros de pessoas e biometria |
ScreenRegras |
Regras de acesso |
ScreenBackup |
Backup, restore e sessões |
ScreenAuditoria |
Auditoria de integridade |
ScreenEventos |
Eventos de acesso |
ScreenManutencao |
Manutenção e status |
ScreenRelatorios |
Geração de relatórios |
Padrões de uso:
- Seleção de terminal via
self.app.get_terminal_display_list()eself.app.find_terminal_by_display() - Operações longas em
threading.Thread(target=..., daemon=True) - Atualização de UI via
self.after(0, callback)(thread-safe) - Logs em widgets
CTkTextboxcom scroll automático
Classe ISAPIClient que encapsula toda comunicação com terminais Hikvision via protocolo ISAPI. Gerencia sessões HTTP com Digest Auth e reconexão automática.
Classes BackupManager e BackupManifest. Cria backups ZIP com integridade SHA256.
Classes CloneSession, SessionManager, SerialLocker e enum OperationType. Isolamento por sessão com lock por serial.
Classes GlobalUser, DeviceUser, UserMapping. Identidade global baseada em employeeNo.
Geração de relatórios em Excel (openpyxl) e PDF (reportlab).
Classes Updater e UpdateInfo. Consulta GitHub Releases API para verificar atualizações.
from core.isapi_client import ISAPIClient
from core.models import Terminal
terminal = Terminal(ip="192.168.1.100", port=80, username="admin", password="senha123")
client = ISAPIClient(terminal, timeout=5)client.get_device_info() # Serial, modelo, firmware
client.get_device_status() # Status operacional
client.get_time() # Hora do sistema
client.get_network_info() # IP, MAC, gateway, DHCP
client.get_capabilities() # Features suportadas
client.get_storage_info() # Uso de memória
client.test_connection() # Teste de conectividadeclient.get_all_users() # Lista todos
client.get_user_count() # Contagem
client.search_users(position=0, count=30) # Busca paginada
client.add_user(user_data) # Criar
client.modify_user(user_data) # Atualizar
client.delete_user(employee_no) # Deletar um
client.delete_all_users(callback=None) # Deletar todosclient.get_face_count() # Contagem de faces
client.get_face(employee_no) # Download JPEG
client.add_face(employee_no, jpeg_bytes) # Upload face
client.delete_face(employee_no) # Remover face
client.delete_faces(employee_list, callback) # Batch delete# Clone completo (users + faces + cards)
client.clone_from(source_client, callback)
# Clone facial 1→1
client.clone_faces_from(source_client, callback)
# Clone facial 1→N (multi-dispositivo)
client.clone_faces_multi(source_client, callback)client.get_door_status(door_id=1)
client.remote_door_open(door_id=1) # Abrir
client.remote_door_close(door_id=1) # Fechar
client.remote_door_always_open(door_id=1) # Manter aberta
client.remote_door_always_close(door_id=1) # Manter fechada
client.remote_door_normal(door_id=1) # Modo normalclient.get_all_rules(max_templates=8, max_weeks=8, max_holidays=8)
client.apply_rules(rules, callback)
client.get_schedule_template(template_id)
client.set_schedule_template(template_id, data)
client.get_week_plan(plan_id)
client.set_week_plan(plan_id, data)
client.get_holiday_group(group_id)
client.set_holiday_group(group_id, data)client.get_event_count()
client.get_recent_events(count=100)
client.search_events(start_time, end_time, position=0, count=30)
client.get_all_events(start_time, end_time, max_events=500)
client.collect_events(start_time)client.export_config() # Exportar config binária
client.import_config(data) # Importar config
client.set_time(time_str, timezone) # Ajustar hora
client.set_ntp(server, timezone, port) # Configurar NTP
client.reboot() # Reiniciar
client.factory_reset(keep_network=False) # Reset de fábricabackup_20260301_143000_SER12345_SID-XXXX.zip
├── users.json # Array de UserInfo
├── cards.json # Array de CardInfo
├── faces/
│ ├── EMP001.jpg # JPEG por employeeNo
│ ├── EMP002.jpg
│ └── ...
├── config.bin # Configuração binária do dispositivo
└── manifest.json # Metadados + hash SHA256
{
"version": "4.0",
"device_serial": "DS-K1T344MX-E120241025V041300ENFU5491529",
"model": "DS-K1T344MX-E",
"firmware": "V4.1.300",
"user_count": 150,
"photo_count": 148,
"card_count": 50,
"config_size": 124492,
"session_id": "SID-3A20-C5EE-44A1",
"timestamp": "2026-03-01T14:30:00",
"backup_hash": "a3f2b1c4d5e6f789",
"created_by": "Protector ISAPI Manager v4.0"
}from core.backup import BackupManager
bm = BackupManager(backup_dir="Backups")
# Criar backup
result = bm.create_backup(client, session, callback,
include_faces=True, include_config=True)
# result: {ok, file, size, manifest, error}
# Restaurar
result = bm.restore_backup(zip_path, target_client, session, callback,
skip_config=False)
# result: {ok, users_restored, faces_restored, cards_restored, config_restored, error}
# Validar integridade
result = BackupManager.validate_backup(zip_path)
# result: {valid, manifest, error}
# Listar backups
backups = bm.list_backups(serial=None)Toda operação destrutiva ou de longa duração é envolvida em uma sessão com ID único. Isso garante rastreabilidade, impede operações concorrentes e permite recover de crash.
| Tipo | Uso |
|---|---|
CLONE_NATIVE |
Clonagem entre terminais da mesma marca |
CLONE_CROSS_BRAND |
Clonagem cross-brand (futuro) |
PURGE |
Limpeza completa do dispositivo |
RESTORE |
Restauração de backup |
BACKUP |
Operação de backup standalone |
PENDING → RUNNING → COMPLETED
→ FAILED
→ CANCELLED
from core.session import SessionManager, OperationType
sm = SessionManager(sessions_dir="sessions")
# Criar sessão
session = sm.create_session(
operation_type=OperationType.BACKUP,
dest_serial="SER12345",
dest_ip="192.168.1.100"
)
# Operar
session.start()
session.mark_employee_done("EMP001")
session.mark_employee_done("EMP002")
session.complete()
# Ou em caso de erro
session.fail("Timeout na conexão")from core.session import SerialLocker
locker = SerialLocker()
locker.acquire("SER12345", "SID-XXXX-XXXX-XXXX") # Adquire lock
locker.is_locked("SER12345") # True
locker.release("SER12345") # LiberaO lock impede que duas operações rodem simultaneamente no mesmo terminal. É thread-safe e baseado em threading.Lock.
App inicia
→ Thread background (3s delay)
→ GET https://api.github.com/repos/{owner}/{repo}/releases/latest
→ Compara versão remota vs local (packaging.version)
→ Se mais nova: callback na UI thread
→ Diálogo com release notes
→ Download com progress bar
→ Executa installer /SILENT
→ App fecha
from core.updater import Updater, check_update_async
updater = Updater(
current_version="4.0.0",
github_owner="ProtectorAnalytics",
github_repo="protector-isapi-manager",
token=None # Opcional para repos privados
)
# Check síncrono
info = updater.check_for_update()
if info:
path = updater.download_update(info, progress_callback)
updater.install_update(path)
# Check assíncrono (recomendado)
check_update_async("4.0.0", "ProtectorAnalytics",
"protector-isapi-manager", callback=on_update)Terminal(
ip="192.168.1.100",
port=80,
username="admin",
password="senha123",
name="Recepção",
model="DS-K1T344MX-E",
serial="DS-K1T344MX-E120241025V041300ENFU5491529",
firmware="V4.1.300"
)Client(
name="Empresa XYZ",
terminals=[Terminal(...), Terminal(...)]
)dados.json— Clientes e terminais (estrutura principal)sessions/*.json— Sessões de operaçãobackups/{serial}/*.zip— Backups completosevents/*.json— Eventos coletados
# Build completo com instalador
python build_release.py --clean --installer
# Build + GitHub Release
python build_release.py --clean --installer --github
# Apenas instalador (se .exe já existe)
python build_release.py --skip-build --installer- Clean — Remove
build/edist/ - build_info.py — Atualiza timestamp de build
- file_version_info.txt — Gera metadados de versão para o .exe
- PyInstaller — Gera
dist/Protector_ISAPI_Manager.exe(~32MB) - Inno Setup — Gera
dist/installer/Protector_ISAPI_Manager_v4.0.0_Setup.exe(~33MB) - GitHub Release — Cria release e faz upload (via
ghCLI)
O version.py é a fonte única de verdade:
VERSION = "4.0.0"
VERSION_STATUS = "stable" # stable | beta | rcN
CHANNEL = "release" # release | dev
GITHUB_OWNER = "ProtectorAnalytics"
GITHUB_REPO = "protector-isapi-manager"Para lançar nova versão: altere VERSION em version.py e rode o pipeline.
| Diretório | Conteúdo | Gitignored |
|---|---|---|
backups/ |
Backups ZIP por serial | Sim |
sessions/ |
Sessões JSON | Sim |
events/ |
Eventos coletados | Sim |
assets/ |
Ícones e logos | Não |
docs/ |
Documentação | Não |
tests/ |
Testes unitários | Não |
scripts/ |
Scripts auxiliares .bat | Não |
| Parâmetro | Valor Padrão |
|---|---|
| Timeout de conexão | 5 segundos |
| Porta HTTP | 80 |
| Auth | Digest (HTTPDigestAuth) |
| NTP Server | a.ntp.br |
| NTP Port | 123 |
| NTP Interval | 60 segundos |
Parâmetros calibrados para estabilidade em operações de clonagem facial:
| Parâmetro | Valor | Descrição |
|---|---|---|
COOLDOWN_MIN |
2.0s | Intervalo mínimo entre envios |
COOLDOWN_START |
2.5s | Cooldown inicial |
COOLDOWN_MAX |
20.0s | Cooldown máximo (backoff) |
BACKOFF_MULT |
2.0x | Multiplicador de backoff |
RECOVER_AFTER |
5 | Envios OK para reduzir cooldown |
RECOVER_FACTOR |
0.6 | Fator de redução do cooldown |
REAUTH_EVERY |
25 | Re-auth preventiva a cada N envios |
HEALTH_EVERY |
20 | Health check a cada N envios |
AUTH_RETRY_MAX |
2 | Tentativas de re-auth |
BUSY_MAX_RETRIES |
3 | Retries quando terminal ocupado |
O backoff adaptativo funciona assim: em caso de erro HTTP ou timeout, o cooldown dobra (BACKOFF_MULT). Após RECOVER_AFTER envios bem-sucedidos, o cooldown reduz em 40% (RECOVER_FACTOR). Isso garante que o terminal não fique sobrecarregado.
Protector Sistemas © 2026 — Todos os direitos reservados