Trabe ya no escribe aquí. Puedes encontrarnos en nuestra publicación en Medium: medium.com/trabe.

4Trabes Historias de una empresa en 100 metros cuadrados

El blog de Trabe Soluciones

Extraer datos de un PLC (autómata) Siemens S7-200 utilizando un cable USB y Excel

| | Comentarios

Los autómatas programables son unos aparatos muy interesantes de los que aún no habíamos hablado nunca en este blog. Permiten controlar todo tipo de procesos más o menos complejos y almacenan información que luego puede ser interesante tratar en un PC. Sin embargo, muchas veces no resulta trivial hacer que esta información que maneja el autómata sea exportada a un PC. A la hora de extraer datos de la memoria de un autómata existen varias posibilidades.

Programar un protocolo específico

Esta opción es bastante laboriosa y presenta el problema de tener que reinventar la rueda en cada escenario de implantación. Sin embargo es la que da una mayor flexibilidad, y puede ser la única opción válida cuando nuestro PLC interactúa con otros dispositivos hardware

Utilizar una librería de comunicación

Existen en el mercado diferentes librerías para acceder de una forma simplificadada a los datos de un autómata. Estas librerías nos evitan tener que pelearnos con los protocolos de comunicación y nos presentan una vista de más alto nivel. Siemens dispone de sus propias librerías (de pago, como todo lo que tenga que ver con PLCs Siemens), pero existen otras opciones muy interesantes como la librería libnodave. Utilizar una librería es una opción muy razonable cuando necesitamos acceder a los datos del autómata desde nuestros programas y no queremos/no podemos utilizar un servidor OPC.

Utilizar un servidor OPC

Hace ya algún tiempo, la industria comenzó a demandar a los fabricantes de sistemas de automatización una cierta tasa de interoperabilidad. OPC permite controlar a través de un PC dispositivos de distintos fabricantes. En el caso de los S7-200, existe un software de Siemens denominado “PC Access” que viene siendo un “mini servidor” OPC. Utilizando este software es bastante sencillo acceder a los datos del PLC desde Excel utilizando VBA. Dado que esta solución es estándar, sencilla y funciona, será la elegida en este caso.

Configurar el ordenador

Esta parte del problema es bastante sencilla. Basta con instalar el software de Siemens y seguir los pasos descritos para instalar el componente en excel. Una vez realizados correctamente estos pasos, tendremos una barra de botones adicional en Excel que nos permite iniciar/detener el refresco de datos. En este ejemplo no vamos a utilizar estas herramientas, pues recuperaremos los datos directamente desde código VBA

¿Qué datos vamos a recuperar?

Los autómatas de la serie 200 tienen varias “zonas” de memoria, pensadas para distintos propósitos. Hay una zona de flags, una zona de datos de usuario, etc. Todas las zonas son accesibles a través de OPC, tanto en modo lectura como en modo lectura/escritura (cuando procede). En este ejemplo vamos a recuperar 100 valores de un tamaño de un byte almacenados en la memoria V, entre las posiciones 0 y 99.

Para conseguir nuestro propósito tendremos que hacer un bucle en el que se vayan leyendo las posiciones necesarias e iremos almacenando los valores en la columna A de nuestra hoja de cálculo, de modo que en la la fila de número i tengamos almacenado el valor correspondiente a la variableVBi-1

Cómo lo hacemos

Muy sencillo. Una vez configurado el equipo abrimos una nueva hoja excel y cargamos el “complemento” de siemens tal como indica la documentación. Sobre una hoja en blanco creamos un botón y asociamos el siguiente código a su evento de click:

1
2
3
4
5
6
7
MsgBox "Comenzando el proceso de importación de datos"
For i=0 To 99
    parameters = "2,VB" & i & ",BYTE,R"
    sStr = Excel.Application.Run("OPCS7200ExcelAddin.XLA!OPCRead", parameters, "")
    Cells(i+1,1) = sStr
Next i
MsgBox "Proceso finalizado"

El código es tan sencillo que no merece mayor explicación. Únicamente comentar que en la linea en que se asigna valor a parameters se le indica al servidor OPC con que dispositivo queremos “hablar”, que posición queremos leer, el tipo y si queremos utilizar la posición en modo lectura o lectura/escritura

Conclusiones

Hemos visto que los autómatas no son máquinas infernales con las que es imposible hacer cosas. Es fácil obtener datos de
ellos si sabemos cómo hacerlo. Obviamente recuperar los datos es solo el principio del problema, pero es la parte que puede parecer más compleja cuando nos hablan en abstracto de este tipo de dispositivos.

Generación dinámica de librerías Javascript internacionalizadas con GetText

| | Comentarios

La internacionalización es uno de los requisitos del proyecto que estamos desarrollando actualmente en Trabe Soluciones con Ruby on Rails. Para cumplirlo estamos utilizando GetText y Ruby GetText para la internacionalización de cadenas y código propio para internacionalizar ActiveRecord (un enfoque habitual cuando uno se enfrenta a este problema).

La aplicación además, cuenta con una adminsitración que, por su interfaz, utiliza una cantidad considerable de Javascript. Dicho Javascript también debe estar internacionalizado. Lo primero que se nos puede pasar por la cabeza es separar las cadenas que requieran traducción a unos ficheros separados y que nuestro layout enlace uno u otro en función del idioma de la petición (o el idioma preferido del usuario). Si lo hacemos así, estamos desaprovechando todas las ventajas que nos aporta el uso de GetText, en especial las facilidades para gestionar la traducción de cadenas (utilizando herramientas como poEdit).

