================= 2 clase Base =====================La clase base es una clase que implementa un timer y una colección y se le han añadido algunos métodos y eventos.
Para ser honestos, sería mejor convertir esta clase en una interfaz, por razones de rendimiento. Sin embargo eso me supone escribir más código... por lo tanto queda como ejercicio para el interesado, (si desea reconvertirlo9.
Esta clase es una clase cuya finalidad es ser instanciada, de hecho 3 clases que se expondrán en otros apartados son implementaciones de esta clase con los añadidos pertinentes (razón de más para que esta fuera una interfaz).
He preferido exponer esta clase antes que el formulario principal, por 2 razones, la 1ª es que parte de la funcionalidad de aquel no se entendería sin antes exponer esta clase (y otra) y la 2ª es que el formulario tiene más cuenta exponerlo como archivo adjunto para no tener que detallar las propiedades, los controles usados, etc...), aunque el código igualmente se expondrá para ser explicado.
Sin más, expongo el código y paso a las explicaciones (antes de entrar en las explicaciones pienso enviar el mensaje y luego reeditarlo, por lo que si lees este mensaje y debajo del código no ves explicaciones deberás esperar a que termine de editarlo.)
' Esta clase es una CÁPSULA que engloba una colección y un timer
' la colección admite la posibilidad de limitar cantidad de elementos.
' el timer se reparte en 2 eventos, y se ha añadido una función de consulta...
' Toda la funcionalidad es común a las 3 clases, es por ello que se unifica en una sola clase
' y las instancias exponen sólo las diferencias específicas de cada una.
Private WithEvents s_Crono As Timer
Private s_Coleccion As Collection
Private p_TamañoCola As Long ' determina si hay un límite máximo en cola de entrada, y si lo hay lo fija en un tamaño.
Private p_LimitarCola As Boolean
Private p_PausadoEntrantes As Boolean
Public Condicion As Boolean ' condicion para servir elemento
' ambos eventos los provee el timer...
' sólo salta el evento si la colección tiene eventos y no se cumple la condición
Public Event Crono()
' sólo salta el evento si la colección tiene elementos y se cumple la condición.
' la condición debemos activarla cuando queramos que nos devuelva el primer elemento.
Public Event ServirElemento(ByRef Elemento As Object)
Private Sub Class_Initialize()
p_LimiteCola = 0 ' indica que no hay límite de transacciones de entrada.
p_LimitarColaSolicitud = False
Set s_Coleccion = New Collection
p_PausadoEntrantes = False
Condicion = False
End Sub
' debe invocarse esta función justo tras ser creada la clase.
Friend Function SetTimer(ByRef Tim As Timer) As Boolean
On Local Error Resume Next
Set s_Crono = T
s_Crono.Interval = 1000
s_Crono.Enabled = False
SetTimer = (s_Crono Is Nothing)
End Function
Private Sub Class_Terminate()
If Not (s_Crono Is Nothing) Then
s_Crono.Enabled = False
Set s_Crono = Nothing
End If
Set s_Coleccion = Nothing
End Sub
' PRECAUCIÓN: sólo debería usarse en la instancia de solicitudes.
' o incluso en la instanacia llamada de ejecución, pero nunca en la siguiente.
' se debe recordar que funcionan en cascada...
Public Property Get PausaEntrantes() As Boolean
PausaEntrantes = p_PausadoEntrantes
End Property
Public Property Let PausaEntrantes(ByVal p As Boolean)
If p_PausadoEntrantes <> p Then
p_PausadoEntrantes = p
End If
End Property
' pausa, pausa tanto entrantes como salientes....
Public Property Get Pausa() As Boolean
Pausa = Not s_Crono.Enabled
End Property
Public Property Let Pausa(ByVal p As Boolean)
If p_Pausado <> p Then
p_PausadoEntrantes = p
s_Crono.Enabled = Not p
End If
End Property
Friend Property Get Intervalo() As Integer
Intervalo = s_Crono.Interval
End Property
Friend Property Let Intervalo(ByVal x As Integer)
s_Crono.Interval = x
End Property
' el timer provee 2 eventos, ambos sólo si la colección tiene elementos.
' Se sirve el primer elemento siempre que se cumpla la condición.
' la condición es un valor buleano que asigna la instancia de esta clase.
' es allí donde se decide cuales son las justificaciones de la condición.
' puesto que cada instancia tendrá condiciones distintas, lo único común es
' precisamente, definir que se 'cumplió la condición' ... de este modo es común a todas las instancias.
' cuando no se cumple la condición se genera el evento crono, que ofrece la oportunidad de evaluar si se cumple o no la condición.
' es allí donde debe establecerse si se cumple o no la condición.
' Dado que se ignora cuando se incumple la condición para cada instancia
' debe decidirse en cualquiera de los 2 eventos el caso que no, o podría activarse la línea comentada
' en cuyo caso sólo se puede decidir si se cumple la condición en el evento crono... dividiendo la frecuencia de entregas...
Private Sub s_Crono_Timer()
If s_Coleccion.Count > 0 Then
If Condicion = True Then
Dim it As Object
Condicion = False
Set it = s_Coleccion.Item(1)
RaiseEvent ServirElemento(it)
' se elimina desde la clase que lo añade a otra colección, para hacerlo compatible con
' la nueva propiedad añadida a última hora: PausarEntrantes
'Call s_Coleccion.Remove(1)
Set it = Nothing
Else
RaiseEvent Crono
End If
End If
End Sub
' es una propiedad de administrador
' si se establece el valor a true, no se podrán añadir elementos mientras
' el nº de elementos en la colección sea mayor= que el tamaño de cola.
Public Property Get LimitarCola() As Boolean
LimitarCola = p_LimitarCola
End Property
Public Property Let LimitarCola(ByVal lc As Boolean)
If lc <> p_LimitarCola Then
p_LimitarCola = lc
End If
End Property
' es una propiedad de administrador
' esta propiedad sólo indica en tamaño de cola, pero quien determina si se limita o no, es Limitarcola
Public Property Get TamañoCola() As Long
TamañoCola = p_TamañoCola
End Property
Public Property Let TamañoCola(ByVal lm As Long)
If lm > p_TamañoCola Then
p_TamañoCola = lm
End If
End Property
Public Property Get Count() As Long
Count = s_Coleccion.Count
End Property
' lo hemos puesto variant poque igual puede pedirse por índice que por key
Public Property Get Elemento(ByVal Index As Variant)
On Local Error Resume Next
Set Elemento = s_Coleccion.Item(Index)
End Property
Friend Property Set Elemento(ByVal Index As Variant, ByRef it As Object)
On Local Error Resume Next
Set s_Coleccion.Item(Index) = it
End Property
' ni añadir ni eliminar se dejan públicas, ya que sólo una clase añade a otra.
Friend Function Añadir(ByRef T As Object, ByVal Key As String, Optional Despues As Boolean = False, Optional DetrasDe As Long = 1) As enAñadiendo
If p_PausadoEntrantes = False Then
If p_LimitarCola = True Then
If s_Coleccion.Count >= p_TamañoCola Then Exit Function
End If
On Local Error Resume Next
Err.Number = 0
If Despues = False Then
Call s_Coleccion.Add(T, Key)
Else ' esta versión sólo la utiliza la clase de solicitudes...
Call s_Coleccion.Add(T, Key, after:=DetrasDe)
End If
If (Err.Number = 0) Then
Añadir = ADD_AÑADIDO
Else
Añadir = ADD_ERROR_COLECCION ' se supone que no puede ser: ADD_ERROR_DATOS, se hubiera detectado en la llamada Add_Transaccion de 1ª fase...(cSolicitudes)
End If
Else
Añadir = ADD_PAUSADO
End If
End Function
' lo normal es eliminar siempre el elemento 1º, las s_Colecciones están basadas en ínndice 1
' no usamos esta función en la práctiva, se provee para futuras necesidades...
Friend Function Eliminar(Optional Posicion As Long = 1) As Boolean
On Local Error Resume Next
If Posicion > 0 Then
Err.Number = 0
s_Coleccion.Remove (Posicion)
Eliminar = (Err.Number = 0)
End If
End Function
Public Function Consulta_MisPedidosEnCola(ByRef IdRef As Form) As Long
Dim n As Long, k As Long
Dim tr As infoTransaccion
On Local Error Resume Next
For k = 1 To s_Coleccion.Count
Set tr = s_Coleccion.Item(k)
If tr.Control.IdRef.Name = IdRef.Name Then
n = n + 1
End If
Next
Set tr = Nothing
Consulta_MisPedidosEnCola = n
End Function
Esta clase la hemos llamado cBase.
Como se dijo al principio, el objetivo de esta clase es encapsular funcionalidad que vamos a reutilizar en diferentes clases. La funcionalidad se detalla a continuación:
Se provee servicio para un timer, puede modificarse su propiedad enabled e interval, enabled es rellamada como pausa (y su valor es not enabled ara que guarde sentido), interval se renombra a intervalo.
Puesto que el timer se relaciona con un objeto collection, se provee una propiedad que media entre pausa del timer y el acceso a la colección que se llama PausaEntrantes .
Cuando la clase se inicializa, se crea una nueva instancia de un objeto collection sobre la variable Coleccion. La colección expone los métodos típicos de la colección Add=Añadir, Remove=eliminar, Count=Count, Item=elemento, pero además se le dota de 2 propiedades más: LimitarCola y TamañoCola así mismo se le añade una función de consulta para búsqueda: Consulta_MisPedidosEnCola
Como decía al inicializar la clase se crea una nueva instancia de colección, y se establecen valores iniciales por defecto a las propiedades.
Tras ser inicializada la clase, necesita una 2ª inicialización (vb6 no permite parámetros al crear clases), esta 2ª inicialización se lleva a cabo sobre la función SetTimer. en dicha función se envía un timer, porque como todo el mundo debe saber las clases no pueden crear objetos (una clase no es un objeto, estrictamente hablando (en VB&) aunque para nuestro efectos es como si lo fuera pues se comporta casi como tal). Sin embargo que no se puedan crear objetos no implica que no pueda albergar y manipular objetos (de interfaz fráfica, por ejemplo un Command, Option, Picturebox... o un Timer).
Esta 2ª inicialización establece valores iniciales al timer que son distntos de los que quedan establecidos en un timer cuando se crea,
esto debe tenerse en cuenta para cambiar más adelante estos valores a los deseados. Si la función no recibió un timer devolverá falso, como esto no es deseable este resultado se utilizará para saber si el servidor estará dispnible o no, es más, si devuelve false, el servidor no se 'encenderá'.
El detalle de como recibe el timer, se explica en otro apartado.
Los métodos de la colección han sido rescritos para acoplar la actuación del timer (entre otras cosas), por ejemplo: si la propiedad PausaEntrantes, se establece a True, la función añadir queda 'bloqueada'. Por ejemplo la función Eliminar por defecto elimina siempre el primer elemento de la colección. Count, por ejemplo no se rescribe, pero como podemos desear tener acceso a dicha propiedad de la colección, debemos exponerla. Naturalmente exponer nuestro métodos rescritos (o no) supone que la colección la hemos designado privada, para que no se puedan invocar directamente los métodos Add, Remove, etc... sin pasar previamente por nuestra funcionalidad...
La función añadir, por defecto añadirá elementos al final de la colección (es lo más frecuente), pero como hemos establecido prioridades en las peticiones de los clientes, existe la probabilidad de que se añadan en otra posición, la instancia (de esta clase) que recibe las solicitudes se encarga de gestionar la prioridad, y es desde allí que se señala el punto de inserción en la colección. el resto de instancias de esta clase siempre añaden al final de la clección.
El método eliminar, en teoría siempre debería eliminar el primer elemento en la colección, pero se ha establecido la posibilidad de que pueda, eliminarse un elemento en cualquier posición dentro de la colección, para atender posibles futuros cambios, en el proyecto actual no se utiliza.
La propiedad PausaEntrantes, permite al administrador pausar el servicio, (lo que impide que los clientes puedan añadir mientras eestá en pausa añadir solicitudes) para cualquier tarea que necesite realizar. Este modo de pausa, sin embargo no detiene el trabajo del servidor, pués sigue gestionando las transacciones almacenadas. También si se dessea puede programarse para que en determinadas situaciones se produzca dicha pausa desde código... Esta propiedad, debe usarse con cuidado, pués el servicio lo que hace es impedir añadir solicitudes en la fase pausada en este modo, desde la fase anterior... en cada instancia, respectivamente se comenta en detalle la implicación de usar esta propiedad.
La propiedad Pausa,si se establece a true (timer.enabled=false) realmente detiene la instancia, es decir no sirve solicitudes a la siguiente fase, ya que de eso se encarga el timer, que está desactivado. Esta propiedad tiene repercusiones parecidas a la anterior, y el cambio de esta comporta obligadamente el cambio en la anterior. La idea es pausar tanto los añadidos como los envíos a la siguiente fase. Esto puede tener su importancia por ejemplo para consultar el estado real de las transacciones, con fines a verificar el comportamiento, realizar estadísticas,etc... por ejemplo cada 10 minutos podrían pausarse todas, y elaborar un informe de cuantas transacciones tiene cada colección, los valores de sus intervalos,etc.. y luego restablecer el servicio. Esto apenas duraría menos de 1 segundo, y sería util para estudiar y afinar el comportamiento del servidor.... Naturalmente puede utilizarse para razones administrativas diversas, estoes sólo una propuesta de ejemplo.
La propiedad Intervalo, es importante ya que cada instancia deberá tener su valor específico y será más o menos difícil encontrar un balance entre todos que sea el más óptimo, es posible que determinadas peticiones requieran un ajuste específico de cada uno de los tmers. En un caso real debería establecerse casos particulares con valores de configuración específicos, ya probados y demostrados como más efectivos para tipos determinados de transacciones. Por ejemplo, supongamos que el servidor se enciende a las 8 de la mañana y se apaga a las 6 de la tarde (para los clientes, pero hay un cliente administrador que al término del día realiza unas labores determinadas), es posible que lo primero que hagan los clientes sea casi exclusivamente solicitar datos, a medida que pasen las horas es probable que las peticiones estén repartidas entre solicitar datos y guardar datos, y posiblemente hacia el mediodía decaigan tanto unas como otras, en ese momento podría aprovecharse para hacerse gestiones internas de mantenimiento, y en definitiva cada tipo de operaciones tiene un comportamiento que podría optimizarse fijando configuraciones específicas demostradas eficaces...
El timer con su evento, provee 2 eventos a las instancias, que se sirva un evento u otro depende de que se cumpla una condición. Puesto que la condición lóogicamente cambiará en cada instancia, el modo de tiodas las instancias tengan una condición común es que esta tenga un valor reconocido por todas, y cuyo valor lo especifique la propia instancia. Así si no se cumple la condición se devuelve el evento crono (que es el equivalente al propio 'elapsed' _Timer, del Timer), durante ese evento se evalúa en la instancia si debe cambiar la condición a True o no. Si la condición se estableció a True desde la instancia entonces se genera en el siguiente ciclo el evento servirElemento, que proporciona un parámetro: el primer elemento de la colección. El consumidor del evento (la instancia) en el tratamiento de ese evento tratará de añadir dicho elemento a la siguiente fase... el resto de los detalles es específico de dichas clases que se explicarán en su debido momento.
Hay que recalcar un detalle, al proporcionar los eventos de esta manera, sucede que la frecuencia de trabajo del timer la estamos diviendo entre '2', ya que en un ciclo evalúa si la condición es false y se establece entonces la condición a true, necesita como mínimo otro ciclo para devolver el elemento desde la condición true... es decir esto influye en el intérvalo real en que se servirán los elementos. Hay 2 alternativas, la más obvia es reducir el intérvaloa la mitad, el otro (que debe estudiarse si interesa o no) es modificar el código como se muestra en este ejemplo alternativo:
Private Sub s_Crono_Timer()
If s_Coleccion.Count > 0 Then
If Condicion = True Then
Dim iT As Object
Condicion = False
Set iT = s_Coleccion.Item(1)
RaiseEvent ServirElemento(iT)
Set iT = Nothing
End If
RaiseEvent Crono
End If
End Sub
Como se ve, en este modo alternativo, tras enviar un elemento por una condición True, acto seguido se envía también el ecento crono para restablecer la condición si fuera necesario, no hay por tanto pérdidas de ciclo. Cada cual debería establecer cual método le interesa más y cambiarlo si procede. Si la clase cBase (esta) fuera una interfaz, cada implementación (las instancias actuales de esta clase) podría optar concretamente por la alternativa específica más eficaz al caso.
La propiedad TamañoCola determina el tamaño que puede asignarse a la colección ( la colecciones están basadas en valores long, como límite de sus elementos). Esta propiedad puede usarse como control de desbordamiento simplemente o como se desee. Por defecto el valor se establece a 0 es decir si no se cambia y se alcanzara el límite de 2^31 -1, ocurriría un error y no se podría añadir a la colección. Por último, decir que esta propiedad sólo establece el tamaño de elementos que admite la colección, pero el establecimiento de bloquear añadir elementos tras alcanzar (o si se superó) el n´mero de elemntos en la colección es la siguiente porpiedad...
La propiedad LimitarCola, determina si se actúa en consecuiencia con un tamaño límite de cola. Debe notarse que si la colección tiene un número de elementos y se establece un valor más bajo y luego está propiedad (limitarcola se establece a true), no se eliminan elementos de la colección, (nunca), sólo impide que se añadan más. Aún podría crearse una propiedad que se llamara AutoLimitarCola, de tal modo que tras alcanzar (o si se habia superado previamente) el tamaño de la cola, se desactivara y con ayuda de otra propiedad de ResetLimitarCola que específica un tamaño de cola menor que Tamaño cola volver a añadir elementos. Un ejemplo: si Count = 128 elementos y el TamañoCola lo establecemos a 100 y ResetLimitarCola =60, significa que al establecer la propiedad AutoLimitarCola, no dejará añadir elementos hasta que count=60 y se volvería a bloquear cuando Count=100. Desarrollar estas propiedades y adaptarlo queda como ejercicio para el interesado...
La función Consulta_MisPedidosEnCola, permite reconocer cuantos elementos de un cliente existen actualmente en esa colección. Esta función es síncrona por lo que si la colecciones alcanzan gran volumen de elementos, podría significar un retraso, por tanto debe usarse con precaución y lo menos posible. Podría y debería crearse una función en la clase cTransa que determine el esado de una transacción de un cliente, bsándose en que colección se encuentra y que orden ocupa en dicha colección, si se proporciona un medio que calcule el promedio de tiempo en servir una transacción se podría valorar un tiempoestimado en resolver la transacción por la que un cliente solicita... para no complicar el código, se han evitado funciones interesantes y útiles pero que son ajenas a resolver el problema planteado.
Lo que respecta a esta clase ya se ha explicado.
Nota: no se descarta que hubiera algún fallo o alguna imprecisión en las explicaciones pués en este momento la clase aún no ha sido probada y es posible que se me haya escapado algún gazapo....
Por hoy vale.
p.d.: debe notarse que la mitad del código lo componen líneas en blanco y comentarios...