Add complete project documentation
Documentation includes: - README.md: Project overview and architecture - API.md: Complete API reference with endpoints - MANUAL_USUARIO.md: User manual in Spanish - INSTALACION.md: Installation and deployment guide - ARQUITECTURA.md: Architecture and database schema - UPLOAD_PANEL.md: CSV upload panel documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
563
docs/API.md
Normal file
563
docs/API.md
Normal file
@@ -0,0 +1,563 @@
|
||||
# Documentacion API
|
||||
|
||||
## Informacion General
|
||||
|
||||
- **URL Base**: `https://api.gestionrecursoshidricos.com/api`
|
||||
- **Formato**: JSON
|
||||
- **Autenticacion**: Bearer Token (JWT)
|
||||
|
||||
## Autenticacion
|
||||
|
||||
### Login
|
||||
```http
|
||||
POST /auth/login
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "usuario@ejemplo.com",
|
||||
"password": "contraseña"
|
||||
}
|
||||
```
|
||||
|
||||
**Respuesta exitosa:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"user": {
|
||||
"id": "uuid",
|
||||
"email": "usuario@ejemplo.com",
|
||||
"name": "Nombre",
|
||||
"role": "ADMIN"
|
||||
},
|
||||
"accessToken": "jwt_token",
|
||||
"refreshToken": "refresh_token"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Refresh Token
|
||||
```http
|
||||
POST /auth/refresh
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"refreshToken": "refresh_token"
|
||||
}
|
||||
```
|
||||
|
||||
### Logout
|
||||
```http
|
||||
POST /auth/logout
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Obtener Perfil
|
||||
```http
|
||||
GET /auth/me
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Proyectos
|
||||
|
||||
### Listar Proyectos
|
||||
```http
|
||||
GET /projects?page=1&pageSize=10
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
**Parametros de consulta:**
|
||||
| Parametro | Tipo | Descripcion |
|
||||
|-----------|------|-------------|
|
||||
| page | number | Numero de pagina (default: 1) |
|
||||
| pageSize | number | Registros por pagina (default: 10) |
|
||||
| status | string | Filtrar por estado (ACTIVE, INACTIVE) |
|
||||
| search | string | Buscar por nombre |
|
||||
|
||||
### Obtener Proyecto
|
||||
```http
|
||||
GET /projects/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Estadisticas del Proyecto
|
||||
```http
|
||||
GET /projects/:id/stats
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Crear Proyecto
|
||||
```http
|
||||
POST /projects
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Nombre del Proyecto",
|
||||
"description": "Descripcion",
|
||||
"area_name": "Nombre del Area",
|
||||
"location": "Ubicacion",
|
||||
"meter_type_id": "uuid-tipo-medidor"
|
||||
}
|
||||
```
|
||||
|
||||
### Actualizar Proyecto
|
||||
```http
|
||||
PUT /projects/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Nuevo Nombre",
|
||||
"status": "ACTIVE"
|
||||
}
|
||||
```
|
||||
|
||||
### Eliminar Proyecto
|
||||
```http
|
||||
DELETE /projects/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
*Requiere rol ADMIN*
|
||||
|
||||
---
|
||||
|
||||
## Concentradores
|
||||
|
||||
### Listar Concentradores
|
||||
```http
|
||||
GET /concentrators?project_id=uuid
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
**Parametros de consulta:**
|
||||
| Parametro | Tipo | Descripcion |
|
||||
|-----------|------|-------------|
|
||||
| project_id | uuid | Filtrar por proyecto |
|
||||
| status | string | Filtrar por estado |
|
||||
| search | string | Buscar por serial o nombre |
|
||||
|
||||
### Obtener Concentrador
|
||||
```http
|
||||
GET /concentrators/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Crear Concentrador
|
||||
```http
|
||||
POST /concentrators
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"serial_number": "CONC001",
|
||||
"name": "Concentrador Principal",
|
||||
"project_id": "uuid-proyecto",
|
||||
"location": "Ubicacion",
|
||||
"ip_address": "192.168.1.100"
|
||||
}
|
||||
```
|
||||
|
||||
### Actualizar Concentrador
|
||||
```http
|
||||
PUT /concentrators/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Nuevo Nombre",
|
||||
"status": "ACTIVE"
|
||||
}
|
||||
```
|
||||
|
||||
### Eliminar Concentrador
|
||||
```http
|
||||
DELETE /concentrators/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Medidores
|
||||
|
||||
### Listar Medidores
|
||||
```http
|
||||
GET /meters?page=1&pageSize=50
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
**Parametros de consulta:**
|
||||
| Parametro | Tipo | Descripcion |
|
||||
|-----------|------|-------------|
|
||||
| page | number | Numero de pagina |
|
||||
| pageSize | number | Registros por pagina |
|
||||
| project_id | uuid | Filtrar por proyecto |
|
||||
| concentrator_id | uuid | Filtrar por concentrador |
|
||||
| status | string | ACTIVE, INACTIVE, OFFLINE, MAINTENANCE, ERROR |
|
||||
| type | string | LORA, LORAWAN, GRANDES CONSUMIDORES |
|
||||
| search | string | Buscar por serial o nombre |
|
||||
|
||||
### Obtener Medidor
|
||||
```http
|
||||
GET /meters/:id
|
||||
```
|
||||
|
||||
### Lecturas del Medidor
|
||||
```http
|
||||
GET /meters/:id/readings?page=1&pageSize=50
|
||||
```
|
||||
|
||||
### Crear Medidor
|
||||
```http
|
||||
POST /meters
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"serial_number": "MED001",
|
||||
"name": "Medidor 001",
|
||||
"concentrator_id": "uuid-concentrador",
|
||||
"project_id": "uuid-proyecto",
|
||||
"area_name": "Zona A",
|
||||
"location": "Depto 101",
|
||||
"type": "LORA",
|
||||
"status": "ACTIVE",
|
||||
"installation_date": "2024-01-15"
|
||||
}
|
||||
```
|
||||
|
||||
### Actualizar Medidor
|
||||
```http
|
||||
PUT /meters/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Nuevo Nombre",
|
||||
"status": "MAINTENANCE"
|
||||
}
|
||||
```
|
||||
|
||||
### Actualizar Parcial (PATCH)
|
||||
```http
|
||||
PATCH /meters/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"status": "ACTIVE"
|
||||
}
|
||||
```
|
||||
|
||||
### Eliminar Medidor
|
||||
```http
|
||||
DELETE /meters/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
*Requiere rol ADMIN*
|
||||
|
||||
---
|
||||
|
||||
## Lecturas
|
||||
|
||||
### Listar Lecturas
|
||||
```http
|
||||
GET /readings?page=1&pageSize=50
|
||||
```
|
||||
|
||||
**Parametros de consulta:**
|
||||
| Parametro | Tipo | Descripcion |
|
||||
|-----------|------|-------------|
|
||||
| meter_id | uuid | Filtrar por medidor |
|
||||
| project_id | uuid | Filtrar por proyecto |
|
||||
| concentrator_id | uuid | Filtrar por concentrador |
|
||||
| start_date | date | Fecha inicio (YYYY-MM-DD) |
|
||||
| end_date | date | Fecha fin (YYYY-MM-DD) |
|
||||
| reading_type | string | AUTOMATIC, MANUAL, SCHEDULED |
|
||||
|
||||
### Resumen de Consumo
|
||||
```http
|
||||
GET /readings/summary?project_id=uuid
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"totalReadings": 1500,
|
||||
"totalMeters": 50,
|
||||
"avgReading": 125.5,
|
||||
"lastReadingDate": "2024-01-20T10:30:00Z"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Crear Lectura
|
||||
```http
|
||||
POST /readings
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"meter_id": "uuid-medidor",
|
||||
"reading_value": 1234.56,
|
||||
"reading_type": "MANUAL",
|
||||
"battery_level": 85,
|
||||
"signal_strength": -45,
|
||||
"received_at": "2024-01-20T10:30:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Eliminar Lectura
|
||||
```http
|
||||
DELETE /readings/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
*Requiere rol ADMIN*
|
||||
|
||||
---
|
||||
|
||||
## Carga CSV (Sin Autenticacion)
|
||||
|
||||
### Subir Medidores CSV
|
||||
```http
|
||||
POST /csv-upload/meters
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: archivo.csv
|
||||
```
|
||||
|
||||
**Formato CSV:**
|
||||
```csv
|
||||
serial_number,name,concentrator_serial,area_name,location,meter_type,status,installation_date
|
||||
MED001,Medidor 1,CONC001,Zona A,Depto 101,LORA,ACTIVE,2024-01-15
|
||||
```
|
||||
|
||||
**Respuesta:**
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "Procesamiento completado: 10 insertados, 5 actualizados, 2 errores",
|
||||
"data": {
|
||||
"total": 17,
|
||||
"inserted": 10,
|
||||
"updated": 5,
|
||||
"errors": [
|
||||
{
|
||||
"row": 15,
|
||||
"field": "concentrator_serial",
|
||||
"message": "Concentrador no encontrado"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Subir Lecturas CSV
|
||||
```http
|
||||
POST /csv-upload/readings
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: archivo.csv
|
||||
```
|
||||
|
||||
**Formato CSV:**
|
||||
```csv
|
||||
meter_serial,reading_value,received_at,reading_type,battery_level,signal_strength
|
||||
MED001,1234.56,2024-01-20 10:30:00,MANUAL,85,-45
|
||||
```
|
||||
|
||||
### Descargar Plantilla Medidores
|
||||
```http
|
||||
GET /csv-upload/meters/template
|
||||
```
|
||||
|
||||
### Descargar Plantilla Lecturas
|
||||
```http
|
||||
GET /csv-upload/readings/template
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Usuarios
|
||||
|
||||
### Listar Usuarios
|
||||
```http
|
||||
GET /users
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
*Requiere rol ADMIN*
|
||||
|
||||
### Crear Usuario
|
||||
```http
|
||||
POST /users
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"email": "nuevo@ejemplo.com",
|
||||
"password": "contraseña123",
|
||||
"name": "Nombre Usuario",
|
||||
"role_id": "uuid-rol",
|
||||
"project_id": "uuid-proyecto"
|
||||
}
|
||||
```
|
||||
*Requiere rol ADMIN*
|
||||
|
||||
### Actualizar Usuario
|
||||
```http
|
||||
PUT /users/:id
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Nuevo Nombre",
|
||||
"is_active": true
|
||||
}
|
||||
```
|
||||
|
||||
### Cambiar Contraseña
|
||||
```http
|
||||
PUT /users/:id/password
|
||||
Authorization: Bearer {accessToken}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"currentPassword": "actual",
|
||||
"newPassword": "nueva123"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Roles
|
||||
|
||||
### Listar Roles
|
||||
```http
|
||||
GET /roles
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
**Roles disponibles:**
|
||||
| Rol | Descripcion |
|
||||
|-----|-------------|
|
||||
| ADMIN | Acceso completo al sistema |
|
||||
| OPERATOR | Gestion de medidores y lecturas de su proyecto |
|
||||
| VIEWER | Solo lectura |
|
||||
|
||||
---
|
||||
|
||||
## Notificaciones
|
||||
|
||||
### Listar Notificaciones
|
||||
```http
|
||||
GET /notifications?page=1&pageSize=20
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Contador No Leidas
|
||||
```http
|
||||
GET /notifications/unread-count
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Marcar como Leida
|
||||
```http
|
||||
PATCH /notifications/:id/read
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
### Marcar Todas como Leidas
|
||||
```http
|
||||
PATCH /notifications/read-all
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Auditoria
|
||||
|
||||
### Listar Logs de Auditoria
|
||||
```http
|
||||
GET /audit-logs?page=1&pageSize=50
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
*Requiere rol ADMIN*
|
||||
|
||||
**Parametros de consulta:**
|
||||
| Parametro | Tipo | Descripcion |
|
||||
|-----------|------|-------------|
|
||||
| user_id | uuid | Filtrar por usuario |
|
||||
| action | string | CREATE, UPDATE, DELETE, LOGIN, etc. |
|
||||
| table_name | string | Filtrar por tabla |
|
||||
| start_date | date | Fecha inicio |
|
||||
| end_date | date | Fecha fin |
|
||||
|
||||
### Mi Actividad
|
||||
```http
|
||||
GET /audit-logs/my-activity
|
||||
Authorization: Bearer {accessToken}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Webhooks TTS (The Things Stack)
|
||||
|
||||
### Health Check
|
||||
```http
|
||||
GET /webhooks/tts/health
|
||||
```
|
||||
|
||||
### Uplink (Datos de Dispositivos)
|
||||
```http
|
||||
POST /webhooks/tts/uplink
|
||||
X-Downlink-Apikey: {webhook_secret}
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"end_device_ids": {
|
||||
"device_id": "device-001",
|
||||
"dev_eui": "0004A30B001C1234"
|
||||
},
|
||||
"uplink_message": {
|
||||
"decoded_payload": {
|
||||
"reading": 1234.56,
|
||||
"battery": 85
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Join Accept
|
||||
```http
|
||||
POST /webhooks/tts/join
|
||||
X-Downlink-Apikey: {webhook_secret}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Codigos de Respuesta
|
||||
|
||||
| Codigo | Descripcion |
|
||||
|--------|-------------|
|
||||
| 200 | Exito |
|
||||
| 201 | Creado exitosamente |
|
||||
| 400 | Error en la solicitud |
|
||||
| 401 | No autorizado |
|
||||
| 403 | Prohibido (sin permisos) |
|
||||
| 404 | No encontrado |
|
||||
| 409 | Conflicto (duplicado) |
|
||||
| 500 | Error interno del servidor |
|
||||
|
||||
## Formato de Error
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Descripcion del error",
|
||||
"error": "CODIGO_ERROR"
|
||||
}
|
||||
```
|
||||
496
docs/ARQUITECTURA.md
Normal file
496
docs/ARQUITECTURA.md
Normal file
@@ -0,0 +1,496 @@
|
||||
# Arquitectura y Base de Datos
|
||||
|
||||
## Arquitectura General
|
||||
|
||||
### Diagrama de Componentes
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CLIENTES │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ │
|
||||
│ │ Navegador Web │ │ Navegador Web │ │ Dispositivos │ │
|
||||
│ │ (App Principal)│ │ (Panel Carga) │ │ LoRaWAN │ │
|
||||
│ └────────┬─────────┘ └────────┬─────────┘ └────────┬─────────┘ │
|
||||
│ │ │ │ │
|
||||
└────────────┼───────────────────────┼───────────────────────┼────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CAPA DE PRESENTACION │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ NGINX (Reverse Proxy) │ │
|
||||
│ │ - SSL/TLS Termination │ │
|
||||
│ │ - Load Balancing │ │
|
||||
│ │ - Static File Serving │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌────────────────────┐ ┌────────────────────┐ │
|
||||
│ │ Frontend React │ │ Upload Panel │ │
|
||||
│ │ (sistema.grh.com) │ │ (panel.grh.com) │ │
|
||||
│ │ Puerto: 5173 │ │ Puerto: 5174 │ │
|
||||
│ └────────────────────┘ └────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CAPA DE SERVICIOS │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ Express.js API Server │ │
|
||||
│ │ (api.grh.com - Puerto 3000) │ │
|
||||
│ ├──────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
||||
│ │ │ Routes │ │ Controllers │ │ Services │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │ - auth │ │ - auth │ │ - auth │ │ │
|
||||
│ │ │ - projects │ │ - project │ │ - project │ │ │
|
||||
│ │ │ - meters │ │ - meter │ │ - meter │ │ │
|
||||
│ │ │ - readings │ │ - reading │ │ - reading │ │ │
|
||||
│ │ │ - users │ │ - user │ │ - user │ │ │
|
||||
│ │ │ - csv-upload│ │ - etc... │ │ - csv-upload│ │ │
|
||||
│ │ │ - webhooks │ │ │ │ - tts │ │ │
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
||||
│ │ │ Middleware │ │ Validators │ │ Jobs │ │ │
|
||||
│ │ │ │ │ │ │ │ │ │
|
||||
│ │ │ - auth │ │ - zod │ │ - cron │ │ │
|
||||
│ │ │ - audit │ │ - schemas │ │ - negative │ │ │
|
||||
│ │ │ - tts verify│ │ │ │ flow │ │ │
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CAPA DE DATOS │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ PostgreSQL Database │ │
|
||||
│ │ Puerto: 5432 │ │
|
||||
│ ├──────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
||||
│ │ │ users │ │ projects │ │concentrators│ │ │
|
||||
│ │ │ roles │ │ gateways │ │ meters │ │ │
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
|
||||
│ │ │ readings │ │ devices │ │ audit_logs │ │ │
|
||||
│ │ │ (meter_ │ │ tts_uplink │ │notifications│ │ │
|
||||
│ │ │ readings) │ │ _logs │ │ │ │ │
|
||||
│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ SERVICIOS EXTERNOS │
|
||||
├─────────────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────────────────────┐ │
|
||||
│ │ The Things Stack (TTS) │ │
|
||||
│ │ Plataforma LoRaWAN │ │
|
||||
│ ├──────────────────────────────────────────────────────────────────┤ │
|
||||
│ │ - Recepcion de uplinks de dispositivos │ │
|
||||
│ │ - Gestion de dispositivos LoRaWAN │ │
|
||||
│ │ - Webhooks hacia la API │ │
|
||||
│ └──────────────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Modelo de Datos
|
||||
|
||||
### Diagrama Entidad-Relacion
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ roles │ │ users │ │ projects │
|
||||
├─────────────┤ ├─────────────────┤ ├─────────────────┤
|
||||
│ id (PK) │──┐ │ id (PK) │ ┌──│ id (PK) │
|
||||
│ name │ └───▶│ role_id (FK) │ │ │ name │
|
||||
│ description │ │ project_id (FK) │◀───┤ │ description │
|
||||
│ permissions │ │ email │ │ │ area_name │
|
||||
└─────────────┘ │ password_hash │ │ │ status │
|
||||
│ name │ │ │ created_by (FK) │──▶ users
|
||||
│ is_active │ │ │ meter_type_id │──▶ meter_types
|
||||
└─────────────────┘ │ └─────────────────┘
|
||||
│ │
|
||||
│ │
|
||||
┌─────────────────┐ │ │
|
||||
│ concentrators │◀───┘ │
|
||||
├─────────────────┤ │
|
||||
│ id (PK) │ │
|
||||
│ serial_number │ │
|
||||
│ name │ │
|
||||
│ project_id (FK) │◀───────────────┘
|
||||
│ status │
|
||||
│ ip_address │
|
||||
└─────────────────┘
|
||||
│
|
||||
│
|
||||
┌────────┴────────┐
|
||||
▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐
|
||||
│ gateways │ │ meters │
|
||||
├─────────────────┤ ├─────────────────┤
|
||||
│ id (PK) │ │ id (PK) │
|
||||
│ gateway_id │ │ serial_number │
|
||||
│ name │ │ name │
|
||||
│ project_id (FK) │ │ project_id (FK) │
|
||||
│ concentrator_id │ │ concentrator_id │
|
||||
│ status │ │ device_id (FK) │──▶ devices
|
||||
└─────────────────┘ │ type │
|
||||
│ │ status │
|
||||
│ │ last_reading │
|
||||
▼ └─────────────────┘
|
||||
┌─────────────────┐ │
|
||||
│ devices │ │
|
||||
├─────────────────┤ ▼
|
||||
│ id (PK) │ ┌─────────────────┐
|
||||
│ dev_eui │ │ meter_readings │
|
||||
│ name │ ├─────────────────┤
|
||||
│ project_id (FK) │ │ id (PK) │
|
||||
│ gateway_id (FK) │ │ meter_id (FK) │
|
||||
│ status │ │ reading_value │
|
||||
└─────────────────┘ │ reading_type │
|
||||
│ │ battery_level │
|
||||
│ │ signal_strength │
|
||||
▼ │ received_at │
|
||||
┌─────────────────┐ └─────────────────┘
|
||||
│ tts_uplink_logs │
|
||||
├─────────────────┤
|
||||
│ id (PK) │
|
||||
│ device_id (FK) │
|
||||
│ raw_payload │
|
||||
│ decoded_payload │
|
||||
│ processed │
|
||||
└─────────────────┘
|
||||
|
||||
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ audit_logs │ │ notifications │ │ meter_types │
|
||||
├─────────────────┤ ├─────────────────┤ ├─────────────────┤
|
||||
│ id (PK) │ │ id (PK) │ │ id (PK) │
|
||||
│ user_id (FK) │ │ user_id (FK) │ │ name │
|
||||
│ action │ │ meter_id (FK) │ │ code │
|
||||
│ table_name │ │ type │ │ description │
|
||||
│ record_id │ │ title │ │ is_active │
|
||||
│ old_values │ │ message │ └─────────────────┘
|
||||
│ new_values │ │ is_read │
|
||||
└─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Descripcion de Tablas
|
||||
|
||||
### Tablas de Autenticacion y Usuarios
|
||||
|
||||
#### `roles`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| name | ENUM | ADMIN, OPERATOR, VIEWER |
|
||||
| description | TEXT | Descripcion del rol |
|
||||
| permissions | JSONB | Permisos detallados |
|
||||
|
||||
#### `users`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| email | VARCHAR | Email unico (login) |
|
||||
| password_hash | VARCHAR | Hash bcrypt de contraseña |
|
||||
| name | VARCHAR | Nombre completo |
|
||||
| role_id | UUID FK | Rol asignado |
|
||||
| project_id | UUID FK | Proyecto asignado (OPERATOR) |
|
||||
| is_active | BOOLEAN | Estado de la cuenta |
|
||||
| last_login | TIMESTAMP | Ultimo acceso |
|
||||
|
||||
#### `refresh_tokens`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| user_id | UUID FK | Usuario propietario |
|
||||
| token_hash | VARCHAR | Hash del token |
|
||||
| expires_at | TIMESTAMP | Fecha de expiracion |
|
||||
| revoked_at | TIMESTAMP | Fecha de revocacion |
|
||||
|
||||
---
|
||||
|
||||
### Tablas de Estructura
|
||||
|
||||
#### `projects`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| name | VARCHAR | Nombre del proyecto |
|
||||
| description | TEXT | Descripcion |
|
||||
| area_name | VARCHAR | Nombre del area |
|
||||
| location | TEXT | Ubicacion |
|
||||
| status | ENUM | ACTIVE, INACTIVE, COMPLETED |
|
||||
| meter_type_id | UUID FK | Tipo de medidor por defecto |
|
||||
| created_by | UUID FK | Usuario creador |
|
||||
|
||||
#### `concentrators`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| serial_number | VARCHAR | Numero de serie unico |
|
||||
| name | VARCHAR | Nombre descriptivo |
|
||||
| project_id | UUID FK | Proyecto asociado |
|
||||
| location | TEXT | Ubicacion fisica |
|
||||
| status | ENUM | Estado del concentrador |
|
||||
| ip_address | VARCHAR | Direccion IP |
|
||||
| firmware_version | VARCHAR | Version de firmware |
|
||||
| last_communication | TIMESTAMP | Ultima comunicacion |
|
||||
|
||||
#### `gateways`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| gateway_id | VARCHAR | ID unico del gateway |
|
||||
| name | VARCHAR | Nombre descriptivo |
|
||||
| project_id | UUID FK | Proyecto asociado |
|
||||
| concentrator_id | UUID FK | Concentrador asociado |
|
||||
| location | TEXT | Ubicacion |
|
||||
| status | ENUM | Estado |
|
||||
| tts_gateway_id | VARCHAR | ID en The Things Stack |
|
||||
|
||||
---
|
||||
|
||||
### Tablas de Medicion
|
||||
|
||||
#### `meters`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| serial_number | VARCHAR | Numero de serie unico |
|
||||
| name | VARCHAR | Nombre descriptivo |
|
||||
| project_id | UUID FK | Proyecto asociado |
|
||||
| concentrator_id | UUID FK | Concentrador asociado |
|
||||
| device_id | UUID FK | Dispositivo LoRaWAN asociado |
|
||||
| area_name | VARCHAR | Nombre del area |
|
||||
| location | TEXT | Ubicacion especifica |
|
||||
| type | VARCHAR | LORA, LORAWAN, GRANDES CONSUMIDORES |
|
||||
| status | ENUM | ACTIVE, INACTIVE, OFFLINE, MAINTENANCE, ERROR |
|
||||
| last_reading_value | NUMERIC | Ultima lectura registrada |
|
||||
| last_reading_at | TIMESTAMP | Fecha de ultima lectura |
|
||||
| installation_date | DATE | Fecha de instalacion |
|
||||
|
||||
**Campos extendidos:**
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| protocol | VARCHAR | Protocolo de comunicacion |
|
||||
| mac | VARCHAR | Direccion MAC |
|
||||
| voltage | DECIMAL | Voltaje |
|
||||
| signal | INTEGER | Intensidad de senal |
|
||||
| leakage_status | VARCHAR | Estado de fuga |
|
||||
| burst_status | VARCHAR | Estado de ruptura |
|
||||
| current_flow | DECIMAL | Flujo actual |
|
||||
| latitude | DECIMAL | Latitud GPS |
|
||||
| longitude | DECIMAL | Longitud GPS |
|
||||
| data | JSONB | Datos adicionales flexibles |
|
||||
|
||||
#### `meter_readings`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| meter_id | UUID FK | Medidor asociado |
|
||||
| device_id | UUID FK | Dispositivo origen |
|
||||
| reading_value | NUMERIC | Valor de la lectura |
|
||||
| reading_type | ENUM | AUTOMATIC, MANUAL, SCHEDULED |
|
||||
| battery_level | SMALLINT | Nivel de bateria (0-100) |
|
||||
| signal_strength | SMALLINT | Intensidad de senal (dBm) |
|
||||
| raw_payload | TEXT | Payload crudo del dispositivo |
|
||||
| received_at | TIMESTAMP | Fecha/hora de recepcion |
|
||||
|
||||
#### `meter_types`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| name | VARCHAR | Nombre del tipo |
|
||||
| code | VARCHAR | Codigo unico |
|
||||
| description | TEXT | Descripcion |
|
||||
| is_active | BOOLEAN | Estado activo |
|
||||
|
||||
---
|
||||
|
||||
### Tablas de IoT (The Things Stack)
|
||||
|
||||
#### `devices`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| dev_eui | VARCHAR | DevEUI unico del dispositivo |
|
||||
| name | VARCHAR | Nombre descriptivo |
|
||||
| device_type | VARCHAR | Tipo de dispositivo |
|
||||
| project_id | UUID FK | Proyecto asociado |
|
||||
| gateway_id | UUID FK | Gateway asociado |
|
||||
| status | ENUM | Estado del dispositivo |
|
||||
| tts_device_id | VARCHAR | ID en TTS |
|
||||
| tts_status | VARCHAR | Estado en TTS |
|
||||
| app_key | VARCHAR | Application Key |
|
||||
| join_eui | VARCHAR | Join EUI |
|
||||
|
||||
#### `tts_uplink_logs`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| device_id | UUID FK | Dispositivo origen |
|
||||
| dev_eui | VARCHAR | DevEUI |
|
||||
| raw_payload | JSONB | Payload completo |
|
||||
| decoded_payload | JSONB | Payload decodificado |
|
||||
| gateway_ids | TEXT[] | IDs de gateways |
|
||||
| rssi | INTEGER | RSSI |
|
||||
| snr | FLOAT | SNR |
|
||||
| processed | BOOLEAN | Indica si fue procesado |
|
||||
| error_message | TEXT | Mensaje de error si aplica |
|
||||
|
||||
---
|
||||
|
||||
### Tablas de Sistema
|
||||
|
||||
#### `audit_logs`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| user_id | UUID FK | Usuario que realizo la accion |
|
||||
| user_email | VARCHAR | Email del usuario |
|
||||
| user_name | VARCHAR | Nombre del usuario |
|
||||
| action | ENUM | CREATE, UPDATE, DELETE, LOGIN, etc. |
|
||||
| table_name | VARCHAR | Tabla afectada |
|
||||
| record_id | UUID | ID del registro afectado |
|
||||
| old_values | JSONB | Valores anteriores |
|
||||
| new_values | JSONB | Valores nuevos |
|
||||
| description | TEXT | Descripcion de la accion |
|
||||
| ip_address | VARCHAR | IP del cliente |
|
||||
| user_agent | TEXT | User Agent del navegador |
|
||||
| success | BOOLEAN | Resultado de la operacion |
|
||||
| error_message | TEXT | Mensaje de error si fallo |
|
||||
|
||||
#### `notifications`
|
||||
| Campo | Tipo | Descripcion |
|
||||
|-------|------|-------------|
|
||||
| id | UUID | Identificador unico |
|
||||
| user_id | UUID FK | Usuario destinatario |
|
||||
| meter_id | UUID FK | Medidor relacionado (opcional) |
|
||||
| notification_type | ENUM | NEGATIVE_FLOW, SYSTEM_ALERT, MAINTENANCE |
|
||||
| title | VARCHAR | Titulo de la notificacion |
|
||||
| message | TEXT | Mensaje detallado |
|
||||
| meter_serial_number | VARCHAR | Serial del medidor (si aplica) |
|
||||
| flow_value | DECIMAL | Valor de flujo (si aplica) |
|
||||
| is_read | BOOLEAN | Estado de lectura |
|
||||
| read_at | TIMESTAMP | Fecha de lectura |
|
||||
|
||||
---
|
||||
|
||||
## Indices
|
||||
|
||||
### Indices Principales
|
||||
|
||||
```sql
|
||||
-- Meters
|
||||
CREATE INDEX idx_meters_serial_number ON meters(serial_number);
|
||||
CREATE INDEX idx_meters_project_id ON meters(project_id);
|
||||
CREATE INDEX idx_meters_concentrator_id ON meters(concentrator_id);
|
||||
CREATE INDEX idx_meters_status ON meters(status);
|
||||
CREATE INDEX idx_meters_type ON meters(type);
|
||||
|
||||
-- Readings
|
||||
CREATE INDEX idx_meter_readings_meter_id ON meter_readings(meter_id);
|
||||
CREATE INDEX idx_meter_readings_received_at ON meter_readings(received_at);
|
||||
CREATE INDEX idx_meter_readings_meter_id_received_at ON meter_readings(meter_id, received_at);
|
||||
|
||||
-- Devices
|
||||
CREATE INDEX idx_devices_dev_eui ON devices(dev_eui);
|
||||
CREATE INDEX idx_devices_project_id ON devices(project_id);
|
||||
|
||||
-- Audit
|
||||
CREATE INDEX idx_audit_logs_user_id ON audit_logs(user_id);
|
||||
CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at);
|
||||
CREATE INDEX idx_audit_logs_table_name ON audit_logs(table_name);
|
||||
|
||||
-- Notifications
|
||||
CREATE INDEX idx_notifications_user_id ON notifications(user_id);
|
||||
CREATE INDEX idx_notifications_is_read ON notifications(is_read);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Flujo de Datos
|
||||
|
||||
### Flujo de Lectura Automatica (TTS)
|
||||
|
||||
```
|
||||
1. Dispositivo LoRaWAN envia uplink
|
||||
│
|
||||
▼
|
||||
2. The Things Stack recibe el mensaje
|
||||
│
|
||||
▼
|
||||
3. TTS envia webhook a /api/webhooks/tts/uplink
|
||||
│
|
||||
▼
|
||||
4. API verifica firma del webhook (X-Downlink-Apikey)
|
||||
│
|
||||
▼
|
||||
5. API guarda en tts_uplink_logs
|
||||
│
|
||||
▼
|
||||
6. API busca device por dev_eui
|
||||
│
|
||||
▼
|
||||
7. API busca meter asociado al device
|
||||
│
|
||||
▼
|
||||
8. API crea registro en meter_readings
|
||||
│
|
||||
▼
|
||||
9. API actualiza last_reading en meters
|
||||
│
|
||||
▼
|
||||
10. Job de deteccion de flujo negativo evalua la lectura
|
||||
│
|
||||
▼
|
||||
11. Si detecta anomalia, crea notification
|
||||
```
|
||||
|
||||
### Flujo de Carga CSV
|
||||
|
||||
```
|
||||
1. Usuario sube archivo CSV
|
||||
│
|
||||
▼
|
||||
2. API parsea el CSV
|
||||
│
|
||||
▼
|
||||
3. Por cada fila:
|
||||
│
|
||||
├─▶ Validar campos requeridos
|
||||
│
|
||||
├─▶ Buscar concentrador por serial
|
||||
│
|
||||
├─▶ Si meter existe: UPDATE
|
||||
│ Si no existe: INSERT
|
||||
│
|
||||
└─▶ Registrar resultado (exito/error)
|
||||
│
|
||||
▼
|
||||
4. Retornar resumen de procesamiento
|
||||
```
|
||||
390
docs/INSTALACION.md
Normal file
390
docs/INSTALACION.md
Normal file
@@ -0,0 +1,390 @@
|
||||
# Guia de Instalacion
|
||||
|
||||
## Requisitos Previos
|
||||
|
||||
### Software Requerido
|
||||
- **Node.js** 18.x o superior
|
||||
- **npm** 9.x o superior
|
||||
- **PostgreSQL** 14.x o superior
|
||||
- **Git**
|
||||
|
||||
### Puertos Utilizados
|
||||
| Servicio | Puerto |
|
||||
|----------|--------|
|
||||
| Frontend Principal | 5173 |
|
||||
| Panel de Carga CSV | 5174 |
|
||||
| Backend API | 3000 |
|
||||
| PostgreSQL | 5432 |
|
||||
|
||||
---
|
||||
|
||||
## Instalacion del Backend (water-api)
|
||||
|
||||
### 1. Clonar el Repositorio
|
||||
```bash
|
||||
git clone https://git.consultoria-as.com/consultoria-as/water-project.git
|
||||
cd water-project
|
||||
```
|
||||
|
||||
### 2. Configurar la Base de Datos
|
||||
|
||||
#### Crear la base de datos:
|
||||
```bash
|
||||
sudo -u postgres psql
|
||||
```
|
||||
|
||||
```sql
|
||||
CREATE DATABASE water_project;
|
||||
CREATE USER water_user WITH PASSWORD 'tu_password_seguro';
|
||||
GRANT ALL PRIVILEGES ON DATABASE water_project TO water_user;
|
||||
\q
|
||||
```
|
||||
|
||||
#### Ejecutar los scripts SQL:
|
||||
```bash
|
||||
cd water-api/sql
|
||||
psql -U water_user -d water_project -f schema.sql
|
||||
psql -U water_user -d water_project -f add_audit_logs.sql
|
||||
psql -U water_user -d water_project -f add_notifications.sql
|
||||
psql -U water_user -d water_project -f add_meter_extended_fields.sql
|
||||
psql -U water_user -d water_project -f add_meter_project_relation.sql
|
||||
psql -U water_user -d water_project -f add_meter_types.sql
|
||||
```
|
||||
|
||||
### 3. Configurar Variables de Entorno
|
||||
|
||||
```bash
|
||||
cd water-api
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Editar `.env`:
|
||||
```env
|
||||
# Server
|
||||
PORT=3000
|
||||
NODE_ENV=production
|
||||
|
||||
# Database
|
||||
DB_HOST=localhost
|
||||
DB_PORT=5432
|
||||
DB_NAME=water_project
|
||||
DB_USER=water_user
|
||||
DB_PASSWORD=tu_password_seguro
|
||||
|
||||
# JWT (generar claves seguras)
|
||||
JWT_ACCESS_SECRET=clave_secreta_acceso_minimo_32_caracteres
|
||||
JWT_REFRESH_SECRET=clave_secreta_refresh_minimo_32_caracteres
|
||||
JWT_ACCESS_EXPIRES_IN=15m
|
||||
JWT_REFRESH_EXPIRES_IN=7d
|
||||
|
||||
# CORS (URLs del frontend separadas por coma)
|
||||
CORS_ORIGIN=http://localhost:5173,http://localhost:5174,https://sistema.gestionrecursoshidricos.com,https://panel.gestionrecursoshidricos.com
|
||||
|
||||
# TTS (The Things Stack) - Opcional
|
||||
TTS_ENABLED=false
|
||||
TTS_BASE_URL=https://your-tts-server.com
|
||||
TTS_APPLICATION_ID=your-app-id
|
||||
TTS_API_KEY=your-api-key
|
||||
TTS_WEBHOOK_SECRET=your-webhook-secret
|
||||
```
|
||||
|
||||
### 4. Instalar Dependencias y Ejecutar
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Desarrollo con hot-reload
|
||||
# o
|
||||
npm run build # Compilar para produccion
|
||||
npm start # Ejecutar version compilada
|
||||
```
|
||||
|
||||
### 5. Verificar Instalacion
|
||||
```bash
|
||||
curl http://localhost:3000/health
|
||||
```
|
||||
|
||||
Respuesta esperada:
|
||||
```json
|
||||
{
|
||||
"status": "ok",
|
||||
"timestamp": "2024-01-20T10:30:00.000Z",
|
||||
"environment": "production"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Instalacion del Frontend Principal
|
||||
|
||||
### 1. Configurar Variables de Entorno
|
||||
|
||||
```bash
|
||||
cd /path/to/water-project
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Editar `.env`:
|
||||
```env
|
||||
VITE_API_BASE_URL=http://localhost:3000
|
||||
```
|
||||
|
||||
Para produccion:
|
||||
```env
|
||||
VITE_API_BASE_URL=https://api.gestionrecursoshidricos.com
|
||||
```
|
||||
|
||||
### 2. Instalar Dependencias y Ejecutar
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Desarrollo
|
||||
# o
|
||||
npm run build # Compilar para produccion
|
||||
npm run preview # Vista previa de produccion
|
||||
```
|
||||
|
||||
### 3. Verificar Instalacion
|
||||
Abrir en el navegador: http://localhost:5173
|
||||
|
||||
---
|
||||
|
||||
## Instalacion del Panel de Carga CSV
|
||||
|
||||
### 1. Configurar Variables de Entorno
|
||||
|
||||
```bash
|
||||
cd upload-panel
|
||||
cp .env.example .env # Si existe, o crear manualmente
|
||||
```
|
||||
|
||||
Crear `.env`:
|
||||
```env
|
||||
VITE_API_URL=http://localhost:3000/api
|
||||
```
|
||||
|
||||
Para produccion:
|
||||
```env
|
||||
VITE_API_URL=https://api.gestionrecursoshidricos.com/api
|
||||
```
|
||||
|
||||
### 2. Instalar Dependencias y Ejecutar
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run dev # Desarrollo (puerto 5174)
|
||||
# o
|
||||
npm run build # Compilar para produccion
|
||||
```
|
||||
|
||||
### 3. Verificar Instalacion
|
||||
Abrir en el navegador: http://localhost:5174
|
||||
|
||||
---
|
||||
|
||||
## Despliegue en Produccion
|
||||
|
||||
### Opcion 1: PM2 (Recomendado)
|
||||
|
||||
#### Instalar PM2:
|
||||
```bash
|
||||
npm install -g pm2
|
||||
```
|
||||
|
||||
#### Configurar ecosystem.config.js:
|
||||
```javascript
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'water-api',
|
||||
cwd: '/path/to/water-project/water-api',
|
||||
script: 'npm',
|
||||
args: 'start',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3000
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
#### Iniciar con PM2:
|
||||
```bash
|
||||
pm2 start ecosystem.config.js
|
||||
pm2 save
|
||||
pm2 startup # Configurar inicio automatico
|
||||
```
|
||||
|
||||
### Opcion 2: Systemd Service
|
||||
|
||||
Crear `/etc/systemd/system/water-api.service`:
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Water API Server
|
||||
After=network.target postgresql.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www-data
|
||||
WorkingDirectory=/path/to/water-project/water-api
|
||||
ExecStart=/usr/bin/node dist/index.js
|
||||
Restart=on-failure
|
||||
Environment=NODE_ENV=production
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
```bash
|
||||
sudo systemctl enable water-api
|
||||
sudo systemctl start water-api
|
||||
```
|
||||
|
||||
### Configurar Nginx (Reverse Proxy)
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/water-project
|
||||
|
||||
# API
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name api.gestionrecursoshidricos.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://localhost:3000;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
}
|
||||
}
|
||||
|
||||
# Frontend Principal
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name sistema.gestionrecursoshidricos.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
root /path/to/water-project/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
|
||||
# Panel de Carga
|
||||
server {
|
||||
listen 443 ssl;
|
||||
server_name panel.gestionrecursoshidricos.com;
|
||||
|
||||
ssl_certificate /path/to/cert.pem;
|
||||
ssl_certificate_key /path/to/key.pem;
|
||||
|
||||
root /path/to/water-project/upload-panel/dist;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Crear Usuario Administrador Inicial
|
||||
|
||||
### Via SQL:
|
||||
```sql
|
||||
-- Primero obtener el ID del rol ADMIN
|
||||
SELECT id FROM roles WHERE name = 'ADMIN';
|
||||
|
||||
-- Crear usuario (password debe ser hash bcrypt)
|
||||
-- Puedes usar: https://bcrypt-generator.com/ para generar el hash
|
||||
INSERT INTO users (email, password_hash, name, role_id, is_active)
|
||||
VALUES (
|
||||
'admin@ejemplo.com',
|
||||
'$2b$10$xxxxx...', -- Hash bcrypt de la contraseña
|
||||
'Administrador',
|
||||
'uuid-del-rol-admin',
|
||||
true
|
||||
);
|
||||
```
|
||||
|
||||
### Via API (si ya tienes un admin):
|
||||
```bash
|
||||
curl -X POST http://localhost:3000/api/users \
|
||||
-H "Authorization: Bearer {token}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"email": "nuevo@ejemplo.com",
|
||||
"password": "password123",
|
||||
"name": "Nuevo Admin",
|
||||
"role_id": "uuid-rol-admin"
|
||||
}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuracion de The Things Stack (TTS)
|
||||
|
||||
### 1. Configurar Variables de Entorno
|
||||
|
||||
```env
|
||||
TTS_ENABLED=true
|
||||
TTS_BASE_URL=https://tu-servidor-tts.com
|
||||
TTS_APPLICATION_ID=tu-aplicacion
|
||||
TTS_API_KEY=tu-api-key
|
||||
TTS_WEBHOOK_SECRET=tu-webhook-secret
|
||||
```
|
||||
|
||||
### 2. Configurar Webhook en TTS
|
||||
|
||||
En la consola de TTS, configurar webhook apuntando a:
|
||||
- **URL Base**: `https://api.gestionrecursoshidricos.com/api/webhooks/tts`
|
||||
- **Eventos**:
|
||||
- Uplink: `/uplink`
|
||||
- Join: `/join`
|
||||
- Downlink ACK: `/downlink/ack`
|
||||
|
||||
### 3. Configurar Header de Autenticacion
|
||||
- **Header**: `X-Downlink-Apikey`
|
||||
- **Valor**: El mismo que `TTS_WEBHOOK_SECRET`
|
||||
|
||||
---
|
||||
|
||||
## Solucion de Problemas
|
||||
|
||||
### Error de conexion a la base de datos
|
||||
```
|
||||
Error: connect ECONNREFUSED 127.0.0.1:5432
|
||||
```
|
||||
- Verificar que PostgreSQL esta corriendo: `sudo systemctl status postgresql`
|
||||
- Verificar credenciales en `.env`
|
||||
|
||||
### Error CORS
|
||||
```
|
||||
Access-Control-Allow-Origin
|
||||
```
|
||||
- Verificar que la URL del frontend esta en `CORS_ORIGIN`
|
||||
|
||||
### Puerto en uso
|
||||
```
|
||||
Error: listen EADDRINUSE :::3000
|
||||
```
|
||||
- Verificar si hay otro proceso: `lsof -i :3000`
|
||||
- Terminar proceso: `kill -9 <PID>`
|
||||
|
||||
### Permisos de archivo
|
||||
```
|
||||
EACCES: permission denied
|
||||
```
|
||||
- Verificar permisos del directorio
|
||||
- Ejecutar: `chown -R $USER:$USER /path/to/water-project`
|
||||
358
docs/MANUAL_USUARIO.md
Normal file
358
docs/MANUAL_USUARIO.md
Normal file
@@ -0,0 +1,358 @@
|
||||
# Manual de Usuario - Sistema GRH
|
||||
|
||||
## Indice
|
||||
|
||||
1. [Acceso al Sistema](#acceso-al-sistema)
|
||||
2. [Dashboard Principal](#dashboard-principal)
|
||||
3. [Gestion de Proyectos](#gestion-de-proyectos)
|
||||
4. [Gestion de Concentradores](#gestion-de-concentradores)
|
||||
5. [Gestion de Medidores](#gestion-de-medidores)
|
||||
6. [Consumo y Lecturas](#consumo-y-lecturas)
|
||||
7. [Panel de Carga CSV](#panel-de-carga-csv)
|
||||
8. [Notificaciones](#notificaciones)
|
||||
9. [Administracion de Usuarios](#administracion-de-usuarios)
|
||||
10. [Auditoria](#auditoria)
|
||||
|
||||
---
|
||||
|
||||
## Acceso al Sistema
|
||||
|
||||
### URL de Acceso
|
||||
- **Sistema Principal**: https://sistema.gestionrecursoshidricos.com
|
||||
- **Panel de Carga CSV**: https://panel.gestionrecursoshidricos.com
|
||||
|
||||
### Inicio de Sesion
|
||||
|
||||
1. Ingrese a la URL del sistema
|
||||
2. Introduzca su correo electronico
|
||||
3. Introduzca su contraseña
|
||||
4. Haga clic en "Iniciar Sesion"
|
||||
|
||||
### Roles de Usuario
|
||||
|
||||
| Rol | Permisos |
|
||||
|-----|----------|
|
||||
| **ADMIN** | Acceso completo a todas las funciones |
|
||||
| **OPERATOR** | Gestion de medidores y lecturas de su proyecto asignado |
|
||||
| **VIEWER** | Solo visualizacion de datos |
|
||||
|
||||
---
|
||||
|
||||
## Dashboard Principal
|
||||
|
||||
El dashboard muestra un resumen general del sistema:
|
||||
|
||||
### Indicadores Clave (KPIs)
|
||||
- **Total de Medidores**: Cantidad total de medidores registrados
|
||||
- **Lecturas del Dia**: Numero de lecturas recibidas hoy
|
||||
- **Alertas Activas**: Notificaciones pendientes de atencion
|
||||
- **Proyectos Activos**: Proyectos en estado activo
|
||||
|
||||
### Alertas Recientes
|
||||
Lista de las ultimas alertas del sistema, incluyendo:
|
||||
- Flujo negativo detectado
|
||||
- Medidores sin comunicacion
|
||||
- Alertas de mantenimiento
|
||||
|
||||
### Actividad Reciente
|
||||
Historial de las ultimas acciones realizadas en el sistema.
|
||||
|
||||
---
|
||||
|
||||
## Gestion de Proyectos
|
||||
|
||||
### Ver Proyectos
|
||||
1. Navegue a **Proyectos** en el menu lateral
|
||||
2. Vera la lista de todos los proyectos
|
||||
3. Use los filtros para buscar proyectos especificos
|
||||
|
||||
### Crear Proyecto
|
||||
1. Haga clic en **"Nuevo Proyecto"**
|
||||
2. Complete los campos:
|
||||
- **Nombre**: Nombre del proyecto (requerido)
|
||||
- **Descripcion**: Descripcion detallada
|
||||
- **Area**: Nombre del area geografica
|
||||
- **Ubicacion**: Direccion o coordenadas
|
||||
- **Tipo de Medidor**: Tipo por defecto para el proyecto
|
||||
3. Haga clic en **"Guardar"**
|
||||
|
||||
### Editar Proyecto
|
||||
1. Haga clic en el icono de edicion del proyecto
|
||||
2. Modifique los campos necesarios
|
||||
3. Haga clic en **"Guardar"**
|
||||
|
||||
### Estadisticas del Proyecto
|
||||
- Haga clic en un proyecto para ver sus estadisticas
|
||||
- Incluye: total de medidores, lecturas, consumo promedio
|
||||
|
||||
---
|
||||
|
||||
## Gestion de Concentradores
|
||||
|
||||
Los concentradores son dispositivos que agrupan multiples medidores.
|
||||
|
||||
### Ver Concentradores
|
||||
1. Navegue a **Concentradores** en el menu lateral
|
||||
2. Filtre por proyecto si es necesario
|
||||
|
||||
### Crear Concentrador
|
||||
1. Haga clic en **"Nuevo Concentrador"**
|
||||
2. Complete los campos:
|
||||
- **Serial**: Numero de serie unico (requerido)
|
||||
- **Nombre**: Nombre descriptivo (requerido)
|
||||
- **Proyecto**: Proyecto al que pertenece (requerido)
|
||||
- **Ubicacion**: Ubicacion fisica
|
||||
- **IP**: Direccion IP (opcional)
|
||||
3. Haga clic en **"Guardar"**
|
||||
|
||||
### Estados del Concentrador
|
||||
| Estado | Descripcion |
|
||||
|--------|-------------|
|
||||
| ACTIVE | Funcionando correctamente |
|
||||
| INACTIVE | Desactivado manualmente |
|
||||
| OFFLINE | Sin comunicacion |
|
||||
| MAINTENANCE | En mantenimiento |
|
||||
| ERROR | Con fallas |
|
||||
|
||||
---
|
||||
|
||||
## Gestion de Medidores
|
||||
|
||||
### Ver Medidores
|
||||
1. Navegue a **Medidores** en el menu lateral
|
||||
2. Use los filtros disponibles:
|
||||
- Por proyecto
|
||||
- Por concentrador
|
||||
- Por estado
|
||||
- Por tipo
|
||||
- Busqueda por serial/nombre
|
||||
|
||||
### Crear Medidor Individual
|
||||
1. Haga clic en **"Nuevo Medidor"**
|
||||
2. Complete los campos:
|
||||
- **Serial**: Numero de serie unico (requerido)
|
||||
- **Nombre**: Nombre descriptivo (requerido)
|
||||
- **Concentrador**: Concentrador asociado (requerido)
|
||||
- **Area**: Nombre del area
|
||||
- **Ubicacion**: Ubicacion especifica (ej: "Depto 101")
|
||||
- **Tipo**: LORA, LORAWAN, o GRANDES CONSUMIDORES
|
||||
- **Estado**: Estado inicial
|
||||
- **Fecha Instalacion**: Fecha de instalacion
|
||||
3. Haga clic en **"Guardar"**
|
||||
|
||||
### Carga Masiva de Medidores (Excel)
|
||||
1. Haga clic en **"Carga Masiva"**
|
||||
2. Descargue la plantilla Excel
|
||||
3. Complete la plantilla con los datos
|
||||
4. Suba el archivo Excel
|
||||
5. Revise los resultados
|
||||
|
||||
### Tipos de Medidor
|
||||
| Tipo | Descripcion |
|
||||
|------|-------------|
|
||||
| LORA | Medidores con comunicacion LoRa |
|
||||
| LORAWAN | Medidores LoRaWAN |
|
||||
| GRANDES CONSUMIDORES | Medidores de alto consumo |
|
||||
|
||||
### Ver Detalle del Medidor
|
||||
- Haga clic en un medidor para ver:
|
||||
- Informacion general
|
||||
- Ultima lectura
|
||||
- Historial de lecturas
|
||||
- Graficos de consumo
|
||||
|
||||
---
|
||||
|
||||
## Consumo y Lecturas
|
||||
|
||||
### Ver Lecturas
|
||||
1. Navegue a **Consumo** en el menu lateral
|
||||
2. Filtre por:
|
||||
- Proyecto
|
||||
- Concentrador
|
||||
- Medidor especifico
|
||||
- Rango de fechas
|
||||
- Tipo de lectura
|
||||
|
||||
### Tipos de Lectura
|
||||
| Tipo | Descripcion |
|
||||
|------|-------------|
|
||||
| AUTOMATIC | Lectura automatica del dispositivo |
|
||||
| MANUAL | Lectura ingresada manualmente |
|
||||
| SCHEDULED | Lectura programada |
|
||||
|
||||
### Crear Lectura Manual
|
||||
1. Haga clic en **"Nueva Lectura"**
|
||||
2. Seleccione el medidor
|
||||
3. Ingrese el valor de lectura
|
||||
4. Opcionalmente ingrese:
|
||||
- Nivel de bateria (0-100)
|
||||
- Intensidad de senal (dBm)
|
||||
5. Haga clic en **"Guardar"**
|
||||
|
||||
### Carga Masiva de Lecturas (Excel)
|
||||
1. Haga clic en **"Carga Masiva"**
|
||||
2. Descargue la plantilla
|
||||
3. Complete con los datos
|
||||
4. Suba el archivo
|
||||
5. Revise los resultados
|
||||
|
||||
### Graficos de Consumo
|
||||
- Visualice el consumo historico en graficos
|
||||
- Filtre por periodo de tiempo
|
||||
- Exporte datos si es necesario
|
||||
|
||||
---
|
||||
|
||||
## Panel de Carga CSV
|
||||
|
||||
El panel de carga CSV permite subir datos de medidores y lecturas sin necesidad de autenticacion.
|
||||
|
||||
### Acceso
|
||||
- URL: https://panel.gestionrecursoshidricos.com
|
||||
|
||||
### Cargar Medidores
|
||||
|
||||
1. En la seccion **"Tomas de Agua"**
|
||||
2. Descargue la plantilla CSV haciendo clic en "Descargar plantilla CSV"
|
||||
3. Complete el archivo con los datos:
|
||||
|
||||
```csv
|
||||
serial_number,name,concentrator_serial,area_name,location,meter_type,status,installation_date
|
||||
MED001,Medidor 1,Mexico-GRH,ZONA A,Depto 101,LORA,ACTIVE,2024-01-15
|
||||
MED002,Medidor 2,Mexico-GRH,ZONA A,Depto 102,LORA,ACTIVE,2024-01-15
|
||||
```
|
||||
|
||||
**Campos:**
|
||||
| Campo | Requerido | Descripcion |
|
||||
|-------|-----------|-------------|
|
||||
| serial_number | Si | Numero de serie unico del medidor |
|
||||
| name | Si | Nombre descriptivo |
|
||||
| concentrator_serial | Si | Serial del concentrador existente |
|
||||
| area_name | No | Nombre del area |
|
||||
| location | No | Ubicacion especifica |
|
||||
| meter_type | No | LORA (default), LORAWAN, GRANDES CONSUMIDORES |
|
||||
| status | No | ACTIVE (default), INACTIVE, MAINTENANCE |
|
||||
| installation_date | No | Fecha YYYY-MM-DD |
|
||||
|
||||
4. Arrastre el archivo o haga clic para seleccionarlo
|
||||
5. Haga clic en **"Subir Archivo"**
|
||||
6. Revise los resultados:
|
||||
- Registros insertados (nuevos)
|
||||
- Registros actualizados (existentes)
|
||||
- Errores encontrados
|
||||
|
||||
**Logica de Upsert:**
|
||||
- Si el `serial_number` ya existe: se **actualiza** el medidor
|
||||
- Si el `serial_number` no existe: se **crea** un nuevo medidor
|
||||
|
||||
### Cargar Lecturas
|
||||
|
||||
1. En la seccion **"Lecturas"**
|
||||
2. Descargue la plantilla CSV
|
||||
3. Complete el archivo:
|
||||
|
||||
```csv
|
||||
meter_serial,reading_value,received_at,reading_type,battery_level,signal_strength
|
||||
MED001,1234.56,2024-01-20 10:30:00,MANUAL,85,-45
|
||||
MED002,567.89,2024-01-20 10:35:00,MANUAL,90,-42
|
||||
```
|
||||
|
||||
**Campos:**
|
||||
| Campo | Requerido | Descripcion |
|
||||
|-------|-----------|-------------|
|
||||
| meter_serial | Si | Serial del medidor existente |
|
||||
| reading_value | Si | Valor numerico de la lectura |
|
||||
| received_at | No | Fecha/hora (default: ahora) |
|
||||
| reading_type | No | AUTOMATIC, MANUAL, SCHEDULED |
|
||||
| battery_level | No | Nivel de bateria 0-100 |
|
||||
| signal_strength | No | Intensidad de senal en dBm |
|
||||
|
||||
4. Suba el archivo
|
||||
5. Revise los resultados
|
||||
|
||||
**Nota:** El medidor debe existir previamente para poder cargar lecturas.
|
||||
|
||||
---
|
||||
|
||||
## Notificaciones
|
||||
|
||||
### Ver Notificaciones
|
||||
1. Haga clic en el icono de campana en la barra superior
|
||||
2. Vera las notificaciones recientes
|
||||
|
||||
### Tipos de Notificacion
|
||||
| Tipo | Descripcion |
|
||||
|------|-------------|
|
||||
| NEGATIVE_FLOW | Flujo negativo detectado en un medidor |
|
||||
| SYSTEM_ALERT | Alerta general del sistema |
|
||||
| MAINTENANCE | Recordatorio de mantenimiento |
|
||||
|
||||
### Gestionar Notificaciones
|
||||
- **Marcar como leida**: Haga clic en la notificacion
|
||||
- **Marcar todas como leidas**: Boton en la parte superior
|
||||
- **Eliminar**: Icono de eliminar en cada notificacion
|
||||
|
||||
---
|
||||
|
||||
## Administracion de Usuarios
|
||||
|
||||
*Solo disponible para usuarios con rol ADMIN*
|
||||
|
||||
### Ver Usuarios
|
||||
1. Navegue a **Usuarios** en el menu lateral
|
||||
2. Vera la lista de todos los usuarios
|
||||
|
||||
### Crear Usuario
|
||||
1. Haga clic en **"Nuevo Usuario"**
|
||||
2. Complete los campos:
|
||||
- **Email**: Correo electronico (sera el usuario de login)
|
||||
- **Nombre**: Nombre completo
|
||||
- **Contraseña**: Contraseña inicial
|
||||
- **Rol**: ADMIN, OPERATOR, o VIEWER
|
||||
- **Proyecto**: Solo para OPERATOR - proyecto asignado
|
||||
3. Haga clic en **"Guardar"**
|
||||
|
||||
### Editar Usuario
|
||||
1. Haga clic en el icono de edicion
|
||||
2. Modifique los campos necesarios
|
||||
3. Haga clic en **"Guardar"**
|
||||
|
||||
### Desactivar Usuario
|
||||
1. Haga clic en el icono de desactivar
|
||||
2. Confirme la accion
|
||||
- El usuario no podra iniciar sesion pero sus datos se conservan
|
||||
|
||||
---
|
||||
|
||||
## Auditoria
|
||||
|
||||
*Solo disponible para usuarios con rol ADMIN*
|
||||
|
||||
### Ver Logs de Auditoria
|
||||
1. Navegue a **Auditoria** en el menu lateral
|
||||
2. Vera el historial de acciones del sistema
|
||||
|
||||
### Filtros Disponibles
|
||||
- Por usuario
|
||||
- Por accion (CREATE, UPDATE, DELETE, LOGIN, etc.)
|
||||
- Por tabla/entidad
|
||||
- Por rango de fechas
|
||||
|
||||
### Informacion del Log
|
||||
Cada registro muestra:
|
||||
- Fecha y hora
|
||||
- Usuario que realizo la accion
|
||||
- Tipo de accion
|
||||
- Entidad afectada
|
||||
- Valores anteriores y nuevos (para updates)
|
||||
- Direccion IP
|
||||
- Resultado (exito/error)
|
||||
|
||||
---
|
||||
|
||||
## Soporte
|
||||
|
||||
Para soporte tecnico o reportar problemas:
|
||||
- Contacte al administrador del sistema
|
||||
- Revise la documentacion tecnica en `/docs`
|
||||
126
docs/README.md
Normal file
126
docs/README.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# Sistema de Gestion de Recursos Hidricos (GRH)
|
||||
|
||||
## Descripcion General
|
||||
|
||||
Sistema web para la gestion y monitoreo de medidores de agua, integrando dispositivos LoRaWAN a traves de The Things Stack (TTS). Permite el seguimiento de consumo, deteccion de anomalias y gestion de proyectos de medicion.
|
||||
|
||||
## Arquitectura del Sistema
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ FRONTEND │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ App Principal (React) │ Panel de Carga (React) │
|
||||
│ Puerto: 5173 │ Puerto: 5174 │
|
||||
│ - Dashboard │ - Carga CSV Medidores │
|
||||
│ - Gestion Medidores │ - Carga CSV Lecturas │
|
||||
│ - Gestion Proyectos │ │
|
||||
│ - Consumo/Lecturas │ │
|
||||
│ - Usuarios/Roles │ │
|
||||
│ - Auditoria │ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ BACKEND API │
|
||||
│ Puerto: 3000 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Express.js + TypeScript │
|
||||
│ - Autenticacion JWT │
|
||||
│ - Control de Acceso por Roles │
|
||||
│ - Webhooks TTS (LoRaWAN) │
|
||||
│ - Jobs Programados (Cron) │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ BASE DE DATOS │
|
||||
│ PostgreSQL │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ - users, roles │
|
||||
│ - projects, concentrators, gateways │
|
||||
│ - meters, meter_readings, devices │
|
||||
│ - notifications, audit_logs │
|
||||
│ - tts_uplink_logs │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ SERVICIOS EXTERNOS │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ The Things Stack (TTS) │
|
||||
│ - Recepcion de datos LoRaWAN │
|
||||
│ - Gestion de dispositivos IoT │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Estructura del Proyecto
|
||||
|
||||
```
|
||||
water-project/
|
||||
├── src/ # Frontend principal (React + Vite)
|
||||
│ ├── api/ # Clientes API
|
||||
│ ├── components/ # Componentes React
|
||||
│ ├── hooks/ # Custom hooks
|
||||
│ ├── pages/ # Paginas de la aplicacion
|
||||
│ └── types/ # Tipos TypeScript
|
||||
│
|
||||
├── water-api/ # Backend API (Express + TypeScript)
|
||||
│ ├── src/
|
||||
│ │ ├── config/ # Configuracion (DB, etc.)
|
||||
│ │ ├── controllers/ # Controladores HTTP
|
||||
│ │ ├── middleware/ # Middlewares (auth, audit)
|
||||
│ │ ├── routes/ # Definicion de rutas
|
||||
│ │ ├── services/ # Logica de negocio
|
||||
│ │ ├── validators/ # Validacion de datos
|
||||
│ │ ├── jobs/ # Tareas programadas
|
||||
│ │ └── utils/ # Utilidades
|
||||
│ └── sql/ # Scripts SQL
|
||||
│
|
||||
├── upload-panel/ # Panel de carga CSV (React + Vite)
|
||||
│ └── src/
|
||||
│ ├── api/ # Cliente API
|
||||
│ └── components/ # Componentes de carga
|
||||
│
|
||||
└── docs/ # Documentacion
|
||||
```
|
||||
|
||||
## Tecnologias Utilizadas
|
||||
|
||||
### Frontend
|
||||
- **React 18** - Framework UI
|
||||
- **Vite 5** - Build tool
|
||||
- **TypeScript** - Tipado estatico
|
||||
- **Tailwind CSS 4** - Estilos
|
||||
- **Material UI** - Componentes UI
|
||||
- **Recharts** - Graficos
|
||||
- **Lucide React** - Iconos
|
||||
|
||||
### Backend
|
||||
- **Node.js** - Runtime
|
||||
- **Express 4** - Framework web
|
||||
- **TypeScript** - Tipado estatico
|
||||
- **PostgreSQL** - Base de datos
|
||||
- **JWT** - Autenticacion
|
||||
- **Multer** - Upload de archivos
|
||||
- **node-cron** - Tareas programadas
|
||||
- **Zod** - Validacion de schemas
|
||||
|
||||
### Servicios Externos
|
||||
- **The Things Stack (TTS)** - Integracion LoRaWAN
|
||||
|
||||
## URLs de Produccion
|
||||
|
||||
| Servicio | URL |
|
||||
|----------|-----|
|
||||
| App Principal | https://sistema.gestionrecursoshidricos.com |
|
||||
| Panel de Carga | https://panel.gestionrecursoshidricos.com |
|
||||
| API | https://api.gestionrecursoshidricos.com |
|
||||
|
||||
## Documentacion Detallada
|
||||
|
||||
- [Manual de Usuario](./MANUAL_USUARIO.md)
|
||||
- [Documentacion API](./API.md)
|
||||
- [Guia de Instalacion](./INSTALACION.md)
|
||||
- [Panel de Carga CSV](./UPLOAD_PANEL.md)
|
||||
- [Arquitectura y Base de Datos](./ARQUITECTURA.md)
|
||||
297
docs/UPLOAD_PANEL.md
Normal file
297
docs/UPLOAD_PANEL.md
Normal file
@@ -0,0 +1,297 @@
|
||||
# Panel de Carga CSV
|
||||
|
||||
## Descripcion
|
||||
|
||||
El Panel de Carga CSV es una aplicacion web independiente que permite subir datos de medidores y lecturas mediante archivos CSV, sin necesidad de autenticacion. Esta diseñado para uso interno y facilita la carga masiva de datos.
|
||||
|
||||
## Acceso
|
||||
|
||||
- **URL**: https://panel.gestionrecursoshidricos.com
|
||||
- **Puerto local**: 5174
|
||||
- **Autenticacion**: No requerida
|
||||
|
||||
---
|
||||
|
||||
## Interfaz de Usuario
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ Panel de Carga de Datos - GRH │
|
||||
│ Sistema de Gestion de Recursos Hidricos │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────────────────┐ ┌─────────────────────────┐ │
|
||||
│ │ TOMAS DE AGUA │ │ LECTURAS │ │
|
||||
│ │ (Medidores) │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ Campos requeridos: │ │ Campos requeridos: │ │
|
||||
│ │ - serial_number │ │ - meter_serial │ │
|
||||
│ │ - name │ │ - reading_value │ │
|
||||
│ │ - concentrator_serial │ │ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ [Descargar plantilla] │ │ [Descargar plantilla] │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ ┌───────────────────┐ │ │ ┌───────────────────┐ │ │
|
||||
│ │ │ Arrastra CSV aqui │ │ │ │ Arrastra CSV aqui │ │ │
|
||||
│ │ │ o haz clic │ │ │ │ o haz clic │ │ │
|
||||
│ │ └───────────────────┘ │ │ └───────────────────┘ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ [ SUBIR ARCHIVO ] │ │ [ SUBIR ARCHIVO ] │ │
|
||||
│ └─────────────────────────┘ └─────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌───────────────────────────────────────────────────────────┐ │
|
||||
│ │ Resultado de la carga: │ │
|
||||
│ │ Total: 100 | Insertados: 95 | Actualizados: 3 | Errores: 2│ │
|
||||
│ │ │ │
|
||||
│ │ Errores: │ │
|
||||
│ │ - Fila 15: Concentrador "XXX" no encontrado │ │
|
||||
│ │ - Fila 42: Valor de lectura invalido │ │
|
||||
│ └───────────────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Carga de Medidores (Tomas de Agua)
|
||||
|
||||
### Formato del CSV
|
||||
|
||||
```csv
|
||||
serial_number,name,concentrator_serial,area_name,location,meter_type,status,installation_date
|
||||
MED001,Medidor 001,Mexico-GRH,ZONA A,Depto 101,LORA,ACTIVE,2024-01-15
|
||||
MED002,Medidor 002,Mexico-GRH,ZONA A,Depto 102,LORA,ACTIVE,2024-01-15
|
||||
MED003,Medidor 003,Mexico-GRH,ZONA B,Depto 201,LORAWAN,ACTIVE,2024-01-16
|
||||
```
|
||||
|
||||
### Campos
|
||||
|
||||
| Campo | Requerido | Tipo | Descripcion | Ejemplo |
|
||||
|-------|-----------|------|-------------|---------|
|
||||
| serial_number | **Si** | Texto | Numero de serie unico del medidor | MED001 |
|
||||
| name | **Si** | Texto | Nombre descriptivo | Medidor Depto 101 |
|
||||
| concentrator_serial | **Si** | Texto | Serial del concentrador existente | Mexico-GRH |
|
||||
| area_name | No | Texto | Nombre del area o zona | ZONA A |
|
||||
| location | No | Texto | Ubicacion especifica | Depto 101, Piso 1 |
|
||||
| meter_type | No | Texto | Tipo de medidor (default: LORA) | LORA |
|
||||
| status | No | Texto | Estado inicial (default: ACTIVE) | ACTIVE |
|
||||
| installation_date | No | Fecha | Fecha de instalacion (YYYY-MM-DD) | 2024-01-15 |
|
||||
|
||||
### Tipos de Medidor Validos
|
||||
- `LORA` (default)
|
||||
- `LORAWAN`
|
||||
- `GRANDES CONSUMIDORES`
|
||||
- `WATER`
|
||||
- `GAS`
|
||||
- `ELECTRIC`
|
||||
|
||||
### Estados Validos
|
||||
- `ACTIVE` (default)
|
||||
- `INACTIVE`
|
||||
- `OFFLINE`
|
||||
- `MAINTENANCE`
|
||||
- `ERROR`
|
||||
|
||||
### Logica de Upsert
|
||||
|
||||
El sistema utiliza el campo `serial_number` como identificador unico:
|
||||
|
||||
- **Si el serial_number YA EXISTE**: Se **actualiza** el medidor con los nuevos valores
|
||||
- **Si el serial_number NO EXISTE**: Se **crea** un nuevo medidor
|
||||
|
||||
Esto permite:
|
||||
1. Agregar nuevos medidores
|
||||
2. Actualizar medidores existentes
|
||||
3. Hacer ambas operaciones en un solo archivo
|
||||
|
||||
### Ejemplo de CSV para Actualizacion
|
||||
|
||||
```csv
|
||||
serial_number,name,concentrator_serial,area_name,location,meter_type,status,installation_date
|
||||
MED001,Medidor Actualizado,Mexico-GRH,ZONA B,Nueva Ubicacion,LORA,MAINTENANCE,2024-01-15
|
||||
```
|
||||
|
||||
Solo se actualizaran los campos proporcionados (no vacios).
|
||||
|
||||
---
|
||||
|
||||
## Carga de Lecturas
|
||||
|
||||
### Formato del CSV
|
||||
|
||||
```csv
|
||||
meter_serial,reading_value,received_at,reading_type,battery_level,signal_strength
|
||||
MED001,1234.56,2024-01-20 10:30:00,MANUAL,85,-45
|
||||
MED002,567.89,2024-01-20 10:35:00,MANUAL,90,-42
|
||||
MED003,890.12,2024-01-20 10:40:00,AUTOMATIC,78,-50
|
||||
```
|
||||
|
||||
### Campos
|
||||
|
||||
| Campo | Requerido | Tipo | Descripcion | Ejemplo |
|
||||
|-------|-----------|------|-------------|---------|
|
||||
| meter_serial | **Si** | Texto | Serial del medidor existente | MED001 |
|
||||
| reading_value | **Si** | Numero | Valor de la lectura | 1234.56 |
|
||||
| received_at | No | Fecha/Hora | Fecha y hora de la lectura | 2024-01-20 10:30:00 |
|
||||
| reading_type | No | Texto | Tipo de lectura (default: MANUAL) | MANUAL |
|
||||
| battery_level | No | Entero | Nivel de bateria (0-100) | 85 |
|
||||
| signal_strength | No | Entero | Intensidad de senal (dBm) | -45 |
|
||||
|
||||
### Tipos de Lectura Validos
|
||||
- `AUTOMATIC` - Lectura automatica del dispositivo
|
||||
- `MANUAL` - Lectura ingresada manualmente (default)
|
||||
- `SCHEDULED` - Lectura programada
|
||||
|
||||
### Notas Importantes
|
||||
|
||||
1. **El medidor debe existir**: El `meter_serial` debe corresponder a un medidor ya registrado en el sistema
|
||||
2. **Fecha por defecto**: Si no se especifica `received_at`, se usa la fecha/hora actual
|
||||
3. **Actualizacion automatica**: Al insertar una lectura, se actualiza automaticamente el `last_reading_value` del medidor
|
||||
|
||||
---
|
||||
|
||||
## Proceso de Carga
|
||||
|
||||
### Paso 1: Descargar Plantilla
|
||||
1. Haga clic en "Descargar plantilla CSV"
|
||||
2. Se descargara un archivo con los encabezados correctos y una fila de ejemplo
|
||||
|
||||
### Paso 2: Preparar el Archivo
|
||||
1. Abra la plantilla en Excel, Google Sheets o cualquier editor de texto
|
||||
2. Complete los datos manteniendo el formato CSV
|
||||
3. Guarde el archivo como `.csv` (valores separados por comas)
|
||||
|
||||
### Paso 3: Subir el Archivo
|
||||
1. Arrastre el archivo al area de carga, o haga clic para seleccionarlo
|
||||
2. Verifique que aparezca el nombre del archivo
|
||||
3. Haga clic en "Subir Archivo"
|
||||
|
||||
### Paso 4: Revisar Resultados
|
||||
El sistema mostrara:
|
||||
- **Total**: Numero total de filas procesadas
|
||||
- **Insertados**: Registros nuevos creados
|
||||
- **Actualizados**: Registros existentes modificados
|
||||
- **Errores**: Filas que no pudieron procesarse
|
||||
|
||||
### Errores Comunes
|
||||
|
||||
| Error | Causa | Solucion |
|
||||
|-------|-------|----------|
|
||||
| "serial_number es requerido" | Celda vacia | Agregar serial |
|
||||
| "concentrator_serial es requerido" | Celda vacia | Agregar serial del concentrador |
|
||||
| "Concentrador no encontrado" | Serial incorrecto | Verificar serial del concentrador |
|
||||
| "Medidor no encontrado" | Serial de medidor incorrecto (lecturas) | Verificar que el medidor exista |
|
||||
| "Valor de lectura invalido" | No es numero | Usar formato numerico (ej: 1234.56) |
|
||||
| "Nivel de bateria invalido" | Fuera de rango | Usar valor entre 0 y 100 |
|
||||
|
||||
---
|
||||
|
||||
## Ejemplos Practicos
|
||||
|
||||
### Ejemplo 1: Cargar 3 Medidores Nuevos
|
||||
|
||||
```csv
|
||||
serial_number,name,concentrator_serial,area_name,location,meter_type,status,installation_date
|
||||
RES-001,Departamento 101,Mexico-GRH,TORRE A,Piso 1,LORA,ACTIVE,2024-01-15
|
||||
RES-002,Departamento 102,Mexico-GRH,TORRE A,Piso 1,LORA,ACTIVE,2024-01-15
|
||||
RES-003,Departamento 201,Mexico-GRH,TORRE A,Piso 2,LORA,ACTIVE,2024-01-15
|
||||
```
|
||||
|
||||
### Ejemplo 2: Actualizar Ubicacion de Medidores
|
||||
|
||||
```csv
|
||||
serial_number,name,concentrator_serial,area_name,location,meter_type,status,installation_date
|
||||
RES-001,,Mexico-GRH,,Nueva Ubicacion,,,
|
||||
RES-002,,Mexico-GRH,,Bodega 1,,,
|
||||
```
|
||||
|
||||
Solo los campos con valor seran actualizados.
|
||||
|
||||
### Ejemplo 3: Cargar Lecturas Diarias
|
||||
|
||||
```csv
|
||||
meter_serial,reading_value,received_at,reading_type,battery_level,signal_strength
|
||||
RES-001,125.50,2024-01-20 08:00:00,MANUAL,95,-40
|
||||
RES-002,89.75,2024-01-20 08:05:00,MANUAL,92,-38
|
||||
RES-003,156.25,2024-01-20 08:10:00,MANUAL,88,-45
|
||||
```
|
||||
|
||||
### Ejemplo 4: Cargar Lecturas sin Fecha (Usa Fecha Actual)
|
||||
|
||||
```csv
|
||||
meter_serial,reading_value,received_at,reading_type,battery_level,signal_strength
|
||||
RES-001,130.25,,,85,
|
||||
RES-002,92.50,,,90,
|
||||
RES-003,161.00,,,85,
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Endpoints
|
||||
|
||||
El panel utiliza los siguientes endpoints de la API:
|
||||
|
||||
### Medidores
|
||||
|
||||
**Subir CSV:**
|
||||
```http
|
||||
POST /api/csv-upload/meters
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: archivo.csv
|
||||
```
|
||||
|
||||
**Descargar plantilla:**
|
||||
```http
|
||||
GET /api/csv-upload/meters/template
|
||||
```
|
||||
|
||||
### Lecturas
|
||||
|
||||
**Subir CSV:**
|
||||
```http
|
||||
POST /api/csv-upload/readings
|
||||
Content-Type: multipart/form-data
|
||||
|
||||
file: archivo.csv
|
||||
```
|
||||
|
||||
**Descargar plantilla:**
|
||||
```http
|
||||
GET /api/csv-upload/readings/template
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuracion Tecnica
|
||||
|
||||
### Variables de Entorno
|
||||
|
||||
Crear archivo `.env` en `upload-panel/`:
|
||||
|
||||
```env
|
||||
VITE_API_URL=https://api.gestionrecursoshidricos.com/api
|
||||
```
|
||||
|
||||
Para desarrollo local:
|
||||
```env
|
||||
VITE_API_URL=http://localhost:3000/api
|
||||
```
|
||||
|
||||
### Ejecutar Localmente
|
||||
|
||||
```bash
|
||||
cd upload-panel
|
||||
npm install
|
||||
npm run dev
|
||||
```
|
||||
|
||||
El panel estara disponible en: http://localhost:5174
|
||||
|
||||
### Compilar para Produccion
|
||||
|
||||
```bash
|
||||
npm run build
|
||||
```
|
||||
|
||||
Los archivos compilados estaran en `upload-panel/dist/`
|
||||
Reference in New Issue
Block a user