Mostrar Mensajes

Esta sección te permite ver todos los mensajes hechos por este usuario, recuerda que solo puedes ver los mensajes en áreas en donde tu tienes acceso.


Mensajes - Soporte

Páginas: [1] 2 3 ... 59
1
STX8091 / Re:Cliente web, enviar petición HTTP SEND - GET
« : junio 19, 2018, 19:52:14 pm »
Es decir en el componente "HTTP SEND GET" veo si el estado de ENO es 0 quiere decir que no termino ?, porque cuando no se envina nada tambien esta en 0 ? O no es asi ?

Claro, cuando es "1", es porque se aceptó para el envío, si por algún motivo no se acepta, es "0", te copio la descripción de la documentación del componente:

El valor del puerto de salida "ENO" es "1" si la operación fue aceptada por el PLC y está en curso para su transmisión al servidor web (esto no quiere decir que la transmisión sea exitosa, para ello debe comprobar el estado de la librería con el componente HttpSendGetLibStatus). Si es "0" la salida ENO, la operación no fue aceptada por algún error (librería ocupada en otra transacción pendiente, error de memoria, librería no inicializada, etc).


El problema que tengo es que tengo que crear dos regitros que se pueden dar simultáneamente. entonce tengo que decirle que si esta ocupado que espere y luego cargue ese parametro ya que la activacion que carga se realiza cuando una entrada se activa y la misma se activa menos de un segundo.

Claro, si tenés dos entradas que se activan por un tiempo pequeño, una a continuación de la otra, y debes enviar sus valores de activación, es probable que mientras estás enviando el primer valor de la entrada, la segunda ocurra y la librería esté ocupada enviando la primera transacción.

A veces las transacciones pueden durar hasta varios segundos, ya que depende de la red y de la computadora que reciba. Generalmente es menos de 1 segundo y más de 300 ms o 500 ms.

En ese caso lo que podes hacer es enviar un entero (ejemplo una variable llamada Estado): por ejemplo, si entrada A es "1", haces el entero "Estado=01", si entrada B es "1", lo hacés "Estado=10". Si ambás entradas son "1", lo hacés "Estado=11".

Luego, si "Estado" es diferente de cero, es porque registraste una entrada activa, entonces con algún flag (que lo podes obtener desde el evento OnHttpSendCompleted), comprobás que la librería este lista para enviar nuevamente, y envías el entero.

Una vez que envías, haces el entero "Estado=0", para que si luego hay una entrada activa, quede registrado para el próximo envío.

Saludos!

2
Kinco / Re:bits
« : junio 19, 2018, 19:01:35 pm »
BUEN DÍA
ALGUIEN SABE COMO PUEDO DAR DE ALTA MAS BITS PARA PODER DIRECCIONAR AL PLC TENGO UNA HMI KINCO MT4434TE Y SOLO TIENE PARA DIRECCIONAR AL PLC B3,B10,B11,B12 Y B13 Y EN EL PROGRAMA DEL PLC (AB MICROLOGIX 1500) TENGO DIRECCIONES COMO B49:0/1.
O COMO HAVER UN SHORT CUT ASI COMO EN FACTORY Y YA LEER TODA LA DATA FILE
ESPERO ALGUIEN ME PUEDA AYUDAR

Buenas tardes, no le sabría decir como es para el PLC de la línea AB MICROLOGIX, desconozco su operación, ya que solo damos soporte para uso del HMI con los PLC de Slicetex Electronics.

Saludos!

3
Kinco / Re:Trend curve
« : junio 19, 2018, 11:20:05 am »
Estoy usando una Pantalla Hmi - Kinco 10'' Touch Mt4532te y me comunico mediante modbus a un equipo remoto mediante el protocolo.
Me puedo comunicar bien con los datos, solo quiero que se generar el movimiento de la barra de datos del historia.

Adjunto programa.

Buenos días. No puedo descomprimir el archivo, me da un error de formato el archivo RAR.

Para habilitar la barra de "scroll", dentro del componente Trend Curve debes ir a pestaña "Trend Graphics", y en opción "Type" seleccionar "Multiple pages".

También colocar Sampling Points = 10 y PageNum = 10 (valores iniciales, luego podés cambiarlos, determinan la cantidad de datos a almacenar).

Posteriormente en pestaña "Scale", tildar "Use scroll bar".

Luego te quedaría algo así en pantalla:



La barra "scroll" a medida que llegan las muestras, se va achicando y luego podes moverla con el dedo.

En el manual hay algunos conceptos para la configuración del Trend Curve que pueden ayudarlo a entender mejor.