Lo que vamos a hacer es generar los Javascripts dinámicamente utilizando Ruby embebido, como un rhtml cualquiera, con lo que podremos incrustar nuestras llamadas a GetText con total impunidad. Resumiendo, que lo que queremos es utilizar algo como ésto en nuestro layout:

1
<%= javascript_include_tag "/dyn_javascripts/#{Locale.get}/application" %>

Para ello necesitamos un poco de código.

Creamos un controlador que responda a las peticiones y que simplemente delegue en la vista correspondiente. Estas vistas estarán en la carpeta app/views/dyn_javascripts y tendrán la extensión .djs (por ejemplo).

1
2
3
4
5
6
7
8
9
10
class DynJavascriptsController < ApplicationController

  layout false

  def method_missing(symbol, *args)
    if symbol.to_s =~ /(.*)\.js$/
      render :action => $1
    end
  end
end

Añadimos la ruta correspondiente al fichero de rutas (routes.rb). En ella pasamos el idioma como parametro porque nuestros filtros de GetText lo buscan en la URL, no por vicio.

1
map.connect 'dyn_javascripts/:language/:action.js', :controller => 'dyn_javascripts'

Ahora creamos un fichero que requeriremos al iniciar la aplicación (en el fichero environment.rb). Este fichero incluye una clase que procesa nuestras plantillas .djs y que debemos registrar con el métodod register_template_handler de ActionView::Base::. Además incluimos el código necesario para que la aplicación encuentre el controlador que hemos definido previamente (la última línea de código) de tal modo que se recargue con cada petición en modo desarrollo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require 'erb'

module DynJavascripts
  class View

    def initialize(action_view) #:nodoc:
      @action_view = action_view
    end

    def render(template, local_assigns={})
      @action_view.controller.headers["Content-Type"] = 'text/javascript'
      @action_view.instance_eval { ERB.new(template).result(binding) }
    end

  end

end

ActionView::Base::register_template_handler 'r_js', DynJavascripts::View

# Nuestro controladoe está situado en 
# lib/common/dyn_js/dyn_javascripts_controller.rb
Dependencies.load_paths << "#{RAILS_ROOT}/lib/common/dyn_js"

Hecho todo esto ya podemos escribir código simpático como el siguiente en nuestra carpeta app/view/dyn_javasctripts:

1
2
3
4
5
6
# Archivo app/views/dyn_javascripts/application.djs
function changeALink(whichLink) {
    var the_link = document.getElementById(whichLink);
    the_link.href =  "<%= url_for :controller => 'a_controller'  %>";
    the_link.innerHTML = "_('Some text')";
}

El último detalle es conseguir que Ruby-GetText extraiga las cadenas de nuestros ficheros .djs. Para ello, vamos a indicarle al parser de rhtml que “sabe” procesar nuestros ficheros djs. La tarea “Rake”: de actualización de los ficheros .po quedaría tal que así:

1
2
3
4
5
6
7
8
9
10
11
12
13
desc "Update pot/po files."
task :updatepo do
  require 'gettext/utils'

  # 
  # ¡¡¡ Abracadabra!!!
  # 
  GetText::ErbParser.instance_variable_set(
      '@config', :extnames => ['.rhtml', '.r_js'])

   GetText.update_pofiles("app_domain",
   Dir.glob("{app,lib,bin}/**/*.{rb,rhtml,rjs,r_js}"), "App 1.0.0"")
end

Sencillo, ¿no?

Instalando Fedora (nunca más Core) 7 en un Dell Dimension C521 o el infierno de la ATI X1300

| | Comentarios

Hace menos de una semana hemos renovado el parque de equipos de Trabe. Nos hemos decantado por unos Dell Dimension C521 con la siguiente configuración:

  • AMD Athlon X2 64 4400+
  • 300GB de disco duro
  • 2GB de memoria
  • Tarjeta gráfica ATI X1300 256MB
  • TFT 22" (1680×1050) + TFT 17" (1280×1024)

Los equipos, en cuanto que “electrodomésticos”, son una maravilla: pequeños (dento de un orden), silenciosos, e incluso, si se me apura, bonitos (aunque esto es para gustos, claro). Pero el problema de los ordenadores es, precisamente, que no son electrodomésticos (me estoy desviando del tema, así que lo dejo para otro post).

A lo que iba. Los equipos traen un Windows Vista Bussiness preinstalado. Un operativo como otro cualquiera, que tarda mucho en arrancar y en cerrarse. Y poco más puedo decir sobre el Vista, porque lo primero que hemos hecho ha sido instalar una distribución linux, una Fedora 7 para ser más concretos. Obviando los pasos básicos de la instalación, y dejando a un lado algún que otro problema puntual con aplicaciones concretas (léase Firefox, léase Adobe Acrobat Reader, léase Eclipse), sí me gustaría destacar lo complicado que resultó poner a funcionar el escritorio (KDE algunos, GNOME otros) en los dos monitores a las resoluciones adecuadas. Por si alguien se encuentra con el problema, aquí queda enlazada una solución, que pasa por parchear el driver cambiando unos cuantos bits aquí y allí… algo muy natural, evidente y nada complicado ¬¬. Tras seguir los pasos indicados en el post citado, el escritorio funciona perfectamente (si dejamos a un lado unos extraños parpadeos o flashes que se producen cada n minutos), extendido a ambos monitores, como debe ser.