library(shiny)
library(ompr)
library(ompr.roi)
library(ROI.plugin.glpk)
library(ggplot2)
library(tidyverse)
# Definir UI
<- fluidPage(
ui
# Título de app
titlePanel("Planeación de Mantenimiento"),
# Opciones para el modelo
fluidPage(
sidebarPanel(
numericInput("m", "Flota", value = 2, min = 1, max = 5, step = 1),
numericInput("H", "Periodos", value = 8, min = 4, max = 12, step = 1),
numericInput("D", "Demanda", value = 480, min = 200, max = 1000, step = 20),
numericInput("Ui", "Periodos entre MTTOs", value = 3, min = 1, max = 7, step = 1),
numericInput("Ti", "Horas entre MTTOs", value = 90, min = 50, max = 200, step = 10),
numericInput("xmax", "Máximo horas operación", value = 744, min = 500, max = 1000, step = 66),
actionButton("solucionar", "Obtener solución!"),
br(),
actionButton("graficar", "Graficar solución!")
width = 3)
,
,
mainPanel(
h4('Descripción del modelo'),
#verbatimTextOutput("periodosMtto"),
#verbatimTextOutput("horasMtto"),
verbatimTextOutput("imprimeModelo"),
verbatimTextOutput("imprimeResultado"),
plotOutput("grafica")
)
)
)
# Definir lógica del servidor para resolver el modelo
<- function(input, output) {
server
# Usado para mostrar variables internas
$periodosMtto <- renderPrint({
outputrep(input$Ui, input$m)
})
$horasMtto <- renderPrint({
outputrep(input$Ti, input$m)
})
# Función para crear modelo planeación
<- reactive({
crearModelo
# Repetir valores ingresados por usuario para Ui y Ti
<- rep(input$Ui, input$m)
realUi <- rep(input$Ti, input$m)
realTi
# Definir el modelo con ompr
<- MIPModel() |>
model
# Variables
add_variable(y[i, t], i=1:input$m, t=1:input$H, type="binary") |>
add_variable(x[i, t], i=1:input$m, t=1:input$H, type="continuous", lb=0) |>
# Función objetivo
set_objective(sum_expr(y[i, t], i=1:input$m, t=1:input$H), "min") |>
# Restricciones
add_constraint(sum_expr(x[i, t], i=1:input$m, t=1:input$H) >= input$D) |>
add_constraint(sum_expr(y[i, j], j=tao:(tao+realUi[i])) >= 1, i=1:input$m, tao=1:(input$H-realUi[i])) |>
add_constraint(sum_expr(x[i, j], j=t:(t+u)) <= realTi[i] + realTi[i]*sum_expr(y[i, j], j=t:(t+u)), i=1:input$m, t=1:input$H, u=0:(input$H-t)) |>
add_constraint(x[i, t] + input$xmax*y[i, t] <= input$xmax, i=1:input$m, t=1:input$H)
# Devolver el modelo
model
})
# Función para entregar características del modelo
$imprimeModelo <- renderPrint(crearModelo())
output
# Ejecutar solución del modelo
<- eventReactive(
resultado $solucionar, {
input
# Activar solver
<- solve_model(crearModelo(), with_ROI(solver = "glpk", verbose = TRUE))
result
result
})
# Función para entregar características de la solución
$imprimeResultado <- renderPrint(resultado())
output
# función para crear gráficas de la solución
<- eventReactive(
plotear $graficar, {
input
# Graficar solución
::grid.arrange(
gridExtraget_solution(resultado(), y[i,t]) |>
mutate(Mtto = "Mant") |>
rename(unidad = i, periodo = t) |>
ggplot()+
geom_point(aes(periodo, value, color = as.factor(value)), size = 5)+
facet_grid(unidad~.)+
::theme_ipsum()+
hrbrthemeslabs(x = "PERIODO", y = "VALOR", col = "MTTO")+
theme(legend.position = "top"),
get_solution(resultado(), x[i,t]) |>
mutate(Horas = "Opera") |>
rename(unidad = i, periodo = t) |>
ggplot()+
geom_point(aes(periodo, value, color = as.factor(value)), size = 5)+
facet_grid(unidad~.)+
::theme_ipsum()+
hrbrthemeslabs(x = "PERIODO", y = "VALOR", col = "OPERA")+
theme(legend.position = "top"),
ncol = 2
)
})
$grafica <- renderPlot(plotear())
output
}
# Run the application
shinyApp(ui = ui, server = server)
Vamos a crear una app para nuestro modelo de optimización de mantenimiento que hemos definido aquí.
Crear un proyecto local
Este repositorio va a contener los archivos necesarios para nuestra app.
Entramos a nuestra sesión de RStudio:
File -> New Project
Con la siguiente ventana emergente:
Figura 1: Opciones para iniciar nuevo proyecto. Realmente voy a comenzar desde cero la aplicación, por lo que
New Directory -> Shiny Application
Figura 2: Configuración del proyecto. Create Project
Mi sesión de RStudio queda así:

.Rproj
: es un archivo de configuración para RStudio que contiene información sobre el proyecto, como el directorio de trabajo, los paquetes instalados, las opciones de visualización y los scripts de ejecución.app.R
: es el archivo principal de un proyecto Shiny que contiene la configuración, la interfaz de usuario y la lógica de negocio de la aplicación..gitignore
: es un archivo de configuración para Git que contiene una lista de patrones de archivos y directorios que se deben ignorar por Git.
Ten presente que en la esquina superior derecha nos indica el proyecto en el que estamos trabajando dev_shiny_mtto_app.Rproj
. En la parte inferior izquierda, en la pestaña de Git, tenemos los cambios que hemos hecho hasta que realicemos un commit
.
Clonar el repositorio desde local a Github.
Por ahora no voy a modificar el app.R
, solo quiero montar la infraestructura para ejecutar el workflow.
Revisamos qué tenemos en la Terminal dentro de RStudio:
Bash
~/Documents/shiny_mtto_app/dev_shiny_mtto_app$ git status
Output
En la rama master
No hay commits todavía
Archivos sin seguimiento:
(usa "git add <archivo>..." para incluirlo a lo que se será confirmado)
.gitignore
app.R
dev_shiny_mtto_app.Rproj
no hay nada agregado al commit pero hay archivos sin seguimiento presentes (usa "git add" para hacerles seguimiento)
Como aquí seleccionamos “Create a git repository”, ya hemos iniciado el proceso para subirlo a Github.
En este post creamos un repositorio en Github y luego lo clonamos a un repositorio local. Ahora vamos a realizar el proceso al revés, ya creado local vamos a clonarlo a Github.
Podemos incluir todos nuestros archivos con el comando:
Bash
~/Documents/shiny_mtto_app/dev_shiny_mtto_app$ git add .
Output
En la rama master
No hay commits todavía
Cambios a ser confirmados:
(usa "git rm --cached <archivo>..." para sacar del área de stage)
nuevo archivo: .gitignore
nuevo archivo: app.R
nuevo archivo: dev_shiny_mtto_app.Rproj
Ya están bajo seguimiento. Clic en Commit:

Insertamos nuestro comentario “first commit” (buenas prácticas) y obtenemos:

En la consola de RStudio ingresamos:
Console
::use_github() usethis
Output
"https" Git protocol.
ℹ Defaulting to
✔ Setting active project to"~/Documents/shiny_mtto_app/dev_shiny_mtto_app".
"cchiquitovalencia/dev_shiny_mtto_app".
✔ Creating GitHub repository "origin" to
✔ Setting remote "https://github.com/cchiquitovalencia/dev_shiny_mtto_app.git".
"master" branch to GitHub and setting "origin/master" as upstream branch.
✔ Pushing <https://github.com/cchiquitovalencia/dev_shiny_mtto_app>. ✔ Opening URL
Si en este punto has quedado perdido, puedes revisar este post para guiarte un poco. Entonces ya tenemos establecido en Github nuestro repositorio creado de manera local:
Desarrollo de Shiny app
Con nuestro modelo de optimización de mantenimiento establecido, vamos a programarlo en una Shiny App en R para poder resolverlo y obtener una solución óptima.
Recordemos que Shiny es un framework para crear aplicaciones web utilizando código R. Está diseñado principalmente con los científicos de datos en mente, y con ese fin, puede crear aplicaciones Shiny bastante complicadas sin conocimientos de HTML, CSS o JavaScript.
Shiny está diseñado para parecer casi mágicamente fácil cuando se está empezando, y sin embargo, cuanto más se profundiza en su funcionamiento, más se da cuenta de que está construido a partir de principios generales. te das cuenta de que está construido a partir de bloques generales que tienen fuertes principios de ingeniería de software detrás de ellos.
Una Shiny App se compone de un bloque ui
(interfaz del usuario) que define el aspecto de la aplicación, y un bloque server
, que define el funcionamiento de la aplicación. Shiny utiliza programación reactiva para actualizar automáticamente las salidas cuando cambian las entradas.
Manos a la obra: reemplazamos todo lo que contiene nuestro archivo app.R
con lo siguiente:
Hasta aquí, lo que hemos hecho es traducir a una app, que no es la más estilizada (espero no hayas pensado que soy experto en diseño gráfico o en experiencia de usuario) que habíamos modelado matemáticamente en un post anterior.
Shiny es muy amplio en cuanto al desarrollo de aplicaciones se refiere. Si quieres un poco más de contexto puedes revisar el libro Mastering Shiny, donde puedes encontrar la documentación para “llevarte de no saber nada sobre Shiny a ser un desarrollador experto capaz de escribir aplicaciones complejas de gran tamaño que sean mantenibles y tengan un buen rendimiento. Adquirirás un conocimiento profundo del modelo de programación reactiva que subyace en Shiny, además de construir una caja de herramientas de técnicas útiles para resolver los desafíos comunes de las aplicaciones”.
Básicamente establecemos en la ui
los parámetros que podrían cambiar, con algunos rangos que puede modificar (no pueden ser muy amplios porque el problema de optimización es de tipo NP-Hard). Creamos un resumen del tamaño del problema, imprimimos el resultado de la optimización que mostramos cuando el usuario usa un botón, y graficamos la solución con otro botón.
Realmente nuestra app es muy sencilla (aunque el problema que resuelve no lo sea). Es tan sencilla que podría tener detractores de la cultura DevOps en cualquier punto. Que sea a prueba de balas no es el punto aquí, sino enviar a producción.
Actualizamos nuestro repositorio en Github: commit
y push
. Tenemos nuestra app lista para clonar desde Github con nuestra instancia en AWS.
Cómo citar
@online{chiquito_valencia2025,
author = {Chiquito Valencia, Cristian},
title = {Crear {Shiny} {App}},
date = {2025-01-29},
url = {https://cchiquitovalencia.github.io/posts/2025-01-28-dev_mtto_app/},
langid = {en}
}