Saludos!


4
STX8091 / Re:Cliente web, enviar petición HTTP SEND - GET
« : junio 19, 2018, 10:43:22 am »
Una consulta, cuantos componentes HTTP SEND -GET puedo colocar ? POrque veo que no son numero como en el caso de los temporizadores u otros elementos.

Buenos días Esteban.

Podés hacer solo una transacción al mismo tiempo al servidor. Es decir, podés llamar al componente las veces que quieras, pero siempre asegurando que la transacción anterior haya terminado para que tenga efecto.

Caso contrario, el componente al estar ocupado en una transacción previa, retorna en la salida ENO el valor "0".

Esto no afecta a la conexión actual, solo que no tiene efecto mientras este ocupado.

Saludos!

5
Kinco / Re:Trend curve
« : junio 11, 2018, 10:44:30 am »
Buenos Tardas, mi consulta es sobre la manera correcta de realizar un gráfico de curva "TREND CURVE".. no puedo hacer que se desplace la barra para datos anteriores y hacer el zoom sobre ellos.

estoy tratando de realizar el modelo de ejemplo.

Buenos días,

¿Qué modelo de HMI y PLC estás utilizando?

¿El componente "Trend curve" de la sección 4.6.1 del manual estas intentando utilizar? ¿Cuál ejemplo?.

Saludos


6
Buenos días Marcos,

En el caso de usar StrFormat con las cadenas "packed" es necesario poner la sentencia "true" en el tercer parámetro, correcto?

Si queres que la cadena resultante sea "packed", utilizar "true". Caso contrario, la salida será "unpacked" independiente del formato de la cadena de entrada.

¿Cuál es el resultado de usar la función de la siguiente manera?

StrFormat(errorMsg[1], DISPLAY_LENGTH, false, !"Falla al inicializar ModBus.")

En este caso, con parámetro "false", en la cadena resultante de salida en errorMsg[1][] vas tener la cadena "Falla al inicializar ModBus.", pero en formato "unpacked".

Es decir, seria lo mismo que hacer:

Código: (Pawn) [Seleccionar]
new errorMsg_Unpacked[] = "Falla al inicializar ModBus."  // Unpacked
Con parámetro "true" la salida seria equivalente a:

Código: (Pawn) [Seleccionar]
new errorMsg_Packed[] = !"Falla al inicializar ModBus."  // Packed
La diferencia esta que el array "errorMsg_Unpacked[]" ocupa 29 celdas *4 = 116 bytes, mientras que errorMsg_Packed[] ocupa 8 celdas * 4 = 32 bytes.



Solo para entender:

Para acceder a cada caracter de errorMsg_Unpacked[] (en el caso que quieras hacerlo), indexas normalmente, ejemplo:

