01 · Del comando a la representación
Una representación compartida
En el tema 38 separamos intención, resultado de comando y estado reportado. El gemelo digital de estado organiza esa información en la plataforma para consultar un equipo y coordinar cambios, incluso cuando no está conectado.
En este curso trabajamos con una representación de identidad, configuración y condiciones operativas. Puede ser la base de modelos más amplios, pero su utilidad inicial ya es concreta: responder qué configuración se pidió, cuál se aplicó y cuándo se obtuvo la última evidencia.
Una plataforma puede conservar ese documento mientras el equipo está desconectado. La disponibilidad del documento no demuestra disponibilidad del dispositivo. Un valor reportado conserva su fecha y no se vuelve actual porque alguien abrió el panel.
Servicios como AWS IoT Device Shadow y Azure IoT Hub device twins ofrecen representaciones de este tipo. Sus APIs y reglas son propias. El ejemplo usa un contrato didáctico independiente y no requiere una cuenta en esos servicios.
02 · Un documento con responsabilidades
Las partes del documento
| Parte | Contenido | Ejemplo |
|---|---|---|
| Identidad y metadatos | Equipo, modelo, ubicación y capacidades | aux07, sitio norte, firmware y esquemas compatibles |
| Deseado | Configuración objetivo autorizada | Alta de 8,5 °C, revisión 3 |
| Reportado | Configuración y condiciones informadas por el equipo | Alta de 8,0 °C, última revisión aplicada 1 |
| Reconciliación | Qué revisión vio, resultado y motivo | Vio la 3, la rechazó por una restricción local |
| Metadatos temporales | Observación, recepción y orden de reportes | Observado a los 12 s; recibido a los 14 |
{
"device": "aux07",
"desired": {
"version": 3, "schema": 1,
"high_dc": 85, "reset_dc": 75
},
"reported": {
"epoch": 1, "seq": 7,
"seen_version": 3, "applied_version": 1,
"high_dc": 80, "reset_dc": 75,
"status": "rejected", "reason": "local_limit",
"observed_at_ms": 1767225612000,
"received_at_ms": 1767225614000
}
}high_dc y reset_dc son décimas de °C. El ejemplo reporta un rechazo: ver la revisión 3 no equivale a aplicarla. El valor anterior sigue siendo la configuración efectiva informada.
El documento es una instantánea. La historia de mediciones permanece en la base del tema 34 y el historial de comandos en el registro del tema 38. Sobrescribir una propiedad actual no conserva por sí solo todos sus cambios anteriores.
El esquema de documentos de AWS Shadow ilustra propiedades deseadas, reportadas, versiones y diferencias. Los nombres de nuestro ejemplo no se presentan como una copia compatible con su protocolo.
03 · Evitar que ambos extremos inventen el mismo estado
Quién escribe cada parte
El servicio autorizado escribe la configuración deseada. El dispositivo publica lo que realmente está usando y el resultado de su evaluación. La plataforma valida y conserva esos reportes. Los metadatos administrativos pueden tener otro propietario.
Guardar el deseado no modifica automáticamente el reportado. Copiar ambos al presionar «guardar» haría parecer aplicada una configuración que el equipo todavía no recibió. La respuesta de la API confirma la escritura en la plataforma, no su aplicación local.
El dispositivo tampoco borra la intención porque no pueda cumplirla: reporta su motivo. Cambiar el deseado para hacerlo coincidir con cualquier valor local escondería un rechazo o una intervención que necesita una decisión del operador.
Propiedades con permiso limitado
Un equipo puede informar sus capacidades, pero no debería alterar su pertenencia a otro cliente. Un operador de un sitio no puede escribir el deseado de otro sitio. Los permisos se verifican por identidad, recurso y operación; ocultar una propiedad en la interfaz no reemplaza esos controles.
En el simulador solo se cambia una configuración de alarma. Las activaciones temporales del tema 37 y operaciones como «ejecutar un ciclo» conservan su contrato de comandos. Un booleano deseado persistente no expresa cuántas veces debe ocurrir una acción puntual.
04 · Igualdad y evidencia
Versiones y frescura
Hay varias secuencias con propósitos distintos. schema indica cómo interpretar el documento; la versión deseada identifica una intención de configuración. El equipo informa la última revisión vista y la última aplicada. La secuencia del reporte ordena observaciones, incluso cuando la configuración no cambió.
revisión aplicada = revisión deseada
valores efectivos = valores deseados
estado de aplicación confirmado y observación suficientemente reciente
Si la plataforma guarda revisión 2 con el mismo valor que ya tenía la 1, la diferencia de valores queda vacía. Aun así, eso no demuestra que el equipo haya validado la nueva revisión o su nuevo esquema. La comparación de valores es necesaria, pero no reemplaza la confirmación de versión.
Un reporte viejo no se rejuvenece al llegar
Se conserva observación y recepción. La edad se calcula desde la observación con una referencia de tiempo confiable. Un mensaje antiguo que llega hoy no prueba que la configuración siga aplicada ahora.
En el laboratorio, reportes de configuración contienen una observación nueva del estado local cada seis segundos de conexión. No son mediciones antiguas de sensor renombradas con otra fecha. El límite de frescura es configurable; la igualdad se incluye: edad igual al límite todavía cuenta como reciente.
La secuencia de reportes se compara dentro de una generación de dispositivo conocida. Un identificador de arranque aleatorio no es un contador ordenable. Al cambiar de generación se necesita un procedimiento de identidad y resincronización, no elegir el texto que parezca mayor.
05 · Escribir sobre lo que se leyó
Dos operadores y una configuración
A y B leen revisión 1. A guarda un alta de 9,0 °C y crea revisión 2. B intenta guardar 8,5 °C sobre su lectura vieja. Si no se comprueba la base, el último escritor reemplaza el cambio anterior sin advertirlo.
Una actualización condicional pide «aplicar solo si la revisión que leí todavía es vigente». El servicio compara y escribe en una operación atómica. Si la base no coincide, informa conflicto y no cambia el documento.
GET /api/dispositivos/aux07/desired
ETag: "desired-12"
PUT /api/dispositivos/aux07/desired
If-Match: "desired-12"
Content-Type: application/json
{"schema":1,"high_dc":85,"reset_dc":75}Una API puede usar If-Match y un ETag fuerte para esta precondición, según RFC 9110. El ETag es un identificador opaco del recurso; el cliente no debe incrementarlo por su cuenta. En otra API se puede usar una revisión de aplicación explícita.
En una base relacional, la idea se representa con UPDATE ... WHERE device = ? AND desired_version = ?. Afectar cero filas significa que no se cumplió la precondición, no que el equipo rechazó una configuración.
Resolver no es repetir ciegamente
El operador relee el documento y decide si mantiene su cambio, lo modifica o lo abandona. Reenviar automáticamente el cuerpo sobre cualquier revisión nueva volvería a ocultar el conflicto. La casilla de resolución del laboratorio representa una decisión explícita de B después de releer.
Las revisiones deseadas del curso son independientes de los reportes de diagnóstico. Algunos servicios usan una revisión global de documento con otras reglas. Hay que respetar el contrato concreto para no confundir versiones de intención, almacenamiento y observación.
06 · Hacer converger con evidencia
El ciclo de reconciliación
- Obtener la instantánea vigente: al conectar o al recibir una notificación de cambio.
- Validar: esquema, rango, coherencia, capacidades y autoridad local.
- Aplicar un bloque: preparar una configuración completa y reemplazarla entre evaluaciones.
- Reportar: revisión vista, revisión aplicada, valores efectivos y resultado.
- Comparar en la plataforma: diferencia, motivo y frescura, sin inventar una aplicación.
Una notificación indica que puede haber cambios; no siempre contiene una configuración completa. Si se pierde o llega desordenada, consultar la instantánea permite recuperar el objetivo vigente. Procesar solo diferencias incrementales exige una base y una política de recuperación de huecos.
El modelo usa documentos completos. Las semánticas de campo ausente, valor nulo y eliminación se definen por esquema o API. No se supone que un null signifique lo mismo en todos los proveedores o que deba convertirse en cero.
Persistir una configuración y aplicarla en RAM son etapas diferentes. El equipo declara el nivel que promete y maneja sus fallas. Nuestra simulación aplica en memoria y no representa un corte de energía ni una escritura flash.
07 · La nube puede avanzar sin el equipo
Trabajar sin conexión
Mientras el dispositivo está fuera de línea, la plataforma puede aceptar nuevas configuraciones deseadas. El reportado conserva la última observación y envejece. Al reconectar, el equipo obtiene el objetivo actual y reporta el resultado de su evaluación.
En el caso inicial, A crea revisión 2 a los 2 s y B resuelve su conflicto creando revisión 3 a los 6 s. El equipo vuelve a los 8 s y aplica directamente la 3. No tuvo que aplicar primero 9,0 °C y luego 8,5 °C.
Esto sirve para una configuración objetivo: importa a qué estado converger. Si cada paso intermedio debe ejecutarse, como tres ciclos de una máquina, se necesita el protocolo de comandos e historial del tema 38. Un documento que conserva solo el objetivo más reciente no sustituye una cola de acciones.
Recuperar sin crear una pelea de valores
El reencuentro no significa que la nube siempre deba imponerse. Se consulta el modo y las condiciones locales. Una intervención local puede impedir aplicar y debe quedar reportada, sin borrar la intención remota ni forzarla en cada iteración.
Al abandonar el modo local, se vuelve a evaluar el objetivo vigente incluso si su revisión ya se había visto. Comparar únicamente «hay una versión mayor» perdería una deriva local posterior. La reconciliación también considera valores efectivos y la transición de autoridad.
08 · Estados que requieren explicación
Rechazos, cambios locales y diagnóstico
| Situación | Presentación | Qué evita |
|---|---|---|
| La observación venció | Observación atrasada | Declarar convergencia actual a partir de evidencia vieja |
| El equipo informa modo local | Intervención local | Que un bucle remoto anule el mando local |
| Vio la revisión vigente y la rechazó | Rechazada por el equipo, con motivo | Reintentos constantes de la misma configuración inválida |
| Faltan revisión, valores o confirmación | Pendiente de reconciliación | Confundir igualdad parcial con aplicación |
| Revisión y valores confirmados recientemente | Convergente según reporte | Presentar un estado sin su fuente temporal |
El dispositivo del laboratorio entiende esquema 1. Puede recibir un esquema 2 que la plataforma admita para otro firmware y rechazarlo sin alterar su configuración válida. También tiene un máximo local configurable, de modo que una intención dentro del rango general puede fallar por una condición adicional del equipo.
Un reporte de rechazo es nuevo aunque la revisión aplicada siga siendo la 1. No se descarta por tener una versión aplicada menor que la deseada: se ordena por su secuencia de observación. La versión aplicada y la secuencia del reporte contestan preguntas distintas.
Si se pierde ese reporte, una observación periódica posterior vuelve a comunicar el estado y su motivo. El ejemplo no retransmite cada paquete perdido; publica instantáneas nuevas cuando hay enlace. El producto debe dimensionar el tráfico y la demora de recuperación según su plan de telemetría.
09 · Un recurso pequeño y acotado
API y persistencia
El gemelo se puede exponer por una API y notificaciones MQTT o WebSocket. El almacenamiento debe conservar identidad, revisiones y metadatos. No hace falta incluir toda la telemetría dentro del mismo JSON: crecería sin límites y dificultaría actualizaciones y consultas.
GET /api/dispositivos/aux07/twin → representación autorizada
PUT /api/dispositivos/aux07/desired → actualización condicional
POST /api/dispositivos/aux07/reported → observación autenticada del equipo
La respuesta a desired confirma almacenamiento de intención.
La actualización de reported conserva tiempo y secuencia del emisor.Una escritura de reporte anterior no reemplaza un reporte más reciente de la misma generación. Una secuencia repetida con contenido incompatible se diagnostica; el ejemplo solo inyecta una copia antigua legítima. Los cambios de identidad, reinicios y migraciones requieren procedimientos explícitos.
Historial y costo
El documento actual es una proyección consultable. Para auditar quién cambió una configuración o cómo fue rechazada se conserva un historial independiente con autor, revisión y resultado. Eliminar un campo actual no debe borrar accidentalmente evidencia que el producto necesita retener.
Cinco mil documentos de 2 KiB representan unos 9,77 MiB lógicos, pero actualizarlos cada minuto generaría 7,2 millones de escrituras al día. Tamaño y tasa de actualización son presupuestos distintos. Se informa por cambio y con un refresco acotado para observar frescura, según la necesidad real.
Los permisos, las copias de seguridad y la recuperación pertenecen al servicio. La persistencia del gemelo en la nube tampoco sustituye la configuración de arranque que el dispositivo debe conservar y validar localmente.
10 · Reconciliación reproducible
Simulación en Python
El programa reproduce el laboratorio: dos escritores, conexión, aplicación y observaciones con demora. El documento inicial es información ya conocida al comenzar. El reloj común está en segundos; el equipo tarda un segundo en aplicar y el reporte dos segundos en volver.
Python en el navegador. El bloque no usa servicios externos ni controla equipos. Se puede editar, ejecutar con Pyodide o copiar a un archivo Python 3.
def simular(red="reconexion", condicional=True, segunda=True, resolver=True,
valorA=90, valorB=85, esquema=1, limiteLocal=100,
intervencion="ninguna", ordenar=True, tardio=True, frescura=10):
deseado = dict(version=1, schema=1, high_dc=80, reset_dc=75, actualizado=0)
equipo = dict(aplicada=1, vista=1, high_dc=80, reset_dc=75, modo="remoto",
estado="aplicada", motivo="", secuencia=1)
reportado = dict(epoch=1, seq=1, aplicada=1, vista=1, high_dc=80, reset_dc=75,
modo="remoto", estado="aplicada", motivo="", observado=0, recibido=0)
antiguo = reportado.copy()
tarea, mensajes, conflictoB, forzar = None, [], False, False
conflictos = ignorados = aplicaciones = rechazos = 0
filas = []
for t in range(33):
online = red == "estable" or (t >= 8 if red == "reconexion" else t < 16)
acciones, emitido = [], False
def publicar():
nonlocal emitido
if not online or emitido:
return
equipo["secuencia"] += 1
r = {k: equipo[k] for k in ("aplicada", "vista", "high_dc", "reset_dc", "modo", "estado", "motivo")}
r.update(epoch=1, seq=equipo["secuencia"], observado=t)
mensajes.append(dict(llega=t+2, reporte=r))
emitido = True
acciones.append(f'Publicar observación seq {equipo["secuencia"]}')
def actualizar(base, valor, autor):
nonlocal deseado, conflictos
if condicional and base != deseado["version"]:
conflictos += 1
acciones.append(f'Conflicto de {autor}: base v{base}, actual v{deseado["version"]}')
return False
deseado = dict(version=deseado["version"]+1, schema=esquema,
high_dc=valor, reset_dc=75, actualizado=t)
acciones.append(f'{autor} guarda deseado v{deseado["version"]}: {valor/10:g} °C')
return True
if t == 0:
acciones.append("Documento inicial conocido: deseado y reportado v1, 8,0 °C")
if t == 2:
actualizar(1, valorA, "Operador A")
if segunda and t == 4:
conflictoB = not actualizar(1, valorB, "Operador B")
if segunda and resolver and conflictoB and t == 6:
acciones.append("B relee y decide explícitamente presentar su valor sobre la revisión actual")
actualizar(deseado["version"], valorB, "B tras resolver")
conflictoB = False
if intervencion != "ninguna" and t == 16:
equipo.update(modo="local", high_dc=80, estado="intervencion", motivo="Selector local: alta de 8,0 °C")
tarea = None
acciones.append("Intervención local: se cambia el valor sin modificar el deseado")
publicar()
if intervencion == "retorno" and t == 22:
equipo.update(modo="remoto", estado="revisar", motivo="")
forzar = True
acciones.append("Regresa a remoto: reconciliar incluso si ya se había visto esa versión")
if tarea is not None and tarea["vence"] == t:
d = tarea["doc"]
equipo.update(aplicada=d["version"], high_dc=d["high_dc"], reset_dc=d["reset_dc"], estado="aplicada", motivo="")
aplicaciones += 1
acciones.append(f'Aplicar configuración v{equipo["aplicada"]} como bloque')
tarea = None
publicar()
if online and equipo["modo"] == "remoto" and tarea is None and (deseado["version"] > equipo["vista"] or forzar):
equipo["vista"], forzar = deseado["version"], False
acciones.append(f'Consultar instantánea vigente v{deseado["version"]}')
if deseado["schema"] != 1 or deseado["high_dc"] <= deseado["reset_dc"] or deseado["high_dc"] > limiteLocal:
motivo = "Esquema no soportado" if deseado["schema"] != 1 else "Restricción local de configuración"
equipo.update(estado="rechazada", motivo=motivo)
rechazos += 1
acciones.append(f'No aplicar v{deseado["version"]}: {motivo}')
publicar()
else:
equipo.update(estado="aplicando", motivo="")
tarea = dict(vence=t+1, doc=deseado.copy())
publicar()
if online and t > 0 and t % 6 == 0:
publicar()
if tardio and t == 13:
mensajes.append(dict(llega=t, reporte=antiguo.copy()))
for m in [m for m in mensajes if m["llega"] == t]:
if not online:
acciones.append("Se pierde el reporte en el corte; habrá otra observación periódica")
continue
if ordenar and m["reporte"]["seq"] <= reportado["seq"]:
ignorados += 1
acciones.append(f'Ignorar reporte anterior seq {m["reporte"]["seq"]}')
continue
reportado = dict(m["reporte"], recibido=t)
acciones.append(f'Actualizar reportado seq {reportado["seq"]}, aplicada v{reportado["aplicada"]}')
mensajes = [m for m in mensajes if m["llega"] > t]
edad = t - reportado["observado"]
delta = [k for k in ("high_dc", "reset_dc") if deseado[k] != reportado[k]]
estado = ("Observación atrasada" if edad > frescura else
"Intervención local" if reportado["modo"] == "local" else
"Rechazada por el equipo" if reportado["vista"] == deseado["version"] and reportado["estado"] == "rechazada" else
"Convergente según reporte" if reportado["estado"] == "aplicada" and reportado["aplicada"] == deseado["version"] and not delta else
"Pendiente de reconciliación")
filas.append(dict(t=t, online=online, deseado=deseado.copy(), equipo=equipo.copy(),
reportado=reportado.copy(), edad=edad, delta=delta, estado=estado,
conflictos=conflictos, ignorados=ignorados, aplicaciones=aplicaciones,
rechazos=rechazos, acciones=acciones))
return filas
base = simular()
for f in base:
if f["acciones"]:
print(f't={f["t"]:2} | {f["estado"]} | {". ".join(f["acciones"])}')
assert (base[-1]["deseado"]["version"], base[-1]["equipo"]["high_dc"], base[-1]["aplicaciones"]) == (3, 85, 1)
assert (base[-1]["conflictos"], base[-1]["ignorados"]) == (1, 1)
assert base[10]["estado"] == "Pendiente de reconciliación"
assert base[11]["estado"] == "Convergente según reporte"
igual = simular(segunda=False, valorA=80)[2]
assert igual["delta"] == [] and igual["estado"] == "Pendiente de reconciliación"
assert simular(resolver=False)[-1]["deseado"]["high_dc"] == 90
assert simular(condicional=False)[-1]["conflictos"] == 0
assert simular(red="estable")[-1]["aplicaciones"] == 2
assert simular(valorB=95, limiteLocal=85)[-1]["estado"] == "Rechazada por el equipo"
assert simular(esquema=2)[-1]["reportado"]["motivo"] == "Esquema no soportado"
assert simular(intervencion="local")[-1]["estado"] == "Intervención local"
assert simular(intervencion="retorno")[-1]["aplicaciones"] == 2
corte = simular(red="corte", intervencion="local")[-1]
assert corte["edad"] == 20 and corte["delta"] == [] and corte["estado"] == "Observación atrasada"
assert simular(ordenar=False)[13]["reportado"]["seq"] == 1
assert simular(red="corte", frescura=20)[-1]["estado"] == "Convergente según reporte" and simular(red="corte", frescura=19)[-1]["estado"] == "Observación atrasada"
print("\n15 comprobaciones superadas.")Caso inicial: un conflicto de B, deseado v3 con alta 8,5 °C, una sola aplicación al reconectar, convergencia informada a los 11 s y un reporte antiguo ignorado. Las quince comprobaciones terminan sin errores.
La persistencia, la autenticación y los cortes durante escritura no están implementados. Se supone que la API ya validó campos y rangos generales. El equipo aplica sus restricciones adicionales. La época es fija en esta ejecución; no se demuestra un protocolo de cambio de identidad.
11 · Exploración interactiva
Laboratorio del gemelo
Prepará el gemelo y avanzá segundo a segundo. El estado interno del equipo se muestra para explicar el modelo; en un panel real solo se conoce a través de sus reportes y sus edades.
Configuración de alarma de aux07
Preparando configuración.
Estado interno del dispositivo simulado
—
—
Deseado en la plataforma
Reportado recibido
Preparando recorrido.
| t (s) | Qué ocurre | Deseado | Aplicada reportada | Estado de vista |
|---|
Recorrido guiado
- En t = 2, A crea v2. En t = 4, B tiene conflicto por usar v1. La decisión explícita de t = 6 crea v3 con 8,5 °C.
- El equipo vuelve a los 8 s y consulta v3; la aplica en 9. A los 10 todavía se conoce un reporte intermedio; a los 11 s llega la aplicación confirmada. Solo se aplicó una configuración nueva.
- Quitá la resolución de B: queda v2 con 9,0 °C. Quitá la escritura condicional: B reemplaza a A en t = 4 sin advertencia. Son decisiones diferentes.
- Elegí enlace siempre disponible. El equipo aplica v2 y luego v3: dos aplicaciones. Desconectado podía saltar la configuración intermedia porque este es un objetivo persistente.
- Quitá la segunda escritura y elegí A = 8,0 °C. A los 2 s no hay diferencia de valores, pero está pendiente confirmar v2. Igualdad no demuestra evaluación de la revisión.
- Elegí B = 9,5 °C y límite local 8,5. La revisión vigente queda rechazada; sigue aplicada v1. Esquema 2 también se rechaza, con otro motivo.
- Con opciones iniciales, desactivá el descarte de reportes antiguos y avanzá a t = 13. Una observación de t = 0 reemplaza indebidamente la nueva. La siguiente observación puede corregirlo, pero la vista ya retrocedió.
- Activá intervención local. Desde t = 16 el valor interno pasa a 8,0 sin borrar el deseado. Al volver a remoto en 22, se reevalúa la misma revisión y se aplica otra vez, con reporte final a los 25.
- Elegí corte desde 16 y una intervención local. A los 32 el documento todavía tiene valores reportados iguales al deseado, pero su observación tiene 20 s. Con límite de 10 se muestra atrasada, sin afirmar convergencia actual.
¿La nube sabe siempre que el modo local cambió?
Solo cuando recibe información que lo demuestra. Si el modo cambió durante un corte, el gemelo conserva la observación anterior y su edad. La simulación permite mirar el interior del equipo para mostrar esa diferencia de conocimiento.
12 · Diseñar responsabilidades y recuperación
Método y confusiones frecuentes
- Definí el alcance: configuración, diagnóstico y capacidades, con historia fuera de la instantánea.
- Asigná propietarios y permisos: cada extremo escribe sus partes autorizadas.
- Separá versiones: esquema, intención, aplicación y secuencia de observación.
- Protegé escrituras concurrentes: precondición atómica y resolución explícita de conflictos.
- Reconciliá desde una base conocida: instantánea y recuperación de notificaciones perdidas.
- Conservá motivos y tiempos: visto, aplicado, rechazado, local y atrasado son estados diferentes.
- Probá deriva y desconexión: la igualdad aparente no sustituye evidencia reciente.
- Diseñá persistencia y evolución: arranque, versiones compatibles y auditoría de cambios.
«Guardar el gemelo cambia inmediatamente el equipo»
Confirma una escritura en la plataforma. La aplicación se conoce después de recibir evidencia del equipo, con revisión y tiempo.
«Si el delta está vacío ya terminó la tarea»
Los valores pueden coincidir con los anteriores mientras la revisión nueva no fue evaluada. También puede tratarse de una observación vencida. Se revisan versión, resultado y frescura.
«El reportado debe copiar siempre al deseado»
Debe representar lo que el equipo informa. Si hubo rechazo, intervención o aplicación pendiente, copiarlo ocultaría el problema.
«Cada cambio del deseado es una acción que se ejecutará»
Una instantánea objetivo puede reemplazar cambios intermedios durante un corte. Para ejecutar cada acción se usa una cola de comandos con identidad y resultados.
«Una secuencia de reporte mayor demuestra una configuración mayor»
Puede informar un rechazo, la misma configuración o una intervención local. Ordena observaciones; la revisión aplicada tiene otro significado.
«Repetir la escritura sobre la última versión resuelve cualquier conflicto»
Puede ocultar el cambio de otro operador. Se relee, se comprende la diferencia y se decide qué intención mantener. La precondición solo detecta la carrera.
13 · Práctica guiada
Predecí antes de comprobar
1. Visto frente a aplicado
El deseado está en v12 y el equipo reporta vista 12, aplicada 11 y rechazo. ¿Ya convergió?
Ver solución
No. Recibió la revisión, pero no la aplicó. Se conserva la configuración válida anterior y se muestra el motivo del rechazo.
2. Valores iguales
V2 repite los valores de v1; solo está confirmada v1. ¿Un delta vacío demuestra que el firmware aceptó v2?
Ver solución
No. Falta confirmar la evaluación y aplicación de esa revisión, incluido su esquema. Coincidir en valores no aporta esa evidencia.
3. Conflicto entre operadores
A y B leen v7. A guarda y crea v8. B escribe con precondición v7. ¿Qué debe ocurrir?
Ver solución
La escritura de B falla por conflicto sin reemplazar v8. B relee y decide qué conservar antes de presentar una nueva actualización.
4. Tres cambios desconectados
Mientras el equipo no tiene red, se guardan objetivos 8,0; 9,0 y 8,5 °C. ¿Debe aplicar los tres al volver?
Ver solución
Para una configuración objetivo, puede aplicar solo el vigente de 8,5 °C. Si cada paso fuera obligatorio, se necesitaría otro contrato de comandos y secuencias.
5. Reporte antiguo
Ya se conoce epoch 1, seq 20. Llega otra observación de la misma época con seq 18. ¿Reemplaza el estado actual?
Ver solución
No. Se reconoce como anterior. Puede conservarse para auditoría si corresponde, pero no hace retroceder la representación actual.
6. Cambio local
El deseado v3 sigue en 8,5 °C y un selector local fija 8,0. ¿Conviene borrar el deseado para que coincidan?
Ver solución
Se reporta la intervención y el valor efectivo. Borrar la intención ocultaría el conflicto. La política de autoridad decide cuándo y cómo reconciliar.
7. Igualdad atrasada
Los valores y revisiones coinciden, pero la observación tiene 200 s y el límite es 120. ¿Qué indica la vista?
Ver solución
Observación atrasada. La plataforma puede afirmar que coincidían entonces, sin demostrar que sigan así ahora.
8. Tamaño y tasa
Hay 5000 documentos de 2 KiB y una actualización por minuto. ¿Cuánto tamaño lógico y cuántas actualizaciones diarias representan?
Ver solución
Son 10.000 KiB, unos 9,77 MiB, y 7.200.000 actualizaciones al día. No incluyen índices, historial ni réplicas; el tamaño pequeño no implica poco trabajo de escritura.
14 · Síntesis
Ideas para recordar
- El gemelo conserva intención, observación y contexto del dispositivo mientras sus participantes se conectan y desconectan.
- Deseado y reportado tienen propietarios distintos. Guardar una intención no confirma su aplicación.
- Esquema, versión deseada, versión vista, aplicada y secuencia del reporte cumplen funciones diferentes.
- Las escrituras condicionales detectan conflictos; resolverlos requiere decidir qué intención conservar.
- La reconciliación valida y aplica una instantánea, respeta autoridad local y reporta evidencia o rechazo.
- Igualdad de valores no alcanza: la vista también verifica versión, estado y frescura.
- En el caso base se aplica directamente v3 al reconectar y se informa convergencia a los 11 s.
- La instantánea no reemplaza historial de telemetría, comandos ni auditoría.
¿Listo para el tema 40?
Antes de avanzar, definí el gemelo de tu proyecto: quién escribe cada campo, qué versiones existen y cómo se representa un rechazo. Probá cambios simultáneos, modo local y un reporte viejo.
En el próximo tema, Seguridad básica de dispositivos, revisamos amenazas, credenciales por defecto y superficie de ataque para proteger los canales, datos y operaciones que construimos.