• Domingo 22 de Diciembre de 2024, 20:48

Autor Tema:  Soporar usuarios recurrentes en un Sistema VB6  (Leído 2649 veces)

lagunax

  • Nuevo Miembro
  • *
  • Mensajes: 14
    • Ver Perfil
Soporar usuarios recurrentes en un Sistema VB6
« en: Miércoles 18 de Marzo de 2009, 23:54 »
0
Hola a todos nuevamente, ojala alguien pueda iluminarme un poco esta duda que tengo.

Acabo de montar a un servidor un sistema de altas-bajas-cambios-consultas en VB6 con una BD access 2003. El problema es que me piden que este sistema sea capaz de soportar usuarios recurentes (varios usuarios utilizandolo a la vez) pero realmente no tengo ni idea de que tipo de procesos se deben implementar para que exista un soporte de este tipo. Estoy completamente en blanco y sin pistas de donde poder investigar esto.

¿Alguien podría ayudarme a conocer más acerca de este tema, porfavor?

 :hitcomp:

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
Re: Soporar usuarios recurrentes en un Sistema VB6
« Respuesta #1 en: Jueves 19 de Marzo de 2009, 15:44 »
0
Básicamente tu programa no tiene que hacer nada excepto detectar cuando la base de datos está inoperante, por exceso de tráfico, para informar al usuario y en todo caso guardar las transacciones localmente si la importancia de guardar los datos es muy elevada.

Casi toda la responsabilidad de esa tarea recáe en la base de datos. Nunca he usado Access2003 por tanto no puedo referirte como se comportará con accesos recurrentes, si te diera problemas trata de trasladarlo a SQLServer. La cuestión es la base de datos la has diseñado tú ?  o tú sólo has creado las funciones de consulta y actualización de la BD ?. Si es lo 2º el administrador de la BD es quien debería proveer las transacciones recurrentes. Si todo depende de ti, no te queda más remedio que empollarte la utilización de Access2003, para poder establecer la funcionalidad adecuadamente.

Se supone de entrada que hay un servidor que porta la base de datos y se supone que hay varios terminales que tienen una instancia (copia) del programa que has realizado.  si esto es así en teoría no debería haber problemas, si los hay hay que acceder a la parte de administración de la base de datos y hacer lo cambios pertienentes, en SQLServer sería tocar la BD Master para configurar algunos parámetros (de permisos de usuarios más que nada) y quizás crear algunos trigger...

En el supuesto y raro caso de que el acceso a la BD fuere desde un único terminal y se hicieran varias transacciones a la BD desde dicha instancia ver nota para que las llamadas fueran concurrentes estas deberían tratarse como multihilo. Lo cual puede programarse con cierta facilidad usando 2 timers que procesan las transacciones. Un timer está a la escucha de transacciones entrantes, si detecta una lo añade a una coleccion de transacciones, el otro timer procesa la transacciones de la colección si detecta que la colección contiene transacciones toma la 1ª dentro de la colección y la procesa, al final de dicha transacción dedica un evento para notificar el resultado de la transacción y acto seguido elimina dicha transacción de la colección, en un pequeño intervalo volverá a consultar si hay transacciones pendientes...

El manejo de la colección podría y debería incorporar capacidad de prioridades, cada funcionalidad que se realice en una base de datos debería tener una prioridad, cuando se realice una petición de una transacción se consulta su prioridad, para añadirla en la colección de transacciones debe hacerse en la posición que le corresponda. esta posición se determina recorriendo la colección de atrás adelante, y tomando la posición la que se alcance cuando coincida una transacción del mismo nivel de prioridad (va detrás de esa) . Un par te punteros que describan: Transaccion_Espera_prioridad_Minima y Transaccion_Espera_prioridad_Maxima registran cual es la prioridad mínima y máxima actualmente en la colección, entonces añadir una nueva transacción puede colocarse directamente al inicio o al final sin recorrer toda la colección comparando dichos valores, al eliminar una transación de la colección habría que actualizar dichos valores, pero seguiría siendo rentable si la cola de transacciones llegara a ser muy alta, para una concuerrencia de unas pocas decenas es probable que no se note eficiencia. Opcionalmente la prioridad podría ser horaria, es decir última en entrar última en procesarse, por tanto en este caso siempre se añadirían al final y se eliminarían del principio....

