Source: http://foro.slicetex.com/index.php?topic=240.0
Timestamp: 2018-01-19 03:06:03+00:00

Document:
Imprimir en VirtualHMI un string que contiene \n
« : noviembre 28, 2016, 19:01:09 pm »
Hola Boris buenas tardes,
Cada vez que envío un email de notificación, primero "armo" en un string "email_cuerpo" todo el cuerpo del email.
Esta variable email_cuerpo contiene "\n" cada tanto, para mostrar el email de una manera fácilmente legible.
Por otro lado, utilizo VirtualHMI para mostrar una gran cantidad de información sobre el funcionamiento del sistema. Entre ellas, tengo una pantalla en donde muestro el cuerpo de la ultima notificación enviada por email, y el resultado de dicho envío.
El problema que tengo es que al mostrar en VirtualHMI (Android) el cuerpo del último email, es como que ignora los comandos de formato "\n" (nueva línea), y todas las líneas aparecen "encimadas" una sobre la otra, en el mismo renglón de VirtualHMI.
La sintaxis que utilizo para mostrar el cuerpo en VirtualHMI es:
nLcdPrintf(0,6,0,"CUERPO= %s", email_cuerpo)
También probé a reemplazar "\n" por "\r\n" y dio el mismo resultado.
Otra opción sería "parsear" completo el string, buscando los "\n" y en base a eso cambiar la posición "Y" de nLcdPrintf(), pero es bastante laborioso.
Donde la estoy errando? O es que nLcdPrintf() no soporta "\n" ?
Re:Imprimir en VirtualHMI un string que contiene \n
« Respuesta #1 : noviembre 30, 2016, 21:29:34 pm »
No, el software VirtualHMI no procesa el '\n' al recibirlo.
Sin embargo es simple de hacer con una función Pawn que parsea la cadena con los caracteres de línea '\n' y envía cada linea por separado a VirtualHMI.
Te adjunto ejemplo en archivo ZIP el proyecto con la función nLcdPrintMultLines(x, y, String[]) que podes usarla cuando necesitas enviar múltiples líneas.
// Imprimir en VirtualHMI cadena con múltiples líneas.
nLcdPrintMultLines(0, 0, "Hola\neste es un mensaje\ncon multiples\nlineas")
La función en cuestión es:
// Funcion : nLcdPrintMultLines()
// Descripcion : Imprime una cadena con múltiples líneas en VirtualHMI.
nLcdPrintMultLines(x, y, const String[])
new Buffer[100]
new i=0, j=0
new Stop = FALSE
// Parsear cadena.
// Copiar una línea de String[] en Buffer[].
while(String[i] != '\n' && String[i] != '\0')
Buffer[j++] = String[i++]
// Si hay carateres en Buffer[], imprimir.
// Agregar terminador de fin de cadena.
Buffer[j] = '\0'
// Imprimir en VirtualHMI Buffer[] e incrementar numero de línea.
nLcdPrintf(x, y++, LCD_CLRLINE, "%s", Buffer)
// Agregar Delay para asegurar fin de transmisión.
DelayMS(150)
// Fin de caracteres en String[] ?.
if(String[i] == '\0')
// Si, parar Loop.
// No, ajustar variables para próxima línea.
Virtual_MultipleLine_Version_1.zip (8.16 kB - descargado 19 veces.)
« Última Modificación: noviembre 30, 2016, 21:31:28 pm por Soporte »
« Respuesta #2 : diciembre 01, 2016, 10:26:53 am »
Este fin de semana la pongo en marcha.
Muy amable Boris.
« Respuesta #3 : diciembre 03, 2016, 14:00:00 pm »
Hola Boris, que tal ?
Estuve trabajando para implementar nLcdPrintMultLines(), lo que parecía muy simple. Si embargo, no me anduvo: siempre, invariablemente siempre, me mostraba en VirtualHMI solo la primera línea de la cadena, hasta el primer \n. Las siguientes, nunca.
Revisé el código que me enviaste, y me pareció perfecto, no pude encontrar un error.
Luego instalé el proyecto que me enviaste (que simplemente manda una cadena) ... y anduvo !
Finalmente, volví a mi proyecto, y hice unos cambios mínimos en el código de nLcdPrintMultLines, y con eso anduvo: le puse el incremento de la variable "y" al final, en lugar de hacerlo dentro de la instrucción nLcdPrintf(), y le saqué el delay.
No entiendo bien por qué esto fue la solución, pero ... ahora funciona. Ver código como quedó a continuación:
new Buffer[160]
while(String != '\n' && String != '\0')
nLcdPrintf(x, y, LCD_CLRLINE, "%s", Buffer)
// Fin de caracteres en String[] ?
if (String == '\0')
Por ahi no creo que sea muy complejo hacer que nLcdPrintf soporte \n y con eso todo sería más sencillo
Por otro lado te cuento que todo esto lo estoy haciendo para poder mostrar en VirtualHMI los "emails históricos" que voy enviando como notificaciones (como si fuera un LOG de los emails enviados), puesto que siempre tuve muchísimos problemas con los emails que no salen. Con motivo de estas pruebas, pude determinar mejor la casuística de los problemas con los emails, que te la cuento a continuación por si se te ocurre la causa del problema:
- Luego de enceder el PLC o de enviarle un comando de RESET, el primer email de power-on sale perfectamente.
- El email que sigue a continuación (varios minutos después), 99% de las veces no sale, y SmtpStatus termina en -1
- Si ahora le envío un RESET, sale el siguiente mail (obviamente no es el mail "anterior" que falló, sino un "nuevo email" que genero en cada power-on)
Mi función de envío de emails es prácticamente idéntica a la sugerida en la nota de aplicación, utilizando SendMail= para iniciar la transmisión, Sending= para indicar que está en curso un envío, y SmtpStatus con el resultado final.
Y estoy usando SmtpInitSimple().
Probé a poner SmtpInitSimple "CADA VEZ" que voy a enviar un email, pero nada cambió.
« Última Modificación: diciembre 03, 2016, 14:01:34 pm por PabloGa »
« Respuesta #4 : diciembre 05, 2016, 12:17:53 pm »
Buenos días Pablo, te contesto a continuación:
Cita de: PabloGa en diciembre 03, 2016, 14:00:00 pm
No entiendo bien por qué esto fue la solución, pero ... ahora funciona. [...]
Tiene pinta como que es algo relacionado al proyecto, alguna variable o algo esta interfiriendo.
¿Tendrás una variable "y" declarada como global antes?.
Si, es probable que lo implemente en VirtualHMI para que lo procese directamente ya que es mas eficiente para evitar transmisiones.
Creo que puede ser el mismo caso que el anterior, algo que interfiere en el proyecto.
Por ejemplo buffers de memoria excesivamente grandes u otra conexión (podrías habilitar que solo envies mail, sin otras conexiones a Thingspeak, VirtualHMI, etc).
Una forma seria que pruebes el ejemplo de la nota de aplicación por separado.
Te paso un ejemplo que envía mail cada 120 segundos aproximadamente. Lo he estado probando
y envía los mails sin problemas uno a continuación del otro.
Solo debes configurar el mail de destino y remitente para usarlo.
Fijate si podes hacer la prueba por separado para despejar la duda de si es otra cosa.
MailAutoSender_Version_4.zip (9.3 kB - descargado 18 veces.)
« Respuesta #5 : diciembre 05, 2016, 12:50:56 pm »
1) Con respecto a nLcdPrintMultLines, yo también pensé que habría alguna interacción con alguna variable global del proyecto, pero no, no existe tal cosa, o al menos no la encontré. De todas formas, con el pequeño cambio que le hice, como te comenté, ya está funcionando ok.
2) Sería perfecto si lográs hacer que nLcdPrintf() procese \n !
Si esto te implica modificar VirtualHMI, acordate de que en VirtualHMI Android existía una limitación en cuanto a la impresión de strings: VirtualHMI Android no puede presentar strings "largos" (creo que el límite estaba en unos 34 caracteres por línea). Mi sugerencia en su momento era hacer que cuando se imprimía un string largo, al llegar al final derecho de la pantalla, simplemente continúe en la linea siguiente. Y de esa manera, que VirtualHMI muestre el string "completo". Si vas a sacar un nuevo release, quizá se podrían hacer las 2 cosas.
3) Con respecto al envío de emails: estoy seguro de que si instalo el script de envío periódico de emails van a salir perfectos. Con certeza tengo algún tipo de interacción en el uso del recurso Ethernet/TCP, que me trae el problema. Estoy usando ModBus, recepción de datos de sensores mediante paquetes UDP, envío de datos a ThingSpeak, VirtualHMI y envío de emails !! El tema es ... cómo encontrar dónde está la interacción ? No se ni por donde comenzar.
No creo que sea una interacción a nivel de variables, porque soy muy cuidadoso con eso, y para cada cosa tengo variables específicas (no "reusadas"). En fin, algún día me pondré a "separar" y ver cuál puede ser el factor que ocasiona este problema.
« Respuesta #6 : diciembre 06, 2016, 10:30:06 am »
Cita de: PabloGa en diciembre 05, 2016, 12:50:56 pm
Si, los tendré en cuenta para la actualización ya que es bastante útil.
En una oportunidad me comentaste que tenias problemas con ModBus usando una aplicación Android creo.
A partir de eso te sugerí usar el comando:
// Desactivar TCP split en stack TCP/IP.
NetTcpSplitOff()
Intenta comentar esa línea (sin usar ModBus que te daba falla) y probar los emails, y resto.
Si tenes arrays de variables globales muy grandes y que no se usan continuamente, podes colocarlo dentro de funciones, los cuales se crearían
cuando la función es llamada, pero no permanecerían de forma continua en memoria. Esto es para bajar la cantidad de memoria, si es que usas mucho.
« Respuesta #7 : diciembre 11, 2016, 19:29:16 pm »
Estoy posteando en un hilo que no es el adecuado, pero de todas formas aquí veníamos con el tema...
Le he dedicado un buen tiempo a probar el tema de la interacción entre Modbus / ThingSpeak / Email para dilucidar la cuestión, y creo que he (si bien no lo pude resolver), tengo buena información para rastrear el problema:
Esta es la secuencia de las pruebas que hice:
1) Probé a comentar la sentencia NetTcpSplitOff(), tal como me sugeriste en tu anterior post. Nada cambió: el primer email sale bien, y los subsiguientes siguen sin salir.
2) Probé a colocar NetTcpSplitOff() "al comienzo", de mi proceso plcmain(), ya que lo estaba poniendo "en medio" de todas las inicializaciones de los distintos servicios. Por las dudas decidí probar a poner esa instrucción antes. Lo que me encontré es que tira un error de compilación "Invalid Expression, assumed zero", si coloco NetTcpSplitOff() antes de VirtualHmiInit() (what?!?). O sea: si no está primero la sentencia VirtualHmiInit() en el código fuente, NetTcpSplitOff() tira error. Si la pongo un renglón después, compila normal. De todas formas, este último problema no tiene nada que ver con el tema que estoy buscando resolver, que es la no salida de los emails; igual te lo comento, porque ahí me parece que hay un bug en el compilador STX Ladder.
3) Luego probé a comentar la sentencia ThingSpeakInit() que tengo en PlcMain() o sea inutilizando la salida a ThingSpeak ... y con esto los emails empezaron a salir normal !
4) Para seguir acorralando el tema, volví a habilitar ThingSpeakInit() en PlcMain(), pero comenté la instrucción que ejecuto una vez por minuto ThingSpeakUP(). Esta función lo que hace es armar el string y lo sube a ThingSpeak. Los emails anduvieron !
5) Pensando en una interacción entre SendMail() y ThingSpeak, hice que la subida a ThingSpeak NO se realice si en ese momento la variable Sending==1, o sea si hay un email "saliendo" en curso (cada mail tarda unos 10 segundos en ser enviado). Los emails dejaron de salir a pesar de esto.
6) Volví a inhabilitar ThingSpeakUp(), que quedó como único cambio, y los mails salen perfectamente.
Y hasta aquí llegué. La conclusión a la que llegué es que CON SOLO UNA VEZ que ese ejecute mi función ThingSpeakUP(), los emails dejan de funcionar. Momentáneamente, dejé inhabilitada la subida con ThingSpeakUp() para re-reconfirmar que los emails funcionan perfecto. Hice unas docenas de pruebas, y todos saliendo correctamente.
Transcribo a continuación mi función ThingSpeakUP(), para que la veas. No tiene nada "raro".
En mi opinión, hay alguna interacción en el firmware, que hace que cuando se hace una subida a ThingSpeak, algun flag o variable del firmware queda mal seteada, y eso hace que los emails siempre se aborten por timeout (error= -1):
ThingSpeakUP()
// Comprobar si no hay conexiones en curso con Thingspeak.
if(ThingSpeakSending == 0)
// Limpia las variables con el string que se enviara a ThingSpeak:
ThingSpeak_String1 = ""
ThingSpeak_String2 = ""
ThingSpeak_String3 = ""
ThingSpeak_String4 = ""
ThingSpeak_String = ""
// aquí armo 4 strings con los conjuntos de "fields". Luego se concatena todo para enviarse a ThingSpeak()
StrFormat(ThingSpeak_String1, 50, false, "field1=%d.%d&field2=%d.%d&field3=%d&", Var1,Var1a,Var2,Var2a,Var3)
StrFormat(ThingSpeak_String2, 50, false, "field4=%d&field5=%d&field6=%d&",Var3,Var5,Var6)
StrFormat(ThingSpeak_String3, 50, false, "field7=%d.%d&", Var7,Var7a)
StrFormat(ThingSpeak_String4, 50, false, "field8=%d", Var8)
// Concatena los 4 segmentos
StrFormat(ThingSpeak_String, 100, false, "%s%s%s%s", ThingSpeak_String1,ThingSpeak_String2,ThingSpeak_String3,ThingSpeak_String4)
// Aquí hace la subida de la info a ThingSpeak
if(HttpSendGet("/update?key=%s&%s", THINGSPEAK_APIKEY, ThingSpeak_String) == 0)
// Marca que hay un envío en curso
ThingSpeakSending = 1
« Respuesta #8 : diciembre 12, 2016, 21:29:40 pm »
Cita de: PabloGa en diciembre 11, 2016, 19:29:16 pm
Esto me parece que es el mismo problema que paso una vez con SmtpInitSimple().
Se trata de un error de declaracion interno, que seguro se propagó al basarme en la declaración erronea.
Lo solucionaré para la próxima revisión de StxLadder.
Voy a armar una prueba con envío de Emails y ThingSpeak al mismo tiempo, así compruebo el error.
Me fijo y te aviso que encuentro.
Gracias por tomarte tu tiempo en analizar el problema!
« Respuesta #9 : diciembre 30, 2016, 08:19:17 am »
Pudiste reproducir el problema ??
« Respuesta #10 : diciembre 30, 2016, 11:55:02 am »
No, este mes nos sobrepasaron los trabajos y lamentablemente no pude dedicarle tiempo al ensayo.
Lo tengo presente igual, cuando vuelva del receso por vacaciones continuo con el asunto.
¡Buen inicio de 2017!
« Respuesta #11 : enero 05, 2017, 20:54:58 pm »
Se que estás de vacaciones, asique este mensaje lo mando "para el día depués"...
1) Creo que encontré el problema de la "incompatibilidad" entre el envío de mails, y ThingSpeak.
En realidad es una incompatibilidad entre el envío de emails, y cualquier uso que se haga de http que implique la resolución de un nombre de dominio.
El problema pasa cuando el PLC hace una resolución de un nombre de dominio. Por ejemplo: si le hacés resolver el nombre "api.thingspeak.com" en algún momento del programa (como por ejemplo cuando vas a subir datos a ThingSpeak), a partir de ese momento, no resuelve más los nombres de dominio de los emails.
Probé a cambiar api.thingspeak.com por su número de IP: "184.106.153.149", y aparentemente ahora funciona. Obviamente que esto no es solución definitiva, porque los IP cambian, pero al menos sirve para encontrar el problema.
2) El problema también se me presentó porque empecé a enviar datos a un servicio que se llama Pushingbox.com (api.pushingbox.com, MUY interesante). Allí también hay que ejecutar una URL tipo http://api.pushingbox.com?blabla, y esta funcionalidad TAMBIÉN me provocaba que los emails no salgan. Por eso comencé a atar cabos...
3) El problema descripto más arriba en el thread sobre el error de compilación relacionado con "NetTcpSplitOff()" continua vigente, aunque no es un problema.
4) He encontrado que no es 100% confiable el resultado retornado por la función HttpSendCheckValidTransaction(). Hay veces que la transacción se hizo correctamente y retorna "0". Otras veces también se hizo correctamente, y retorna "1".
5) Finalmente, cómo puedo hacer para imprimir el valor de una variable que tiene adentro un número de IP?? Por ejemplo, si uso nLcdPrintf(0,01,0,"THINGSPEAK IP= %X", ThingSpeakIP), me muestra todo en hexadecimal, en vez del formato "184.106.153.149". Hay alguna función que ya haga este formateado??
Saludos y felices vacaciones !
« Respuesta #12 : febrero 04, 2017, 17:48:43 pm »
No sé si habrás tenido oportunidad de leer mi post inmediato superior.
He podido reproducir con bastante certeza dónde está el problema de las funcionalidades que dependen de internet (emails y http). El problema está en la resolución de nombres de dominio. Una vez que una de las 2 funcionalidades hizo la resolución de nombre, "la otra" queda sin funcionar.
Espero que puedas resolverlo. Muchas gracias !
« Respuesta #13 : febrero 05, 2017, 21:26:43 pm »
Efectivamente, hay un tema de resolución de nombres según estoy probando. Cuando envío mails y uso IP para Thingspeak, ambos funcionan bien.
Si uso dirección para Thingspeak, los mails salen, pero deja de funcionar Thingspeak.
Aún no encuentro el error en el código, pero ya lo estoy analizando.
En cuanto tenga la solución, te aviso.
Lo que podes hacer es convertir cada byte de la variable de 32-bits por separado:
nLcdPrintf(0,01,0,"THINGSPEAK IP= %03d.%03d.%03d.%03d", (ThingSpeakIP>>24) & 0xFF, (ThingSpeakIP>>16) & 0xFF, (ThingSpeakIP>>8) & 0xFF, ThingSpeakIP & 0xFF)
Eso esta solucionado, pero aún no subí el nuevo StxLadder para descargar.
Para ThingSpeak?.. este método comprueba que el código de estado a nivel protocolo HTTP sea 200, pero un servidor puede retornar el otro código de estado como valido.
Igual, habria que comparar con HttpSendGetResponseCode() para ver que retornó el servidor.
Gracias por tus pruebas y tiempo que son muy valiosas !
Te mantengo al tanto, espero poder solucionarlo a la brevedad.
« Última Modificación: febrero 07, 2017, 20:09:07 pm por Soporte »
« Respuesta #14 : febrero 07, 2017, 20:05:24 pm »
Te adjunto la versión de firmware 223 para el STX8081-D1 que corrige la resolución de nombres usando SMTP y HTTP send.
No es necesario modificar el código de tu proyecto, solo actualizar el firmware.
Por otro lado, si envías un MAIL o petición HTTP al mismo tiempo, es probable que alguno de los clientes tenga ocupado el resolvedor de nombres, al estar intentando obtener la dirección para obtener la IP. En este caso, gana el que primero lo este usando.
En esa situación, tanto las funciones SmtpMailSend() o HttpSendXXX(), retornarán el código "-5" si encuentran el resolvedor de nombre ocupado.
Igual es poco probable, porque la resolución de nombre se hace rápido, pero puede depender del trafico de red y tardar algunos segundos.
Es para tener en cuenta, en caso de encontrar ese código de error, deberías intentar llamar nuevamente a la función de envío, ya que el resolvedor de nombres podría haberse desocupado.
También la librería retorna un código de error con las funciones xxxGetStatus() cuando no se ha podido resolver el nombre.
Esto también estaba especificado antes, así que si ya lo hacías, no cambia.
Luego NetTcpSplitOff() será corregido cuando suba la versión 1.8.2 de StxLadder.
Probalo y avisame si quedo todo bien.
stx8081-d1-v223.sff (178.14 kB - descargado 15 veces.)

References: resolución 
 resolución 
 resolución 
 resolución 
 resolución 
 resolución 
 resolución