CreaRack-SL

Rack Editor - Documentación Técnica

Versión: 1.0 Fecha: 03-04-2026 Estado: Produccion - Konva.js Canvas + Auto-Save + Half-Width Devices


Resumen

Editor visual de racks de datacenter integrado en CreaRack Pro. Permite diseñar la disposición física de equipos dentro de armarios rack estándar de 19” mediante un canvas interactivo basado en Konva.js.

Funcionalidades principales:

  • Canvas 2D interactivo con zoom, pan y drag-drop de dispositivos
  • Librería de stencils con importación Visio (.vssx/.vdx/.vss)
  • Dispositivos full-width y half-width (medio rack) con detección de colisiones 2D
  • Panel de propiedades: nombre, puertos (Ethernet + SFP), management config, notas
  • Auto-save con debounce 500ms — los cambios se persisten automáticamente
  • Undo/Redo via StateManager (Ctrl+Z / Ctrl+Y)
  • Templates: guardar configuraciones de rack reutilizables (Masters)
  • Copy from…: copiar el montaje de cualquier rack o Master en otro, modo Replace (borra destino) o Add on top (solo donde no colisiona) — v1.79.0
  • SSH integrado: terminal xterm.js + navegador SFTP desde el editor
  • Config Backups: snapshots de running-config con diff viewer
  • Auto-Provision: panel flotante de dispositivos descubiertos pendientes de colocación
  • QR Labels: generación e impresión de etiquetas QR por dispositivo o rack completo
  • Export: CSV, JSON, QR labels
  • Papelera con soft-delete y restauración
  • Grupos de racks con asignación y colores

Arquitectura

Estructura de Archivos

Frontend (static/js/editor/ — 20 archivos, ~7.500 LOC):

ArchivoLOCResponsabilidad
main.js~363Bootstrap: carga datos del DOM, inicializa Konva, orquesta módulos
state.js~55Estado global compartido (STATE), constantes de grid, helpers de posición
konva_setup.js~189Inicialización Stage/Layer, zoom, pan, renderizado del frame del rack
devices.js~432Creación, posicionamiento, drag-drop, snap y colisión de dispositivos
history_adapter.js~172Adaptador Undo/Redo via StateManager con snapshots inmutables
editor_api.js~86Auto-save con debounce 500ms, serialización hacia backend
ui_library.js~272Panel de librería de stencils: búsqueda, carpetas, drag hacia canvas
PropertiesPanelRenderer.js~563Renderizado HTML del panel de propiedades del dispositivo seleccionado
PropertiesPanelEvents.js~925Eventos del panel: cambios de nombre, puertos, acciones (SSH, QR, etc.)
auto_provision_panel.js~239Panel flotante de dispositivos pendientes de Auto-Provision
ssh_integration.js~411Terminal SSH embebido, gestión de sesiones, integración SFTP
SshConfigModal.js~254Modal de configuración SSH: IP, puerto, credenciales, vendor autodetect
import_export.js~506Importación Visio (.vssx/.vdx/.vss), gestión de categorías
backups.js~623Snapshots de configuración de red, diff viewer, restore
sftp_ui.js~340Navegador SFTP (árbol, upload, download) via Local Agent
ConnectionModal.js—Modal de conexiones inter-dispositivo (cables, puertos)
DeviceInfoModal.js—Modal de información detallada del dispositivo
PortConfigDiff.js—Visor de diferencias de configuración de puertos
modal_helper.js—Utilidades compartidas para modales

Backend (racks/ — Django app, ~3.300 LOC):

racks/
├── models.py              # 6 modelos: BoxCategory, RackGroup, Stencil, Rack, Device, ConfigBackup
├── views.py               # Vistas HTMX: index (lista racks), rack_editor (canvas)
├── urls.py                # 3 rutas: /, /editor/{id}/, /rack/{id} (legacy)
├── schemas.py             # Pydantic schemas para API (Django Ninja)
├── api/
│   ├── __init__.py        # Router principal /api/racks/
│   ├── racks.py           # CRUD racks + dispositivos + apply_template + copy_from (~410 LOC)
│   ├── library.py         # Stencils + Visio import (~745 LOC)
│   ├── export.py          # CSV/JSON/PDF export (~1.023 LOC)
│   ├── labels.py          # QR labels API (~111 LOC)
│   └── trash.py           # Papelera racks (~96 LOC)
├── services/
│   ├── racks.py           # RackService: list, create, update, bulk_update (~398 LOC)
│   ├── copy_from.py       # copy_devices_from(): copia el montaje de otro rack/Master, modos replace/add (~94 LOC, v1.79.0)
│   └── library.py         # LibraryService: stencils, Visio import (~133 LOC)
└── migrations/            # 12+ migraciones