Básicamente el mecanismo de recurrencia está descrito en los 2 párrafos anteriores, pero como te digo las bases de datos hoy por hoy incorporan dichos mecanismos, sólo deben hacerse las configuraciones precisas (y especialmente los permisos de acceso de los usuarios)... por ejemplo si se determina una cola máxima de transacciones en la coleccción en espera de procesar, cualquier petición de añadir una nueva transacción en espera será respondida como cola llena "guarde localmente su propia cola de transacciones en espera y vuelva a solicitar dentro del intervalo x"... El cliente al recibir dicha notificación de vuelta si la transacción no procede de otra instancia de este mecanismo lo guarda en una instancia de este mecanismo de modo local y actualiza su timer a un intérvalo sugerido de x, cuando la cola de transacciones esté abierta quizás el intervalo de espera debe cambiarse a uno más fluído, esto se debe enviar como el evento de respuesta de la transacción...

p.d.: si necesitas un ejemplo sencillo para crear y administrar un sistema de cola de transacciones podría escribir un pequeño código ilustrando los pasos sencillos, pero deberías ser paciente ya que tampoco tengo en estos momentos todo el tiempo del mundo para atender en el foro como me gustaría... pero bueno si cabe la posibilidad de que la descripción de tu problema no fuere exactamente como describes y necesitas un mecanismo de este tipo, lo indicas y con paciencia le dedicaría algún ratillo...

nota: (es decir habría una aplicación secundaria por cada operario que envía sus datos a este programa y este los postprocesa para manejarse con la BD... no sería lo más óptimo pero en configuraciones donde sólo un terminal es potente y los demás son poco potentes o viejos quizás sea la solución provisional más aceptable)
«Ma non troppo»
----> ModoVacaciones = False<----

lagunax

  • Nuevo Miembro
  • *
  • Mensajes: 14
    • Ver Perfil
Re: Soporar usuarios recurrentes en un Sistema VB6
« Respuesta #2 en: Jueves 19 de Marzo de 2009, 16:33 »
0
Gracias por tu respuesta tan rápida Nebire.

De hecho el sistema es de lo más sencillo, utilizo setencias ADO para accesar a la BD y en algunos casos lo hago manualmente (sin ADO, definiendo las variables).

La BD en Access 2003 la diseñe de la manera más sencilla, como tampoco he utilizado access tan a fondo, más que nada como un repositorio de datos, únicamente cree los campos de la misma con sus respectivos tipos de datos. Del acceso y modifcicación de la BD esta a cargo el código en Basic.

Si pudieras ayudarme con ese codigo de crear y administrar un sistema de cola de transacciones te estaré sumamente agradecido, claro que puedo esperar, no hay problema con la espera. Como realmente es un diseño muy sencillo tanto del programa de basic como de la BD espero que no te resulte complicado.

De nuevo gracias por tu ayuda, estaré al pendiente de tu respuesta.

 :good:

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
Re: Soporar usuarios recurrentes en un Sistema VB6
« Respuesta #3 en: Jueves 19 de Marzo de 2009, 17:54 »
0
Bien, aquí en mi localidad nos tomamos 'puente' hasta el lunes, por tanto tengo un tiempo disponible...
si no te importa te lo mostraré en 3 o 4 pqueños proyectos de modo que a cada paso el proyecto es lo anterior más algunos cambios o añadidos. De ese modo el seguimiento del mismo te será creo que más fácil entenderlo... Si lo coloco todo terminado es probable que te cueste segurilo y entender la relevancia de cada cosa.

Descripción del primer proyecto:
Así pués el primer proyecto será el flujo entre las partes implicadas... para simplificar el caso y no depender de posibles errores y /o malas instalaciones, configuraciones, en ejemplos nunca suelo trabajar con bases de datos sino con estructuras de modo que se separa claramente lo que es entender el problema planteado de los posibles problemas de los defectos o carencias ajenas al puro entendimiento del problema.

En este proyecto proveeremos una clase que trabaje con las colecciones y provea los eventos, es quien despacha las transacciones. Y proveeremos un formulario que actúa del consumidor de esta clase. en el siguiente proyecto se crearían instancias de este formulario para simular diferentes usuarios...