Código: (Pawn) [Seleccionar]
nLcdPrintf(0, 1, LCD_CLRLINE, ""%c %c %c %c %c", errorMsg_Unpacked[0], errorMsg_Unpacked[1],errorMsg_Unpacked[2],errorMsg_Unpacked[3],errorMsg_Unpacked[4])
Imprime "F a l l a"

En el caso de un "packed" string, se agrupan 4 carácter por celda, estando el primer carácter de 8-bit, en la posición más alta. Por lo tanto hay que "desempaquetarlo" para obtener caracter por caracter.

Para ello usamos GetCellChar(cell, char_num), donde "cell" es el entero donde esta el carácter y char_num el número de caracter a obtener (del 0 al 3).

Ejemplo:

Código: (Pawn) [Seleccionar]
nLcdPrintf(0, 1, LCD_CLRLINE, ""%c %c %c %c %c", GetCellChar(errorMsg_Packed[0], 0), GetCellChar(errorMsg_Packed[0], 1),GetCellChar(errorMsg_Packed[0], 2),GetCellChar(errorMsg_Packed[0], 3),GetCellChar(errorMsg_Packed[1], 0))
Imprime "F a l l a"

Saludos!


7
Buenas tardes Marcos,

Te paso a detallar las causas del problema y como solucionarlo.

CAUSAS

Luego de analizar el proyecto completo que enviaste por email, encontré que el mismo "colgaba" el PLC en el mismo instante que se transfería el código desde StxLadder a la memoria del PLC, sin ejecutar instrucción alguna de tu código.

Por lo tanto, el problema no es de error de código o de alguna función como StrFormat() que producía misteriosamente cierta interacción de acuerdo a si se comentaba o no.

El problema es de memoria RAM, en este caso, el proyecto utilizaba más memoria RAM que la que tiene asignada el dispositivo para código de usuario, por lo tanto, cuando se cargaba al PLC, interfería con el sistema operativo interno, escribiendo su área de memoria, lo cual generaba fallas aleatorias, de acuerdo a si el proyecto utilizaba más o menos RAM (por ello cuando comentabas, a veces funcionaba y otras no).

El "culpable" fue StxLadder que comprobaba erróneamente la cantidad de RAM utilizada por el proyecto y no te daba advertencia alguna de que estabas superando los limites del modelo de dispositivo que tenés.

SOLUCIÓN

Esto se solucionó en la última versión de StxLadder, la versión 1.9.0 que liberé hoy.

Te recomiendo instalarla.

Esta versión te muestra al compilar en detalle cuanta memoria ROM y RAM utilizas en el proyecto, y cuanto te queda disponible. También, si te excedes, te da un error de compilación para que bajes el consumo.

Por otro lado, también se mejoró el firmware del PLC, que también comprueba previamente si el programa que va a ejecutar no excede sus limites de RAM / ROM. Es decir, un doble-check, entre StxLadder y el PLC. Aumentando la seguridad, y evitando "colgarse" si el programa es inválido, solo prenderia el led RUN ERROR, pero no perderías conectividad (como te pasaba).

La versión de firmware que debés bajar es la 211 o superior para tu modelo de PLC:

http://slicetex.com/hw/stx8091/soft.php#Firmware

APLICACIÓN A TU PROYECTO

Una vez que instales StxLadder versión 1.9.0 y actualices el firmware del PLC a la versión 211, abrí tu proyecto.

Si es el mismo que me pasaste a mí, al compilar deberías ver el siguiente mensaje de error:

Cita de: StxLadder 1.9.0
Error de Memoria: Insuficiente memoria RAM: El proyecto requiere 23268 bytes y el dispositivo tiene disponible 16384 bytes. Disminuya variables globales, variables locales inicializadas, comparta/reutilice variables globales, utilice packed strings, agrupe datos, etc

El mensaje dice que utilizas 23268 bytes de memoria RAM, pero el dispositivo tiene 16384 (16KB).

En el PLC tenés dos memorias, RAM (donde van las variables lectura/escritura) y la ROM (donde van las instrucciones de código para ejecutar la lógica o datos de solo lectura).

La RAM es más pequeña que la ROM, por lo tanto hay que tener más cuidado en definir grandes cantidades de variables, que en escribir código.

El compilador Pawn, considera a los string literales o constantes string como variables que van en RAM, por lo tanto cuando escribís "Falla al inicializar ModBus." en StrFormat(), se coloca en memoria RAM dicha cadena, ocupándote lugar:

Código: (Pawn) [Seleccionar]
StrFormat(errorMsg[1], DISPLAY_LENGTH, false, "Falla al inicializar ModBus.")
La cadena "Falla al inicializar ModBus." tiene 26 caracteres más un terminador "0", en total ocupa 27 posiciones. En Pawn, las cadenas son arrays de enteros, por lo tanto cada posición tiene 4 bytes.

La memoria RAM consumida por esa cadena es de: 27*4=108 bytes.

Es bastante, y más si utilizas muchísimas cadenas en tu código, a la larga ocupas toda la memoria RAM.

Un truco para que una cadena literal (constante) no utilice tanta memoria RAM es haciéndola "packed", en ese caso, el compilador agrupa en cada posición 4 caracteres. Esto se especifica con el operador " ! " antes de una cadena, por ejemplo:

Código: (Pawn) [Seleccionar]
StrFormat(errorMsg[1], DISPLAY_LENGTH, true, !"Falla al inicializar ModBus.")
Ahora la cadena de 27 caracteres ocupa 27 bytes, una reducción importante.

Notar como el tercer parámetro de StrFormat() es "true", indicando que la cadena resultante en errorMsg[] va a ser packed también.

Como la cadena resultante es "packed", podes definir a errorMsg[]  con un tamaño en "celdas" 4 veces menor.

Otro ejemplo:

Código: (Pawn) [Seleccionar]
new String1[] = !"Falla al inicializar ModBus."
Esto solo funciona con cadenas, pero para variables enteras o flotantes, arrays, etc, cada vez que definís uno de forma global o del tipo estático (inicializado dentro de una función), te ocupa 4 bytes de memoria RAM. Por lo tanto, también, usar a discreción e intentar ahorrar lo más que pueda.

LUEGO VOY A PUBLICAR UN POST CON TRUCOS Y CONSEJOS PARA DISMINUIR MEMORIA RAM

Volviendo a tu proyecto, una forma de considerar a todas las cadenas como "packed" por defecto, es utilizando la opción "Packed literal strings" antes de compilar como muestra la siguiente imagen:



Se encuentra en menú "Proyecto > Propiedades > Compilador (pestaña)".

Ahora si volvés a compilar te va a mostrar "Compilación correcta":



Notar como en los mensajes ahora el total de RAM requerida es "10348" bytes, respecto a los "23268" bytes de antes.

Una reducción importante logra la opción Packed literal strings, que considera a todas tus cadenas literales como packed, tal como si le hubieras aplicado el símbolo " ! " a cada una.

Cuando esta opción esta activada, si antepones el símbolo " ! " a la cadena, genera el comportamiento inverso, se transforma en unpacked y ocupa 1 carácter por celda.



Si bien utilizar la opción Packed literal strings ya te permite liberar RAM necesaria para que el proyecto se pueda cargar, te recomiendo no utilizar tantas cadenas, así tenes RAM disponible para futuro uso cuando el proyecto crezca con nuevas funciones o código que le agregues.

En todo caso, hacé las cadenas menos descriptivas, utilizá códigos de error para hacerlas más cortas en extensión, etc.

Por otro lado, al usar el argumento "true" en StrFormat(), te habilita a que puedas reducir el tamaño del array:

Código: (Pawn) [Seleccionar]
new errorMsg[8][DISPLAY_LENGTH+5]
Que si lo analizás, tiene 8*(DISPLAY_LENGTH+5) celdas. Si DISPLAY_LENGTH=34, la cantidad de celdas utilizadas es:

312 celdas = 1248 bytes (bastante)

Si el string que vas a almacenar es "packed" con StrFormat() podrías reducir DISPLAY_LENGTH a 10, para almacenar 39+1 caracteres por linea:

Código: (Pawn) [Seleccionar]
#define DISPLAY_LENGTH 10
new errorMsg[8][DISPLAY_LENGTH]

Ahora, el total seria: 80*4 = 320 (bytes)

Podes ir probando reduciendo código o diferentes alternativas con variables, compilar y ver que opción te genera menos consumo de RAM, hasta que logres un equilibrio.

En el detalle de compilación, donde dice "Data block 6252 bytes", esa es la RAM usada por variables globales, strings, etc.

Luego haré un post resumiendo y explicando mejor como ahorrar RAM, pero esto es lo fundamental.

Saludos!

8
STX8081 / Ejemplo display LCD para I2C y memoria EEPROM
« : junio 07, 2018, 12:13:50 pm »
Para aquellos que utilizan el display LCD a través de I2C (puerto HP2/HP3), ver siguiente post del foro:

foro.slicetex.com/index.php?topic=269.msg1432#msg1432

Adjuntamos en esta oportunidad el mismo ejemplo, pero que también escribe y lee datos de memoria EEPROM.

Cabe mencionar que las funciones para la memoria EEPROM están descriptas en el Manual de Programación Pawn del PLC.

Ver proyecto adjunto.

Saludos!

9
A continuación adjuntamos el ejemplo "MbTcpServerCountOut.zip" para el PLC que a diferencia del ejemplo "MbTcpServerCounter.zip" conmuta una salida a transistor cada vez que el registro ModBus 40001 es puesto a 0.

Requiere un PLC con salida PWM o transistor rápida.

Esto permite medir tiempos mediante un osciloscopio o analizador lógico en la salida.

Capturas tomadas en salida digital (la duración de cada pulso  alto o bajo es el tiempo entre escritura lectura):





Log tomado desde aplicación Visual C# (sin relación a capturas previas):

Código: (log.txt) [Seleccionar]
Response: 915 ms
Response: 934 ms
Response: 20 ms
Response: 30 ms
Response: 76 ms
Response: 97 ms
Response: 139 ms
Response: 161 ms
Response: 21 ms
Response: 31 ms
Response: 84 ms
Response: 95 ms
Response: 140 ms
Response: 161 ms
Response: 205 ms
Response: 218 ms
Response: 271 ms
Response: 282 ms
Response: 327 ms
Response: 347 ms
Response: 389 ms
Response: 411 ms
Response: 458 ms
Response: 469 ms
Response: 522 ms
Response: 532 ms
Response: 10 ms
Response: 31 ms
Response: 9 ms
Response: 24 ms
Response: 76 ms
Response: 86 ms
Response: 133 ms
Response: 154 ms
Response: 10 ms
Response: 30 ms
Response: 20 ms
Response: 31 ms
Response: 79 ms
Response: 89 ms
Response: 141 ms
Response: 152 ms
Response: 205 ms
Response: 217 ms
Response: 21 ms
Response: 32 ms
Response: 13 ms
Response: 33 ms
Response: 76 ms
Response: 97 ms
Response: 142 ms


10
Kinco / Re:Tiempo de actualización de elementos
« : junio 04, 2018, 13:21:01 pm »
Buenos días Francisco,

Estuve probando el HMI con tu diseño, y presentaba el problema que describís.

Como no encontré error alguno en el HMI, luego de algunas horas de pruebas, decidí recrear el proyecto desde cero y funcionó perfecto.

Ahora, yo utilicé el el HMIware 2.4 para el diseño, cuando lo abrí con la nueva versión que utilizás en tu proyecto, HMIware 2.5 presentó el mismo problema. Cosa de no creer.

Aparentemente cuando abrís un proyecto creado en versión 2.4 desde el software versión 2.5, pasa esto.

No tuve tiempo para hacer un proyecto desde cero utilizando HMIware 2.5 para ver si así funciona bien.



Pasos a seguir:

Para no dilatar más, te recomiendo desinstalar la versión 2.5 e instalar la versión 2.4 del soft para el HMI en tu PC. En el CD de instalación del paquete de venta esta dicha versión, sino avísame y te comparto algún link de descarga.

Luego abrí el proyecto "HMI_SlicetexTankTest_V1.zip" que te adjunto y probalo, es un "clon" de tu diseño, pero más rustico. Lo único que le falta es actualizar los gráficos y animaciones de los tanques.

Si te funciona bien, seguí utilizando la versión 2.4 del software, ya que no hay forma de abrir un proyecto creado con 2.5 en la versión 2.4 (pero sí al revés, que es lo que no funciona, quizás algo hace mal el soft al actualizar diferentes versiones en proyectos creados con versiones previas).

Seguramente tu diseño, lo basaste en algún diseño de ejemplo de la página, y como fueron creados con versión 2.4, se trasladó el problema. No sé si creando desde cero en 2.5, esto se soluciona, cosa de locos.



Por otro lado, revisando tu código en el PLC, noto que en la Network N026, N027 y N028 escribís en la EEPROM en cada Scan Cycle. Esto no es recomendado, porque agotás la vida útil de la EEPROM con miles de escrituras por segundos, y además crea un retardo de decenas de milisegundos en el Scan Cycle, ya que la EEPROM tiene un tiempo de escritura.

Para evitar esto, te adjunto el proyecto "PLC_TanquesSlicetex_Version_1.zip" para el PLC, que desactiva estos componentes (borralos luego) y en su lugar llama al diagrama "WriteEeprom" cada diez segundos, para guardar los últimos valores, de tal forma de evitar escribir permanentemente en EEPROM.



Me habías comentado de un defasaje de tiempo, el tema de la EEPROM puede ser una causa, pero también, como los segundos/contadores los incrementas desde Principal.sld, y a su vez antes/luego de cada incremento tenes bastante código, esto genera que el procesador pierda tiempo ejecutando componentes antes de incrementar contadores. Este pequeño delay (producido pro ejecutar muchas cosas antes en el Scan Cycle) a la larga puede introducirte el error de tiempo que observás.

Lo ideal seria que trates de agrupar código en fuciones Ladder, y solo llamarlas si es necesario, o con Temporizadores Ladder, llamar a fragmentos de código cada 50 o 100 mS (por ejemplo aquellos que no requieran una repuesta en tiempo real ya sea en el PLC o en HMI, como carga de registros, etc), de tal forma de no ocupar el procesador en la lógica principal para ejecutar todos los componentes al mismo tiempo.

Saludos!

11
Buenas tardes.

Te adjunto un ejemplo StringArrayCheck.zip que replica las cadenas que me pasaste con StrFormat().

Cargalo al PLC, y accedé vía el navegador web a la IP del dispositivo, debería mostrarte las cadenas, o decime si te da error.

Deberías ver algo así:





Por otro lado, como no veo un error que salte a la vista, ¿tenés alguna versión del código del proyecto que te de el error descripto, para que yo pueda reproducirlo?.

Es decir, podrías eliminar/comentar todas las partes que no tengan que ver con el error, dejando solo el código problematico (que al cargar te produce el RunError). Luego desde menú "Archivo > Crear backup del proyecto (zip)", generás el archivo ZIP para adjuntar con el proyecto funcional en cuestión así lo puedo abrir y cargar al PLC.

Las partes que requieran lectura de alguna entrada, comunicacíon de módulo externo, etc, eliminalas, para no tener que cablear hardware externo a la hora de probar el programa, ya que aparenta ser un tema de software puntual.

Saludos!






12
Librería ModBus TCP Master para Visual C#

A continuación subimos un ejemplo simple en Visual C# para comunicarse al PLC utilizando protocolo ModBus TCP.

En este caso, el PLC actúa como Esclavo y desde Visual C# (una computadora) nos comunicamos como Maestro para escribir/leer registros o bits.

Aplicación de ejemplo

Cargue en el PLC el proyecto MbTcpServerCounter.zip para StxLadder que adjuntamos.

Luego descargue el proyecto ModBusTcpResponseTime.zip para Visual C# y ejecute la aplicación "ModBusTcpResponseTime.exe" de la carpeta "bin\Release".

Especifique dirección del PLC (ejemplo 192.168.1.81) y haga click en botón "Conectar". El puerto por defecto utilizado es 502. A continuación, si no hay errores de comunicación verá la siguiente pantalla (similar):



El funcionamiento es muy simple.

En el PLC, como esclavo ModBus, constantemente se incrementa una variable contador cada 1 mS y se la carga al registro ModBus número 40001.

En el caso que el registro 40001 se haga 0 (por ejemplo un Maestro escribió valor 0), la variable contador se hace cero nuevamente.
Por lo tanto, el valor en el registro 40001 representa el tiempo que pasó desde que un Maestro escribió el valor 0 y volvió a leer el valor del registro.

La aplicación en Visual C#, simplemente envía cada 10 mS una transacción ModBus de escritura de registro (escribe valor 0 en 40001) y luego intenta leer el mismo registro a través de ModBus.

Luego imprime dicho valor en pantalla, que representa el tiempo entre escritura y lectura ModBus.

También calcula el valor promedio de los últimos X valores (definido en caja Cantidad de muestras) y el valor máximo alcanzado.

Adicionalmente en el directorio del ejecutable, se guarda un archivo de texto llamado "log.txt" que registra todas las lecturas de tiempo realizadas de la última conexión para realizar un mejor analisis.

Código en C#

Abra el proyecto en Visual C# (recomendado versión 2015 o superior).

La librería ModBus TCP Master, se encuentra en el archivo stx8xxx.dll incluido en el directorio "bin\release" del proyecto.
Es esta librería que incluimos como referencia en el proyecto (References) y debe ser empleada en su aplicación Visual C# para tener acceso a las funciones ModBus TCP.

Luego agregamos el espacio de nombre en el código fuente con:

Código: (C#) [Seleccionar]
using stx8xxx;

Creamos el objeto que accede a las funciones ModBus como maestro:

Código: (C#) [Seleccionar]
private ModBusTcpMaster MbMaster;
Para conectarnos, simplemente especificamos dirección IP y puerto (que se obtienen de los controles de la ventana):

Código: (C#) [Seleccionar]
MbMaster = new ModBusTcpMaster(textDeviceIP.Text, (ushort)numDevicePort.Value);
Luego especificamos dos eventos, que serán llamados cuando tengamos repuesta de datos y excepciones (un error):

Código: (C#) [Seleccionar]
MbMaster.OnResponseData += new ModBusTcpMaster.ResponseData(MbMaster_OnResponseData);
MbMaster.OnException += new ModBusTcpMaster.ExceptionData(MbMaster_OnException);

Posteriormente, en el código creamos una funcion para leer el registro ModBus 40001, asociado al contador del PLC:

Código: (C#) [Seleccionar]
        private bool ReadPlcCounterRegister()
        {
            bool ErrorsFound = false;

            // Check connection.
            if (!IsConnectedMsg())
            {
                ErrorsFound = true;
                return ErrorsFound;
            }

            // Unique transaction ID (only for identify this transaction on response event).
            ushort ID = 2;

            // Send "Read Holding Register" request.
            MbMaster.ReadHoldingRegister(ID, 40001, 1);

            // Return to caller.
            return ErrorsFound;
        }

Notar como usamos la función "MbMaster.ReadHoldingRegister()" para leer un Holding Register del PLC en dirección "40001". El ID, es un valor arbitrario que utilizamos para luego poder identificar en la repuesta que los datos recibidos corresponden a esta transacción y no a otra que podamos haber realizado.

También creamos una función para escribir el registro del PLC numero 40001:

Código: (C#) [Seleccionar]
        private bool WritePlcCounterRegister(ushort Value)
        {
            bool ErrorsFound = false;

            // Check connection.
            if (!IsConnectedMsg())
            {
                ErrorsFound = true;
                return ErrorsFound;
            }

            // Unique transaction ID (only for identify this transaction on response event).
            ushort ID = 1;

            // Get byte array from "Value".
            byte[] Data = BitConverter.GetBytes((ushort)IPAddress.HostToNetworkOrder((ushort)Value));
           
            // Send "Write Single Holding Register" request.
            MbMaster.WriteSingleRegister(ID, 40001, Data);           

            // Return to caller.
            return ErrorsFound;
        }

Nuevamente, notar como usamos la función "MbMaster.WriteSingleRegister()" para escribir un Holding Register del PLC en dirección "40001". El ID, es un valor arbitrario que utilizamos para luego poder identificar en la repuesta que los datos recibidos corresponden a esta transacción y no a otra que podamos haber realizado.

También, el objeto "MbMaster" de la librería contiene más funciones para leer bits, o escribir/leer varios registros al mismo tiempo. Ver documentación en código ofrecida por Visual C#

Luego, desde un timer, llamamos a estas funciones cada 10 mS, donde comprobamos variable ReadyForTransaction, que indica que estamos listos para comenzar una nueva transacción y luego comprobamos variable StartWriteTransaction, si es "1", enviamos una transacción de escritura, sino, enviamos una transacción de lectura:

Código: (C#) [Seleccionar]
        private void timerPolling_Tick(object sender, EventArgs e)
        {
            //
            // Check if previous response was received or is ready for new transaction.
            //

            if (ReadyForTransaction)
            {
                //
                // Check transaction to do: write or read counter register.
                //

                if (StartWriteTransaction)
                {
                    // Write counter register with 0 value.
                    WritePlcCounterRegister(0);
                }
                else
                {
                    // Read PLC counter register.
                    ReadPlcCounterRegister();
                }
            }
        }

La respuesta a estas transacciones, las obtenemos del evento:

Código: (C#) [Seleccionar]
        private void MbMaster_OnResponseData(ushort ID, byte function, byte[] values)
        {
            // ------------------------------------------------------------------------
            // Seperate calling threads
            // ------------------------------------------------------------------------

            if (this.InvokeRequired)
            {
                this.BeginInvoke(new ModBusTcpMaster.ResponseData(MbMaster_OnResponseData), new object[] { ID, function, values });
                return;
            }

            // ------------------------------------------------------------------------
            // Identify requested data
            // ------------------------------------------------------------------------

            StringBuilder ResponseStr = new StringBuilder("Respuesta recibida: ");

            //
            // Check ModBus function code received.
            //

            switch (function)
            {
                case 3:
                    ResponseStr.Append("Read holding register ");

                    //
                    // Check transaction ID (counter read).
                    //

                    if (ID == 2)
                    {
                        //
                        // Get register value.
                        //
                        // UInt16 RegisterValue = (UInt16)(((UInt16)(values[0] << 8)) | ((UInt16)(values[1])));
                        //

                        // Check architecture endianess.
                        if (BitConverter.IsLittleEndian)
                        {
                            // Reverse array values.
                            Array.Reverse(values);
                        }

                        // Get register value from received bytes.
                        UInt16 RegisterValue = BitConverter.ToUInt16(values, 0);

                        // Update text box values on windows form.
                        UpdateTextBox(RegisterValue);

                        // Read register is done, now, write register on next timer tick.
                        StartWriteTransaction = true;
                    }

                    break;
                case 6:
                    ResponseStr.Append("Write single register ");

                    //
                    // Check transaction ID (counter write).
                    //

                    if (ID == 1)
                    {
                        // Write register is done, now, read register on next timer tick.
                        StartWriteTransaction = false;
                    }

                    break;
                default:
                    ResponseStr.Append("Unknown ");
                    break;
            }

            // Show message in status bar.
            ResponseStr.AppendFormat("({0}) - data bytes/words ({1}/{2}).", function, values.Length, values.Length / 2);

            PrintStatusBarMessage(ResponseStr.ToString());

            // Response received, ready for next transaction.
            ReadyForTransaction = true;
        }

Cuando este evento es llamado, nos pasa 3 argumentos:

  • ID: Identificación de transacción que colocamos al realizar una operación.
  • function: Código de Función ModBus al que pertenece la repuesta.
  • values[]: Bytes recibidos de la repuesta ModBus.

Notar como el siguiente fragmento es llamado, cuando hay una repuesta de escritura:

Código: (C#) [Seleccionar]
                case 6:
                    ResponseStr.Append("Write single register ");

                    //
                    // Check transaction ID (counter write).
                    //

                    if (ID == 1)
                    {
                        // Write register is done, now, read register on next timer tick.
                        StartWriteTransaction = false;
                    }

Procesamos, si es "function=6" (repuesta de una transacción Write single register) y "ID=1" (lo establecimos en WritePlcCounterRegister(), hacemos la variable StartWriteTransaction=false para que en el próximo evento timer en timerPolling_Tick() realicemos una operación de lectura.

En la repuesta de lectura, luego de llamar a función "ReadPlcCounterRegister()" se entra al siguiente fragmento de código:

Código: (C#) [Seleccionar]
                case 3:
                    ResponseStr.Append("Read holding register ");

                    //
                    // Check transaction ID (counter read).
                    //

                    if (ID == 2)
                    {
                        //
                        // Get register value.
                        //
                        // UInt16 RegisterValue = (UInt16)(((UInt16)(values[0] << 8)) | ((UInt16)(values[1])));
                        //

                        // Check architecture endianess.
                        if (BitConverter.IsLittleEndian)
                        {
                            // Reverse array values.
                            Array.Reverse(values);
                        }

                        // Get register value from received bytes.
                        UInt16 RegisterValue = BitConverter.ToUInt16(values, 0);

                        // Update text box values on windows form.
                        UpdateTextBox(RegisterValue);

                        // Read register is done, now, write register on next timer tick.
                        StartWriteTransaction = true;
                    }

Aquí obtenemos el registro leído en RegisterValue (convertimos de valor de red a valor de computadora de 16-bits) y luego actualizamos las cajas de texto de la ventana con la función UpdateTextBox().

Al final del evento se hace la variable ReadyForTransaction igual a true indicando que la aplicación esta lista para una nueva transacción ya que recibimos el dato que esperábamos (esto es simplemente como lógica en nuestro ejemplo, para no enviar una transacción nueva sí no recibimos la enviada previamente).

Código: (C#) [Seleccionar]
            // Response received, ready for next transaction.
            ReadyForTransaction = true;
        }



Aún la librería no esta publicada en la página web oficialmente por estar en desarrollo, por ello cualquier duda, pueden utilizar nuestro foro.

Abrir los ejemplos para el PLC con StxLadder 1.8.9 o superior.

Saludos!

13
Kinco / Re:Tiempo de actualización de elementos
« : mayo 30, 2018, 11:23:11 am »
Buenas, aca va el proyecto tanto del HMI como del PLC. las variables que tiene demora en la actualizacion son las que se usan para "producto" y para "lote" y el gif animado del tanque 5 en el frame 0.
Gracias!

Buenos días. Parece que te olvidaste de ajuntar el proyecto o de alguna manera no se subió  :)

Fijate de subirlo todo comprimido como archivo ZIP.

Saludos!

14
Kinco / Re:Tiempo de actualización de elementos
« : mayo 29, 2018, 11:54:58 am »
Buenas, tengo de las dos. Tengo un gif animado que se activa cuando escribo 1 un registro en el PLC (para indicar que un rele está activo) y tengo dos valores que cargo en la pantalla; uno a través de un elemento de Text input y otro de un number input. Luego en la pantalla principal tengo dos elementos para visualizar los valores cargados.

Buenos días.

Si crees que puedo reproducir la falla cargando el proyecto en el PLC y en el HMI, adjunta los mismos así los pruebo y veo el efecto.

En todo caso, deshabilita o comentá las funciones del PLC que requieran algún hardware externo para que funcione, como entradas analógicas, digitales, etc. De tal forma que pueda cargarlo directamente y reproducir funcionamiento en pantalla sin cableado adicional.

Saludos!

15
Kinco / Re:Tiempo de actualización de elementos
« : mayo 28, 2018, 21:08:17 pm »
La aplicación de la pantalla consiste en una pantalla principal donde se ve info resumida y 3 pantallas particulares con los datos de cada tanque. En las pantalla particulares puedo cargar el producto y el lote y me indica si está funcionando o no el mezclador del tanque. Sin embargo cuando luego de cargar los datos en la pantalla particular, voy a la principal donde se pueden ver estos datos, demora unos segundos (entre 5 y 8 ) para actualizar la información. y algunas veces no la actualiza hasta que no voy a la pantalla particular y retorno a la principal. Puede ser algo mal configurado o es asi? Puedo adjuntar el proyecto si es necesario.
Gracias

Buenas. La info que no se refresca rápido, ¿la obtenés del PLC o es un dato que guardás en memoria del HMI en una pantalla particular, y luego la cargás en la pantalla principal?

Saludos!

Páginas: [1] 2 3 ... 59