Templates:

TemplateRutaPropósito
editor.htmltemplates/racks/editor.htmlPágina principal con canvas, paneles laterales, toolbars
index.htmltemplates/racks/index.htmlLista de racks del dashboard

Estado Global (state.js)

Objeto STATE

El módulo state.js exporta un singleton mutable que actúa como fuente de verdad para todo el editor:

export const STATE = {
    rackData: null,        // Datos del rack cargados del DOM (#rack-data)
    devices: [],           // Array de objetos dispositivo con nodos Konva
    selectedNode: null,    // Nodo Konva actualmente seleccionado
    isLocked: false,       // Bloqueo de interacción del canvas
    currentDragItem: null, // Item arrastrándose desde la librería
    stage: null,           // Instancia Konva.Stage
    layer: null,           // Instancia Konva.Layer principal
    rackGroup: null,       // Konva.Group del frame del rack
    pos: { x: 50, y: 50 },// Posición dinámica del rack en el canvas
    CONFIG: {
        U_HEIGHT: 35,      // Pixeles por unidad de rack (U)
        RACK_WIDTH: 400,   // Ancho fijo del rack en pixeles
        RACK_HEIGHT: 0     // Calculado en init: height_u * U_HEIGHT
    }
};

Sistema de Slots (Half-Width)

Soporta dispositivos de medio ancho (195px) que comparten una misma U:

export const SLOT = { FULL: 'full', LEFT: 'left', RIGHT: 'right' };
export const HALF_WIDTH = 195;  // (RACK_WIDTH - 10) / 2
FunciónDescripción
getSlotX(slot)Retorna 0 para LEFT/FULL, HALF_WIDTH para RIGHT
getRackHeight()Retorna CONFIG.RACK_HEIGHT
calculateDeviceY(u_position, u_height)Convierte posición U a coordenada Y: RACK_HEIGHT - (u_position - 1) * U_HEIGHT

Estructura de un Dispositivo

Cada entrada en STATE.devices[]:

{
    type: "switch",              // Tipo de dispositivo
    originalName: "Cisco 2960",  // Nombre del stencil original
    u_height: 1,                 // Altura en unidades U
    u_position: 15,              // Posición U (desde abajo, base 1)
    notes: "",                   // Notas del usuario
    image: "/media/uploads/...", // URL de la imagen del stencil
    db_id: 42,                   // ID en BD (null si nuevo, no guardado aún)
    half_width: false,           // true = dispositivo de medio ancho
    slot: "full",                // "full" | "left" | "right"
    ports: {                     // Mapa de puertos (dos formatos coexisten)
        "eth0": { live: "up", label: "WAN", desired: "trunk" },  // Enriquecido
        "sfp1": "10G"                                             // Legacy
    },
    meta: {                      // Metadatos heredados del stencil
        ports: {...}, notes: "...", half_width: false
    },
    konvaNode: Konva.Group       // Referencia al nodo visual en canvas
}

Canvas Konva.js (konva_setup.js)

Inicialización

STATE.stage = new Konva.Stage({
    container: containerId,
    width: containerWidth,
    height: containerHeight,
    draggable: true            // Pan habilitado
});
STATE.layer = new Konva.Layer();
STATE.stage.add(STATE.layer);

Jerarquía Visual

Konva.Stage (draggable, zoom con wheel)
└── Konva.Layer
    ├── STATE.rackGroup (Konva.Group)
    │   ├── Rect        — fondo del rack/cabinet
    │   ├── Lines       — separadores horizontales por U
    │   └── Text        — números de U: 1, 2, 3...
    ├── DeviceGroup_1 (Konva.Group, draggable)
    │   ├── Rect        — fondo del dispositivo
    │   ├── Image       — stencil
    │   └── Text        — nombre
    └── DeviceGroup_N ...

Interacción

EventoComportamiento
wheelZoom con factor 1.1x, recalcula posición de cámara
mousedownCursor grabbing (pan del canvas)
mouseupCursor default
Stage dragPan libre del canvas completo

fitRackToView(): Centra el rack en el viewport, respeta el ancho de paneles laterales (librería izquierda + propiedades derecha). Actualiza STATE.pos y reposiciona dispositivos.