... queda a la espera ...
«Ma non troppo»
----> ModoVacaciones = False<----

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
Re: Soporar usuarios recurrentes en un Sistema VB6
« Respuesta #4 en: Sábado 21 de Marzo de 2009, 20:10 »
0
Hola Lagunax, te comento...

El programa de ejmplo ya está bastante avanzado, no obstante mañana tengo el día bastante ocupado.
Así, entre el lunes y el martes espero encontrar otro momento para finalizarlo... probarlo (por si se me coló algún error de bulto9 y subirlo.
«Ma non troppo»
----> ModoVacaciones = False<----

lagunax

  • Nuevo Miembro
  • *
  • Mensajes: 14
    • Ver Perfil
Re: Soporar usuarios recurrentes en un Sistema VB6
« Respuesta #5 en: Lunes 23 de Marzo de 2009, 16:37 »
0
No hay problema Nebire, estas ayudandome bastante con esto, así que puedo esperar el tiempo necesario.

Una vez más gracias por todo tu apoyo.

 :beer:

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
1 Presentación y módulo de arranque
« Respuesta #6 en: Jueves 26 de Marzo de 2009, 17:08 »
0
Hola Lagunax...
He decidido cambiar de estrategia, exclusivamente por razones de tiempo, por tanto en vez de exponérte el proyecto en diferentes fases te lo expondré por partes. El caso es equivalente y el fin el mismo que lo entiendas... la ventaja de la idea anterior es que verías como sufre la transformación en cada fase, pero ello me supone dedicarle más tiempo del que quisiera.

Mientras termino (no encuentro tiempo contínuo, para darle el empuje final), iré exponiendo las partes terminadas para que vayas dando bocados...
El código de archivo se expondrá siempre en un mensaje nuevo, aunque luego en el mismo se refiera a otros.


================ 1 Presentación y módulo de arranque ======================

El proyecto se compone de 5 clases, 3 formularios y 2 módulos. Por tanto digamos que se compone de 10 partes aunque se desarrolle en más o menos mensajes.

Para empezar qué mejor que el código del módulo de arranque. Este es muy breve, por tanto luego
El proyecto arranca desde una rutina MAIN, por tanto crea una aplicación nueva para albergar el proyecto, no hagas cambios a éste proyecto, usa una copia para hacer los cambios que desees en áquel, este lo dejas como referencia.

Este código es muy breve, aparte de la sub main, lleva una enumeración y un par de variables. Los detalles explicativos debajo del código.
Código: Visual Basic
  1.  
  2. '  para investigar la razón de cierre y permitir o denegar si hay transacciones pendientes.
  3. Public Enum enCierrePrograma
  4.     CIERRE_NO_DEFINIDO = -1
  5.     CIERRE_USUARIO_USA_BOTONES = 0
  6.     CIERRE_CODIGO_UNLOAD_FORM = 1
  7.     CIERRE_FIN_SESION_WINDOWS = 2
  8.     CIERRE_ADMINISTRADOR_TAREAS = 3
  9.     CIERRE_FORM_FORZADO_FORMMDI = 4
  10.     CIERRE_FORM_FORZADO_CIERRE_PADRE = 5
  11. End Enum
  12.  
  13.  
  14. Public RazonCierre As enCierrePrograma
  15. Public Transa As cTransa
  16.  
  17. Public Sub main()
  18.     frmPrincipal.Show (1)
  19.     ' cuando se cierre el form cliente finaliza la aplicación.
  20.    Set Transa = Nothing
  21.     End
  22. End Sub
  23.  
  24.  

La enumeración 'enCierrePrograma' y la variable 'RazonCierre'  operan en conjunto con las rutinas queryUnload de los formularios. La idea es determinar si hay transacciones pendientes y permitir o no el cierre, para evitar usos indebidos (se expondrá) en su momento un cierre real con transacciones pendientes, se podrá o no ocultar el formulario cliente, de modo que los usuarios 'patosos' no insistan y produzcan una pérdida de datos. Los detalles de esta enumeración se harán cuando se explique el formulario cliente.

La variable cTransa es el sustento del servidor. Cuando se ejecuta main, se abre un formulario es el formulario del administrador del servidor. Este es bastante simple, tiene 2 botones para iniciar y detener el servidor y opciones de comportamiento del servidor.

Cuando se abre el formulario principal se crea una instancia de la clase cTransa. Transa es esa instancia, Transa engloba toda la funcionalidad del servicio, el control de la misma por parte del administrador se realiza a través de ese formulario principal. El

Con el objeto de probar y simular los clientes, se crean desde el formulario del servidor instancias del cliente, una matriz (para el caso). En el caso real deberás modificar este aspecto para que un cliente remoto encienda el servidor si está apagado. Esto sería tan sencillo como añadir un procesado de command$ en este módulo de arranque. Este módulo lo he dejado muy breve porque será quien deba absorver la mayor parte de los cambios que realices a la aplicación. Siendo breve te será fácil modificarlo sin que el cóodigo existente suponga un obstáculo. de hecho incluso la enumeración podrías moverla a otro módulo que se expondrá más adelante que simplmenete recoge los tipos, con lo que sólo te quedarían 1 decena de líneas de código.

El presente proyecto se ha concebido como  un servidor con múltiples clientes 'insitu', es decir no son remotos. La razón es que, lo que se trata de exponer es la interoperatividad asíncrona de los clientes con el servidor (que es lo que pedías), ...y no la conexión.
El presente proyecto se ha concebido con uso de datos definidos por el usuario (estructuras) frente al uso de base de datos. La razón es la misma que la anterior, facilitar la explicación del funcionamiento sin que interfiera los problemas derivados de la inadecuada conexión, errores de instalación etc... de la base de datos. (es un flaco favor explicar el funcionamiento de un motor de 4 tiempos, con un motor real de modo que al tratar de aflojar los tornillos para abrir el motor, estos no ceden y el tiempo se malgasta en encontrar las herramientas para solucionar un problema ajeno al problema inicial, que era pongamos mostrar la forma y localización de las válvulas).

Al caso debe entenderse que una estructura es muy parecido a una tabla. Es más soy de la opinión que las bases de datos aparte de tener tablas de datos deberían tener también tablas de funciones, de hecho te animo a que lo implementes. Crea una copia de tu aplicación y la base de datos para hacer cambios, y no toques la original. ..en otro momento volveremos sobre esto, si al término de la conversación (no de este u otro mensaje), se me olvidare, recuérdamelo con un quote de este párrafo...

En el siguiente mensaje en vez de exponer el formulario principal, expondré una clase básica. Las razones se explican allí...
«Ma non troppo»
----> ModoVacaciones = False<----

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
2 clase Base
« Respuesta #7 en: Jueves 26 de Marzo de 2009, 17:19 »
0
================= 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.)

