<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David De La Cruz Morales</title>
    <description>The latest articles on DEV Community by David De La Cruz Morales (@david_delacruzmorales_).</description>
    <link>https://dev.to/david_delacruzmorales_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2548068%2Ff6d56532-0f4e-41b6-b1e9-f45757eb0437.png</url>
      <title>DEV Community: David De La Cruz Morales</title>
      <link>https://dev.to/david_delacruzmorales_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/david_delacruzmorales_"/>
    <language>en</language>
    <item>
      <title>Reporte Técnico: Desarrollo de un Simulador de Estacionamiento Concurrente en Go</title>
      <dc:creator>David De La Cruz Morales</dc:creator>
      <pubDate>Mon, 09 Dec 2024 16:07:13 +0000</pubDate>
      <link>https://dev.to/david_delacruzmorales_/calculando-espacios-y-pintando-slots-en-fyne-resolviendo-un-problema-en-un-simulador-de-1296</link>
      <guid>https://dev.to/david_delacruzmorales_/calculando-espacios-y-pintando-slots-en-fyne-resolviendo-un-problema-en-un-simulador-de-1296</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introducción&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Este proyecto consiste en un simulador de estacionamiento concurrente desarrollado en &lt;strong&gt;Go&lt;/strong&gt;, utilizando la librería gráfica &lt;strong&gt;Fyne&lt;/strong&gt; para la interfaz de usuario. Su objetivo es modelar el comportamiento de un estacionamiento en tiempo real, gestionando la entrada y salida de vehículos de forma concurrente y mostrando visualmente el estado actualizado de los cajones de estacionamiento.&lt;br&gt;
