Control de planta
Diseño paso a paso
Un método repetible para pasar del enunciado de una instalación a su programa concurrente. Lo desarrollamos con un ejemplo completo: una célula de etiquetado donde las piezas llegan por una cinta, se etiquetan de una en una y salen por otra cinta; la etiquetadora tiene un cargador de 50 etiquetas y un operario lo repone cuando se agota.
Paso 1 · Inventario de elementos
| Elemento | Entradas (sensores) | Salidas (actuadores) |
|---|---|---|
| Cinta de entrada | barrera de llegada de pieza | marcha/paro |
| Etiquetadora | presencia de pieza en su interior | ciclo de etiquetado, contador de etiquetas |
| Cinta de salida | barrera de salida | marcha/paro |
| Cargador de etiquetas | contador restante | — (lo repone el operario) |
Paso 2 · Actividades concurrentes → hilos
Pregunta clave: ¿qué cosas ocurren a la vez? Cada una es un hilo:
hEntrada: trae piezas hasta la etiquetadora.hEtiquetadora: etiqueta y entrega.hSalida: evacúa las piezas etiquetadas.hOperario: repone el cargador cuando se agota.
Paso 3 · Recursos y capacidades
- La posición de la etiquetadora admite 1 pieza → buffer de capacidad 1.
- El cargador admite 50 etiquetas → recurso contador.
- La cinta de salida solo admite 1 pieza en su extremo → buffer de capacidad 1.
Paso 4 · Relaciones de sincronización
Cada junta productor/consumidor recibe su pareja sp/sc, y cada recurso compartido, su semáforo:
| Semáforo | Valor inicial | Significado |
|---|---|---|
spEntradaEti | 1 | hueco en la etiquetadora (productor: hEntrada) |
scEntradaEti | 0 | pieza presente en la etiquetadora (consumidor: hEtiquetadora) |
spEtiSalida | 1 | hueco en la cinta de salida |
scEtiSalida | 0 | pieza etiquetada en la salida |
sEtiquetas | 50 | etiquetas restantes en el cargador |
sAviso | 0 | señal «cargador agotado» para el operario |
Paso 5 · Bucle de cada hilo
semaforo spEntradaEti = 1, scEntradaEti = 0;
semaforo spEtiSalida = 1, scEtiSalida = 0;
semaforo sEtiquetas = 50;
semaforo sAviso = 0;
void hEntrada() {
while (1) {
cintaEntrada(1); // cinta en marcha
while (!sensorLlegada()); // hasta que llegue una pieza
cintaEntrada(0);
wait(spEntradaEti); // espera hueco en la etiquetadora
transferirAEtiquetadora(); // empuja la pieza dentro
signal(scEntradaEti); // la etiquetadora ya tiene pieza
}
}
void hEtiquetadora() {
while (1) {
wait(scEntradaEti); // espera pieza
wait(sEtiquetas); // gasta una etiqueta (si hay 0, bloquea)
etiquetar(); // ciclo de etiquetado
wait(spEtiSalida); // espera hueco en la salida
transferirASalida();
signal(spEntradaEti); // su posición queda libre
signal(scEtiSalida); // hay pieza etiquetada en la salida
}
}
void hSalida() {
while (1) {
wait(scEtiSalida); // espera pieza etiquetada
cintaSalida(1);
while (!sensorSalida()); // hasta que la pieza abandone
cintaSalida(0);
signal(spEtiSalida); // el hueco vuelve a estar libre
}
}
void hOperario() {
while (1) {
wait(sAviso); // duerme hasta que falten etiquetas
reponerCargador(); // el operario carga 50 etiquetas
for (int i = 0; i < 50; i++)
signal(sEtiquetas); // repone los 50 permisos
}
}
int main() {
concurrente {
hEntrada();
hEtiquetadora();
hSalida();
hOperario();
}
}
Paso 6 · Completar el aviso del cargador
Falta que alguien haga signal(sAviso). Lo natural es en la propia etiquetadora cuando detecta
el cargador casi vacío; con semáforo contador basta comprobar antes de esperar:
// dentro de hEtiquetadora, justo antes de wait(sEtiquetas):
if (etiquetasRestantes() == 1)
signal(sAviso); // avisa con antelación al operario
Paso 7 · Verificación de invariantes
Antes de darlo por bueno, repasa la lista:
- Deadlock: ¿algún hilo espera un semáforo que solo señaliza él mismo o un hilo que a su vez lo espera? (Aquí no: el grafo de dependencias es una cadena.)
- Capacidades: ¿puede alguna posición física tener 2 piezas? Cada
signal(sc…)corresponde a una sola pieza → no. - Señales perdidas: todo
signaltiene unwaitque lo consumirá tarde o temprano. - Inanición: ¿puede una pieza quedarse sin procesar para siempre? No hay colas con prioridad que la salten.
Paso 8 · Traducción a C++ moderno
std::binary_semaphore spEntradaEti(1), scEntradaEti(0);
std::binary_semaphore spEtiSalida(1), scEtiSalida(0);
std::counting_semaphore<50> sEtiquetas(50);
std::binary_semaphore sAviso(0);
// wait(s) → s.acquire(); signal(s) → s.release();
// Los hilos se lanzan como std::jthread para parada ordenada:
int main() {
std::jthread t1(hEntrada), t2(hEtiquetadora),
t3(hSalida), t4(hOperario);
}
Por cada junta de la instalación escribe dos frases: «el productor espera hueco antes de depositar y avisa elemento después» y «el consumidor espera elemento antes de retirar y avisa hueco después». Con esas dos frases y el inventario de recursos, el programa sale solo.