Gestión de Dispositivos (devices.js)

Creación

addDeviceToRack(deviceData):

  1. Crea Konva.Group con atributos del dispositivo
  2. Posición absoluta: x = STATE.pos.x + getSlotX(slot), y = STATE.pos.y + calculateDeviceY()
  3. Hijos del grupo: Rect (fondo) + Image (stencil) + Text (nombre)
  4. draggable: !STATE.isLocked
  5. Registra en STATE.devices[] y añade a STATE.layer

Snap (Magnetismo)

Cada dispositivo tiene un dragBoundFunc:

  • Distancia snap: 60px a los raíles del rack
  • Full-width: Snap al raíl izquierdo
  • Half-width: Snap a LEFT o RIGHT según proximidad al centro del rack
  • Vertical: Snap a incrementos de U_HEIGHT (35px)

Detección de Colisiones

FunciónAlgoritmo
checkCollisionU(targetU, uHeight, ignoreNode)1D: solapamiento vertical entre rangos de U
checkCollision(targetU, uHeight, slot, ignoreNode)2D: vertical + horizontal. FULL bloquea cualquier posición. LEFT y RIGHT coexisten si no se solapan verticalmente

Flujo Drag-End

dragend →
  1. Validar que el dispositivo está dentro del rack
  2. Calcular u_position desde Y pixel
  3. checkCollision(targetU, uHeight, slot)
  4. Si colisión → revertir a posición anterior
  5. Si válido → actualizar u_position y slot
  6. Disparar auto-save

Eliminación

removeDeviceFromRack(device): destruye nodo Konva, elimina de STATE.devices, snapshot en historial, auto-save.


Undo/Redo (history_adapter.js)

Adaptador entre el editor y el servicio global StateManager:

Edición → saveState() → createSnapshot() → STATE_MANAGER.push(snapshot)
Ctrl+Z  → STATE_MANAGER.undo() → restoreState(snapshot)
                                      ↓
                                  Limpia STATE.devices
                                  Re-crea nodos Konva via addDeviceToRack()
                                  Restaura selección
                                  Dispara auto-save

Cada snapshot captura: array de dispositivos (clonado profundo de ports) + U position del seleccionado.


Persistencia y API (editor_api.js)

Auto-Save

Debounce de 500ms. Serializa STATE.devices[] y envía:

ApiService.put(`/api/racks/${rackId}/devices`, { devices });

Conversión Coordenadas - U Position

uPosition = (RACK_HEIGHT - (yRelative + deviceHeight)) / U_HEIGHT + 1
// yRelative = konvaNode.y() - STATE.pos.y
// deviceHeight = u_height * U_HEIGHT

Panel de Propiedades

Renderizado (PropertiesPanelRenderer.js)

Genera HTML dinámico para el dispositivo seleccionado:

  1. Cabecera: Nombre editable + tipo
  2. Imagen: Preview del stencil
  3. Posición: U position (read-only) + altura en U
  4. Management Config: IP, vendor, hostname
  5. Puertos Ethernet: Grid de hasta 48 puertos — formato enriquecido (live/desired/label) o legacy (string)
  6. Puertos SFP: Grid de hasta 8 puertos (layout 2 columnas)
  7. Notas: Textarea editable
  8. Acciones: Delete, Clone, Save to Library, QR, SSH, Backups

Eventos (PropertiesPanelEvents.js)

EventoAcción
Input nombreActualiza texto del nodo Konva + auto-save
Input puertoActualiza device.ports[key] + auto-save
Blur campoSnapshot Undo via request-save-history
DeleteremoveDeviceFromRack() + confirmación
CloneDuplica en siguiente U disponible
Save to LibraryPOST /api/stencils/create_from_device
QRQRLabelService.showDevicePreview()
SSHSshConfigModal → openSSHSession()
BackupsModal de config snapshots

Librería de Stencils (ui_library.js)

Estructura de un Stencil

{
    id: 123,
    name: "Cisco Catalyst 2960",
    image_path: "/media/uploads/stencils/cisco_2960.png",
    default_u_height: 1,
    manufacturer: "Cisco",
    category: "Switches",
    extra_data: '{"half_width": false, "ports": {...}}'
}

Drag desde Librería al Canvas

  1. Stencil tiene atributos data-id, data-name, data-type, data-height, data-image
  2. dragstart → metadata en STATE.currentDragItem
  3. drop sobre canvas → calcula posición U desde coordenadas del mouse
  4. addDeviceToRack() con datos del stencil
  5. STATE.currentDragItem = null