El proyecto combina los conceptos de concurrencia, el patrón de diseño &lt;strong&gt;Observer&lt;/strong&gt; y la renderización dinámica en una interfaz gráfica. Este reporte detalla el uso de estas herramientas, los desafíos encontrados (particularmente con el patrón Observer y Fyne), y cómo se resolvieron, con el objetivo de brindar una referencia técnica para otros desarrolladores.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;1. Inicialización de Fyne&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Fyne es una librería moderna para desarrollar interfaces gráficas con Go. La inicialización básica sigue estos pasos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crear una nueva aplicación con &lt;code&gt;app.New().&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Configurar la ventana principal con &lt;code&gt;app.NewWindow().&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Diseñar el contenido utilizando contenedores y widgets de Fyne.&lt;/li&gt;
&lt;li&gt;Llamar a &lt;code&gt;ShowAndRun()&lt;/code&gt; para ejecutar la aplicación.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;En el simulador, se creó una ventana principal que integra la vista del estacionamiento y se conecta con el modelo de lógica concurrente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func main() {
    myApp := app.New()
    mainWindow := myApp.NewWindow("Simulador de Parking")

    estacionamiento := models.NewEstacionamiento(20)
    parkingView := views.NewParkingView()

    mainScene := scenes.NewMainScene(estacionamiento, parkingView)
    mainWindow.SetContent(parkingView.Container)

    mainWindow.ShowAndRun()
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Este flujo básico facilita la separación entre la lógica de negocio y la interfaz gráfica.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Uso del Patrón Observer&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Por qué usar el patrón Observer&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El patrón Observer se utilizó para mantener sincronizadas las capas del modelo y la vista. Cuando un vehículo entra o sale del estacionamiento, el modelo notifica a la vista, que actualiza los elementos gráficos correspondientes. Este patrón es ideal para sistemas donde múltiples componentes deben reaccionar ante un mismo evento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problemas encontrados al usar el patrón Observer en Go&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implementar el patrón Observer en Go puede ser desafiante, especialmente para quienes están acostumbrados a su implementación en lenguajes orientados a objetos como Java o C#. Un problema común al usar este patrón en Go es &lt;strong&gt;el manejo de concurrencia y bloqueos mutuos&lt;/strong&gt; al notificar a los observadores.&lt;/p&gt;

&lt;p&gt;Inicialmente, al iterar sobre los observadores registrados en el modelo (Estacionamiento) para notificar eventos, se producían &lt;strong&gt;condiciones de carrera&lt;/strong&gt; y bloqueos. Esto sucedía porque el método que registraba nuevos observadores no estaba protegido correctamente, lo que causaba accesos simultáneos a la lista de observadores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cómo se solucionó&lt;/strong&gt;&lt;br&gt;
Para resolver este problema, se utilizó un mutex &lt;code&gt;(sync.Mutex)&lt;/code&gt; para proteger el acceso concurrente a la lista de observadores. Además, se implementaron métodos seguros para registrar observadores y notificar eventos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (e *Estacionamiento) RegistrarObservador(o Observer) {
    e.mu.Lock()
    defer e.mu.Unlock()
    e.observadores = append(e.observadores, o)
}

func (e *Estacionamiento) NotificarVehiculoEntra(id, cajon, espaciosDisponibles, capacidad int) {
    e.mu.Lock()
    defer e.mu.Unlock()
    for _, o := range e.observadores {
        o.OnVehiculoEntra(id, cajon, espaciosDisponibles, capacidad)
    }
}

func (e *Estacionamiento) NotificarVehiculoSale(id, cajon, espaciosDisponibles, capacidad int) {
    e.mu.Lock()
    defer e.mu.Unlock()
    for _, o := range e.observadores {
        o.OnVehiculoSale(id, cajon, espaciosDisponibles, capacidad)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Implementación completa en el proyecto&lt;/strong&gt;&lt;br&gt;
El modelo Estacionamiento actúa como el sujeto observable, mientras que la escena principal (MainScene) y otros componentes, como la vista gráfica, son observadores:&lt;br&gt;
&lt;strong&gt;1. Definición de la interfaz Observer:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;package models

type Observer interface {
    OnVehiculoEntra(id, cajon, espaciosDisponibles, capacidad int)
    OnVehiculoSale(id, cajon, espaciosDisponibles, capacidad int)
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Notificación de eventos desde el modelo:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (e *Estacionamiento) VehiculoEntra(id int) {
    // Lógica para manejar la entrada del vehículo
    espaciosDisponibles := e.capacidad - e.ocupados
    e.NotificarVehiculoEntra(id, cajon, espaciosDisponibles, e.capacidad)
}

func (e *Estacionamiento) VehiculoSale(id int) {
    // Lógica para manejar la salida del vehículo
    espaciosDisponibles := e.capacidad - e.ocupados
    e.NotificarVehiculoSale(id, cajon, espaciosDisponibles, e.capacidad)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Respuesta de los observadores:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (s *MainScene) OnVehiculoEntra(id, cajon, espaciosDisponibles, capacidad int) {
    s.View.UpdateState(espaciosDisponibles, capacidad, id, cajon, "entra")
}

func (s *MainScene) OnVehiculoSale(id, cajon, espaciosDisponibles, capacidad int) {
    s.View.UpdateState(espaciosDisponibles, capacidad, id, cajon, "sale")
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta solución asegura que las actualizaciones sean consistentes y que las condiciones de carrera no afecten el rendimiento del sistema.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Problema Técnico: Renderizado y Cálculo de Posiciones
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Contexto&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El principal reto técnico fue calcular las posiciones de los cajones en la interfaz gráfica y actualizar su color en tiempo real. Los cajones debían:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Estar dispuestos en dos filas con un espaciado uniforme.&lt;/li&gt;
&lt;li&gt;Cambiar dinámicamente de color (rojo para ocupado, negro para disponible).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Problemas Identificados&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Cálculo dinámico de posiciones:&lt;/strong&gt; Los cajones de estacionamiento debían posicionarse en dos filas, con un espaciado uniforme entre ellos. Sin embargo, calcular y actualizar estas posiciones resultó complejo, ya que dependían de coordenadas precisas dentro de un contenedor sin diseño &lt;code&gt;(container.NewWithoutLayout())&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sincronización visual:&lt;/strong&gt; Al manejar múltiples hilos concurrentes, surgieron inconsistencias visuales al intentar actualizar los colores de los cajones en tiempo real. En ocasiones, los cambios no se reflejaban o causaban errores gráficos.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Cálculo de posiciones&lt;/strong&gt;&lt;br&gt;
Se emplearon coordenadas absolutas para definir la posición inicial y el espaciado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;xStart, yTop, yBottom := float32(185), float32(120), float32(200)
spotSpacing := float32(55)

// Fila superior
for i := 0; i &amp;lt; 10; i++ {
    parkingSpots = append(parkingSpots, fyne.Position{X: xStart + float32(i)*spotSpacing, Y: yTop})
}

// Fila inferior
for i := 0; i &amp;lt; 10; i++ {
    parkingSpots = append(parkingSpots, fyne.Position{X: xStart + float32(i)*spotSpacing, Y: yBottom})
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Renderizado dinámico&lt;/strong&gt;&lt;br&gt;
Se implementaron funciones para pintar los cajones según su estado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (v *ParkingView) DrawRedRectangle(x, y float32, slotID int) {
    if _, exists := v.slotRects[slotID]; exists {
        return
    }

    rect := canvas.NewRectangle(color.RGBA{R: 255, G: 0, B: 0, A: 255})
    rect.Resize(fyne.NewSize(30, 30))
    rect.Move(fyne.NewPos(x, y))

    v.Overlay.Add(rect)
    v.slotRects[slotID] = rect
    v.Overlay.Refresh()
}

func (v *ParkingView) RemoveRedRectangle(slotID int) {
    if rect, exists := v.slotRects[slotID]; exists {
        v.Overlay.Remove(rect)
        delete(v.slotRects, slotID)
        v.Overlay.Refresh()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sincronización Visual&lt;/strong&gt;&lt;br&gt;
Para garantizar que los cambios visuales fueran consistentes con el estado del sistema, se actualizó el texto de la etiqueta principal y el estado de los cajones dentro de una función central:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func (v *ParkingView) UpdateState(espaciosDisponibles, capacidad int, id, cajon int, accion string) {
    v.Label.SetText(fmt.Sprintf("Espacios disponibles: %d/%d", espaciosDisponibles, capacidad))

    if accion == "entra" {
        v.DrawRedRectangle(v.parkingSpots[cajon-1].X, v.parkingSpots[cajon-1].Y, cajon)
    } else if accion == "sale" {
        v.RemoveRedRectangle(cajon)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto asegura una representación gráfica precisa y actualizada en todo momento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusión&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Este proyecto no solo logró su objetivo de simular un estacionamiento concurrente, sino que también enfrenta problemas prácticos de desarrollo, como el uso del patrón Observer y la creación de interfaces gráficas con Fyne. Los problemas encontrados y las soluciones implementadas buscan servir como una guía para otros desarrolladores que comienzan con Go o que enfrentan retos similares.&lt;br&gt;
La implementación del patrón Observer en Go, en particular, demuestra cómo manejar la concurrencia de manera segura y eficiente. Este reporte, al documentar estos problemas y soluciones, tiene como objetivo aportar a la comunidad de programadores interesados en aprender y aplicar estas herramientas, facilitando su proceso de aprendizaje y desarrollo.&lt;br&gt;
Si tuviste alguna duda sobre la implementación y la solución de este puedes consultar mi repositorio de github: &lt;a href="https://github.com/daviddlcm/simulador-parking.git" rel="noopener noreferrer"&gt;simulador-parking.git&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>programming</category>
      <category>go</category>
      <category>fyne</category>
    </item>
  </channel>
</rss>