Código: Visual Basic
  1.  
  2.  
  3. ' Esta clase es una CÁPSULA que engloba una colección y un timer
  4. ' la colección admite la posibilidad de limitar cantidad de elementos.
  5. ' el timer se reparte en 2 eventos, y se ha añadido una función de consulta...
  6. ' Toda la funcionalidad es común a las 3 clases, es por ello que se unifica en una sola clase
  7. '   y las instancias exponen sólo las diferencias específicas de cada una.
  8.  
  9.  
  10. Private WithEvents s_Crono         As Timer
  11. Private s_Coleccion                    As Collection
  12. 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.
  13. Private p_LimitarCola                  As Boolean
  14. Private p_PausadoEntrantes        As Boolean
  15. Public Condicion                         As Boolean       ' condicion para servir elemento
  16.  
  17.  
  18. ' ambos eventos los provee el timer...
  19. ' sólo salta el evento si la colección tiene eventos y no se cumple la condición
  20. Public Event Crono()
  21. ' sólo salta el evento si la colección tiene elementos y se cumple la condición.
  22. ' la condición debemos activarla cuando queramos que nos devuelva el primer elemento.
  23. Public Event ServirElemento(ByRef Elemento As Object)
  24.  
  25.  
  26.  
  27.  
  28. Private Sub Class_Initialize()
  29.     p_LimiteCola = 0   ' indica que no hay límite de transacciones de entrada.
  30.     p_LimitarColaSolicitud = False
  31.     Set s_Coleccion = New Collection
  32.     p_PausadoEntrantes = False
  33.     Condicion = False
  34. End Sub
  35.  
  36. ' debe invocarse esta función justo tras ser creada la clase.
  37. Friend Function SetTimer(ByRef Tim As Timer) As Boolean
  38.     On Local Error Resume Next
  39.    
  40.     Set s_Crono = T
  41.     s_Crono.Interval = 1000
  42.     s_Crono.Enabled = False
  43.     SetTimer = (s_Crono Is Nothing)
  44. End Function
  45.  
  46. Private Sub Class_Terminate()
  47.     If Not (s_Crono Is Nothing) Then
  48.         s_Crono.Enabled = False
  49.         Set s_Crono = Nothing
  50.     End If
  51.     Set s_Coleccion = Nothing
  52. End Sub
  53.  
  54.  
  55.  
  56.  
  57.  
  58.  
  59.  
  60. ' PRECAUCIÓN: sólo debería usarse en la instancia de solicitudes.
  61. '   o incluso en la instanacia llamada de ejecución, pero nunca en la siguiente.
  62. '   se debe recordar que funcionan en cascada...
  63. Public Property Get PausaEntrantes() As Boolean
  64.     PausaEntrantes = p_PausadoEntrantes
  65. End Property
  66.     Public Property Let PausaEntrantes(ByVal p As Boolean)
  67.         If p_PausadoEntrantes <> p Then
  68.             p_PausadoEntrantes = p
  69.         End If
  70.     End Property
  71.  
  72. ' pausa, pausa tanto entrantes como salientes....
  73. Public Property Get Pausa() As Boolean
  74.     Pausa = Not s_Crono.Enabled
  75. End Property
  76.     Public Property Let Pausa(ByVal p As Boolean)
  77.         If p_Pausado <> p Then
  78.             p_PausadoEntrantes = p
  79.             s_Crono.Enabled = Not p
  80.         End If
  81.     End Property
  82.    
  83. Friend Property Get Intervalo() As Integer
  84.     Intervalo = s_Crono.Interval
  85. End Property
  86.     Friend Property Let Intervalo(ByVal x As Integer)
  87.         s_Crono.Interval = x
  88.     End Property
  89.    
  90. ' el timer provee 2 eventos, ambos sólo si la colección tiene elementos.
  91. '  Se sirve el primer elemento siempre que se cumpla la condición.
  92. '    la condición es un valor buleano que asigna la instancia de esta clase.
  93. '    es allí donde se decide cuales son las justificaciones de la condición.
  94. '    puesto que cada instancia tendrá condiciones distintas, lo único común es
  95. '    precisamente, definir que se 'cumplió la condición' ... de este modo es común a todas las instancias.
  96. ' 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.
  97. '  es allí donde debe establecerse si se cumple o no la condición.
  98. ' Dado que se ignora cuando se incumple la condición para cada instancia
  99. '  debe decidirse en cualquiera de los 2 eventos el caso que no, o podría activarse la línea comentada
  100. '  en cuyo caso sólo se puede decidir si se cumple la condición en el evento crono... dividiendo la frecuencia de entregas...
  101. Private Sub s_Crono_Timer()
  102.     If s_Coleccion.Count > 0 Then
  103.         If Condicion = True Then
  104.             Dim it As Object
  105.            
  106.             Condicion = False
  107.             Set it = s_Coleccion.Item(1)
  108.             RaiseEvent ServirElemento(it)
  109.            
  110.             ' se elimina desde la clase que lo añade a otra colección, para hacerlo compatible con
  111.             '   la nueva propiedad añadida a última hora: PausarEntrantes
  112.             'Call s_Coleccion.Remove(1)
  113.            
  114.             Set it = Nothing
  115.         Else
  116.             RaiseEvent Crono
  117.         End If
  118.     End If
  119. End Sub
  120.  
  121.  
  122.  
  123.  
  124.  
  125. ' es una propiedad de administrador
  126. '    si se establece el valor a true, no se podrán añadir elementos mientras
  127. '   el nº de elementos en la colección sea mayor= que el tamaño de cola.
  128. Public Property Get LimitarCola() As Boolean
  129.     LimitarCola = p_LimitarCola
  130. End Property
  131.     Public Property Let LimitarCola(ByVal lc As Boolean)
  132.         If lc <> p_LimitarCola Then
  133.             p_LimitarCola = lc
  134.         End If
  135.     End Property
  136.    
  137. ' es una propiedad de administrador
  138. '   esta propiedad sólo indica en tamaño de cola, pero quien determina si se limita o no, es Limitarcola
  139. Public Property Get TamañoCola() As Long
  140.     TamañoCola = p_TamañoCola
  141. End Property
  142.     Public Property Let TamañoCola(ByVal lm As Long)
  143.         If lm > p_TamañoCola Then
  144.             p_TamañoCola = lm
  145.         End If
  146.     End Property
  147.    
  148. Public Property Get Count() As Long
  149.     Count = s_Coleccion.Count
  150. End Property
  151.    
  152. ' lo hemos puesto variant poque igual puede pedirse por índice que por key
  153. Public Property Get Elemento(ByVal Index As Variant)
  154.     On Local Error Resume Next
  155.     Set Elemento = s_Coleccion.Item(Index)
  156. End Property
  157.     Friend Property Set Elemento(ByVal Index As Variant, ByRef it As Object)
  158.         On Local Error Resume Next
  159.         Set s_Coleccion.Item(Index) = it
  160. End Property
  161.  
  162. ' ni añadir ni eliminar se dejan públicas, ya que sólo una clase añade a otra.
  163. 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
  164.     If p_PausadoEntrantes = False Then
  165.         If p_LimitarCola = True Then
  166.             If s_Coleccion.Count >= p_TamañoCola Then Exit Function
  167.         End If
  168.        
  169.         On Local Error Resume Next
  170.         Err.Number = 0
  171.         If Despues = False Then
  172.             Call s_Coleccion.Add(T, Key)
  173.         Else    ' esta versión sólo la utiliza la clase de solicitudes...
  174.             Call s_Coleccion.Add(T, Key, after:=DetrasDe)
  175.         End If
  176.        If (Err.Number = 0) Then
  177.             Añadir = ADD_AÑADIDO
  178.         Else
  179.             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)
  180.        End If
  181.     Else
  182.         Añadir = ADD_PAUSADO
  183.     End If
  184. End Function
  185.  
  186. ' lo normal es eliminar siempre el elemento 1º, las s_Colecciones están basadas en ínndice 1
  187. '   no usamos esta función en la práctiva, se provee para futuras necesidades...
  188. Friend Function Eliminar(Optional Posicion As Long = 1) As Boolean
  189.     On Local Error Resume Next
  190.    
  191.     If Posicion > 0 Then
  192.         Err.Number = 0
  193.         s_Coleccion.Remove (Posicion)
  194.         Eliminar = (Err.Number = 0)
  195.     End If
  196. End Function
  197.  
  198.  
  199.  
  200. Public Function Consulta_MisPedidosEnCola(ByRef IdRef As Form) As Long
  201.     Dim n As Long, k As Long
  202.     Dim tr As infoTransaccion
  203.    
  204.     On Local Error Resume Next
  205.    
  206.     For k = 1 To s_Coleccion.Count
  207.         Set tr = s_Coleccion.Item(k)
  208.         If tr.Control.IdRef.Name = IdRef.Name Then
  209.             n = n + 1
  210.         End If
  211.     Next
  212.     Set tr = Nothing
  213.     Consulta_MisPedidosEnCola = n
  214. End Function
  215.  
  216.  

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:

Código: Visual Basic
  1.  
  2.  
  3. Private Sub s_Crono_Timer()
  4.     If s_Coleccion.Count > 0 Then
  5.         If Condicion = True Then
  6.             Dim iT As Object
  7.            
  8.             Condicion = False
  9.             Set iT = s_Coleccion.Item(1)
  10.             RaiseEvent ServirElemento(iT)            
  11.             Set iT = Nothing    
  12.         End If
  13.         RaiseEvent Crono
  14.     End If
  15. End Sub
  16.  
  17.  

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...
«Ma non troppo»
----> ModoVacaciones = False<----

Nebire

  • Miembro HIPER activo
  • ****
  • Mensajes: 670
    • Ver Perfil
Re: Soporar usuarios recurrentes en un Sistema VB6
« Respuesta #8 en: Domingo 29 de Marzo de 2009, 23:28 »
0
Mañana expondré otra parte... sin embargo espero alguna intervención de tu parte, de lo contrario consideraré que te has olvidado del asunto... ...lo que menos me agrada es perder el tiempo... ...para nada.

Expón dudas si las tienes, preguntas si te quedan, o, lo que sea, pero el foro si no es interactivo es una tarea y por ahí no se cede...
«Ma non troppo»
----> ModoVacaciones = False<----