Backup y restauración de la librería de stencils

El panel Library del Rack Editor incluye botones para hacer backup y restaurar toda la librería de stencils de la organización.

Para hacer backup de los stencils:

  1. Abre el Rack Editor (cualquier rack)
  2. En el panel izquierdo Library, pulsa el botón Backup
  3. Se descarga un archivo ZIP con todos los stencils, categorías e imágenes de tu organización

Para restaurar stencils desde un backup:

  1. En el panel Library, pulsa el botón Restore
  2. Selecciona el archivo ZIP de backup
  3. El sistema analiza el contenido y muestra un resumen de lo que se importará
  4. Confirma para restaurar los stencils

Endpoints de la API:

  • GET /api/racks/library/backup — Descarga el backup ZIP completo de la librería
  • POST /api/racks/library/restore/analyze — Analiza un archivo de backup antes de restaurar
  • POST /api/racks/library/restore/confirm — Confirma y ejecuta la restauración

El backup incluye stencils personalizados, categorías y las imágenes asociadas. Los stencils globales (Default Pack) no se incluyen porque están disponibles para todas las organizaciones.

Importación Visio (Pipeline 2 pasos)

  1. Analyze: POST /api/racks/visio/analyze (FormData con .vssx/.vdx/.vss) → session_id + lista de masters
  2. Confirm: POST /api/racks/visio/confirm ({ session_id, masters, category_name }) → crea stencils
  3. Cleanup: DELETE /api/racks/cleanup/session/{id} — archivos temporales

Auto-Provision Panel (auto_provision_panel.js)

Flujo de integración con el módulo Auto-Provision:

Auto-Provision wizard
  ↓  Escribe en localStorage
localStorage['auto_provision_pending'] = {
    version: 2,
    racks: { "rack_42": [{ name, type, stencil_id, management_config }] }
}
  ↓  Editor detecta al cargar
auto_provision_panel.js lee localStorage, filtra por rack actual
  ↓  Renderiza panel flotante
Lista de dispositivos pendientes para ESTE rack
  ↓  Usuario selecciona y arrastra
addDeviceToRack() con management_config auto-set
  ↓  Dispositivo consumido
Se elimina de localStorage, panel se actualiza

Soporta formato v1 (legacy, array plano) y v2 (objeto con contexto de rack).


SSH Integrado

Terminal (ssh_integration.js)

1. Click "SSH" en panel de propiedades
2. SshConfigModal muestra formulario con datos de management_config
3. Confirmación → openSSHSession(deviceId, displayName)
4. Modal con terminal xterm.js embebido
5. WebSocket: ws://localhost:5050/ws/terminal (via Local Agent)
6. SFTP sidebar lazy-loaded si se solicita

Configuración (SshConfigModal.js)

Campos: IP, puerto SSH (default 22), vendor (autodetect SNMP), username, password, enable password.

Device reachability check via Local Agent:

POST http://localhost:5050/check?host={ip}&port={port}
→ { reachable: true/false }

SFTP (sftp_ui.js)

Browser → Django API → Local Agent (localhost:5050) → asyncssh → Dispositivo
Endpoint AgentDescripción
POST /sftp/listListar directorio
POST /sftp/downloadDescargar archivo
POST /sftp/uploadSubir archivo (multipart)
POST /sftp/mkdirCrear directorio
POST /sftp/deleteEliminar archivo/directorio

UI: Árbol de archivos con expansión dinámica, breadcrumbs, upload/download.


Config Backups (backups.js)

Snapshots de running-config para dispositivos de red gestionados:

  1. POST /api/device/{deviceId}/backup — Ejecuta SSH show running-config
  2. Backend selecciona comando según vendor (Cisco/Arista/Juniper)
  3. Almacena en ConfigBackup con hash SHA256 para detección de cambios
OperaciónDescripción
CreateSSH → captura config → almacena con hash
ListHistorial con timestamps y tipo (auto/manual/pre_change)
DiffCompara dos snapshots lado a lado
RestoreAplica configuración de backup anterior

Modelos Django (racks/models.py)

BoxCategory

Categorización de dispositivos. unique_together: (name, organization).

CampoTipoDescripción
nameCharField(100)Nombre de la categoría
organizationFK(Organization)Nullable — null = global

RackGroup

Agrupación lógica de racks. unique_together: (name, organization).

CampoTipoDescripción
nameCharField(100)Nombre del grupo
colorCharField(20)Color hex (default #6c757d)
organizationFK(Organization)Propietario
created_atDateTimeFieldAuto

Stencil

Templates de dispositivos (globales del sistema o por organización).

CampoTipoDescripción
nameCharField(100)Nombre del stencil
image_pathCharField(200)Ruta de la imagen
default_u_heightIntegerFieldAltura por defecto en U
manufacturerCharField(100)Fabricante
categoryCharField(100)Carpeta/categoría
extra_dataTextField (JSON)Metadata: half_width, ports, etc.
organizationFK(Organization)Nullable — null = stencil global del sistema

Property: image_url — resuelve path a URL servible.

Rack

Armario rack de datacenter.

CampoTipoDescripción
nameCharField(100)Nombre del rack
locationCharField(200)Ubicación física
notesTextFieldNotas libres
height_uIntegerFieldUnidades totales (default 42)
power_consumptionFloatFieldConsumo en kWh
statusCharFieldactive / planned / deprecated / maintenance
organizationFK(Organization)Propietario (CASCADE)
parent_rackFK(self)Rack padre (nullable, SET_NULL)
groupsM2M(RackGroup)Grupos asignados
created_atDateTimeFieldAuto
updated_atDateTimeFieldAuto
deleted_atDateTimeFieldSoft delete (nullable, indexed)
is_templateBooleanFieldTemplate reutilizable (indexed)

Metodos: soft_delete(), restore(). Property: is_deleted.

Device

Dispositivo dentro de un rack.

CampoTipoDescripción
rackFK(Rack)Rack contenedor (CASCADE)
nameCharField(100)Nombre del dispositivo
notesTextFieldNotas libres
u_positionIntegerFieldPosición U (base 1, desde abajo)
u_heightIntegerFieldAltura en U (default 1)
model_dataTextField (JSON)half_width, slot, ports, image, type
statusCharFieldonline / offline / warning / unknown
last_status_checkDateTimeFieldNullable
management_configTextField (JSON)IP, vendor, credenciales (Fernet AES encrypted)
groupsM2M(RackGroup)Grupos asignados

Properties: has_network_management, management_ip, management_vendor. Metodo: _decrypt_if_needed(value) — Fernet decryption para credenciales.

ConfigBackup

Snapshots de configuración de dispositivos de red.

CampoTipoDescripción
deviceFK(Device)Dispositivo (CASCADE)
rackFK(Rack)Rack (CASCADE)
config_textTextFieldRunning-config completo
config_hashCharField(64)SHA256 para detección de cambios
backup_typeCharFieldauto / manual / pre_change
created_atDateTimeFieldAuto (indexed)
created_byFK(User)Nullable (SET_NULL)
vendorCharField(50)Fabricante del dispositivo
device_versionCharField(100)Versión del firmware
is_activeBooleanFieldActivo/archivado
notesTextFieldNotas

Indexes: (device, -created_at), (rack, -created_at). Ordering: -created_at.

Relaciones

Organization (core)
    ├──→ Rack (FK) ←──→ RackGroup (M2M)
    │       ├──→ Device (FK) ←──→ RackGroup (M2M)
    │       │       └──→ ConfigBackup (FK)
    │       └──→ ConfigBackup (FK)
    ├──→ RackGroup (FK)
    ├──→ Stencil (FK, nullable = global si null)
    └──→ BoxCategory (FK, nullable)

API Endpoints (42+)

Racks CRUD (/api/racks/)

MetodoURLDescripción
POST/api/racks/Crear rack
GET/api/racks/{id}Obtener rack con dispositivos
PUT/api/racks/{id}Actualizar metadata
DELETE/api/racks/{id}Soft-delete
POST/api/racks/{id}/renameRenombrar
POST/api/racks/{id}/cloneClonar con dispositivos
PUT/api/racks/{id}/devicesAuto-save: sincronizar dispositivos del canvas
POST/api/racks/{id}/create_templateGuardar como template
POST/api/racks/{id}/apply_templateAplicar template (legacy — la UI del editor usa copy_from)
POST/api/racks/{id}/copy_fromCopy from… (v1.79.0): copiar el montaje de otro rack o Master — origen = cualquier rack de la organización (source_id), modo replace (borra destino) o add (solo donde no colisiona, regla de ocupación de racks/utils/placement.py). Notas y management_config no viajan. Servicio: racks/services/copy_from.py::copy_devices_from()
GET/api/racks/listLista ligera
GET/api/racks/templatesLista de templates
PUT/api/racks/bulk-updateActualización masiva

Grupos (/api/racks/groups/)

MetodoURLDescripción
GET/api/racks/groupsListar grupos
POST/api/racks/groupsCrear grupo
PUT/api/racks/groups/assignAsignar racks a grupo
DELETE/api/racks/groups/{id}Eliminar grupo

Stencils (/api/racks/stencils/)

MetodoURLDescripción
GET/api/racks/stencilsListar stencils (sistema + organización)
PUT/api/racks/stencils/{id}Editar stencil
DELETE/api/racks/stencils/{id}Eliminar stencil
POST/api/racks/stencils/customCrear custom
PUT/api/racks/stencils/{id}/moveMover de categoría
POST/api/racks/stencils/create_from_imageCrear desde imagen upload
POST/api/racks/stencils/create_from_deviceCrear desde dispositivo en canvas

Visio Import (/api/racks/visio/)

MetodoURLDescripción
POST/api/racks/visio/analyzeAnalizar archivo Visio
POST/api/racks/visio/confirmConfirmar importación
DELETE/api/racks/cleanup/session/{id}Limpiar sesión temporal

Library Backup/Restore (/api/racks/library/)

MetodoURLDescripción
POST/api/racks/library/importImportar librería JSON
GET/api/racks/library/backupExportar backup completo
POST/api/racks/library/restore/analyzeAnalizar backup
POST/api/racks/library/restore/confirmConfirmar restauración
DELETE/api/racks/library/category/deleteEliminar categoría
PUT/api/racks/library/category/renameRenombrar categoría

Export (/api/racks/export/)

MetodoURLDescripción
GET/api/racks/export/csvExportar todos los racks a CSV
GET/api/racks/{id}/export/csvExportar rack específico a CSV

Labels (/api/racks/labels/)

MetodoURLDescripción
GET/api/racks/labels/{target}Generar QR labels (target = “all” o rack_id)
GET/api/racks/labels/rack_info/{target}Info del rack para QR

Papelera (/api/racks/trash/)

MetodoURLDescripción
GET/api/racks/trash/racksListar racks en papelera
POST/api/racks/trash/racks/{id}/restoreRestaurar rack
DELETE/api/racks/trash/racks/{id}Eliminar permanentemente
DELETE/api/racks/trash/racksVaciar papelera
GET/api/racks/trash/countContar racks en papelera

Diagramas de Flujo

Carga del Editor

GET /editor/{rack_id}/
  ↓ Django view → template editor.html
  ↓ main.js bootstrap
  ├── Lee #rack-data del DOM → STATE.rackData
  ├── Calcula CONFIG.RACK_HEIGHT = height_u * U_HEIGHT
  ├── konva_setup.js → Stage, Layer, rackGroup
  ├── fitRackToView() → Centra rack en viewport
  ├── devices.js → addDeviceToRack() por cada device
  ├── ui_library.js → Carga stencils en panel izquierdo
  ├── auto_provision_panel.js → Check localStorage pending
  └── history_adapter.js → Snapshot inicial

Auto-Save

Usuario edita (drag, rename, port change...)
  ↓ Módulo correspondiente
  ├── Actualiza STATE.devices[i]
  ├── triggerAutoSave() → debounce 500ms → saveRackState()
  │                                           ↓
  │                                   PUT /api/racks/{id}/devices
  └── dispatchEvent('request-save-history')
          ↓
      history_adapter.saveState() → createSnapshot() → STATE_MANAGER.push()

Última actualización: 21-08-2026 Mantenido por: Equipo CreaRack

Véase también

  • [[crearack-tech—agents—dev-rack-editor]] — agente técnico del Rack Editor
  • [[concept—racks—editor]] — concepto del Rack Editor
  • [[entity—racks—model—rack]] — modelo Rack
  • [[entity—racks—model—device]] — modelo Device (dispositivo)
  • [[entity—racks—model—stencil]] — modelo Stencil
  • [[crearack—conceptos—rack-editor-conceptos]] — conceptos base del Rack Editor para usuarios
  • [[crearack—racks—crear-rack]] — cómo crear un rack en la app
  • [[crearack-tech—guides—interconnection-guide]] — guía de interconexionado (cableado y puertos entre dispositivos)