Plantillas de proyectos de programa para hardware real
Aquí en La-Tecnologia generalmente proyectamos hacks que residen en el mundo real. Sin embargo, estoy convencido de que algunas de las hazañas más interesantes están bajo el poder de lo que es posible en los programas, no en el hardware. Sin el software adecuado, ninguna impresora 3D podría imprimir y ningún quad podría volar. El código fuente que impulsa estas máquinas puede necesitar meses de refinamiento para crear su estructura. En una palabra, estos paquetes de software son complicados y no suceden de la noche a la mañana.
Así que cómo faru Ellos pasan; mejor: ¿cómo podría ni ¿Sucederá eso? ¿Cómo escribimos un programa lo suficientemente flexible para coexistir pacíficamente con todo tipo de variaciones de hardware?
Lo que separa los grandes proyectos de software de código abierto como ROS, LinuxCNC y Multiwii del código de hackathon occidental, son los principios fundamentales que gobiernan cómo se escriben. En la programación orientada a objetos, estos principios se denominan patrones de diseño. En las siguientes publicaciones, romperé la portada de algunos de estos. Además, los empaquetaré en ejemplos del mundo real para que podamos ver estos patrones interactuar con hardware real. La próxima vez que abra el código fuente para un gran proyecto de hardware de código abierto, espero que pueda generar algunas de estas plantillas en el código. Mejor aún, espero que traiga estas plantillas a su próximo proyecto de robot y máquina. Empecemos.
Para facilitar la lectura, todos los ejemplos funcionan en Python3. Los siguientes fragmentos están truncados por brevedad, pero los ejemplos reales en el repositorio funcionarán si tiene un hardware similar.
Programar Profetas de antes
Antes de saltar, tengo que prometerle que estas plantillas de proyecto no me pertenecen. De hecho, la mayoría de ellos tienen décadas de antigüedad. Si tiene curiosidad, mire el libro: Plantillas de diseño, escrito por autores cuyos nombres son tan difíciles de recordar que los llamaremos colectivamente los "Cuatro Grandes". Para muchos ingenieros de software, este libro es la referencia completa para las plantillas de diseño de objetos y es sólido. Pensé en entregar esta publicación como una reseña de un libro en la que me babearía por todas las elegantes plantillas de programación que podrías poner en tu próximo proyecto de blink. ¿El problema? ¡Sin anteojeras! De hecho, todo el libro crea ejemplos en torno a la escritura de un editor de documentos gráficos. Para nosotros, los monos de dispositivos, ¡eso es aburrido y se limita a inaceptable!
Con esto en mente, presentaré las palabras de sabios adornadas con el tratamiento adecuado de hardware real.
La mayoría de estos ejemplos
Ahora, para la pregunta sin respuesta: ¿para quién son estos ejemplos? Los lectores de La-Tecnologia tienen todos los gustos. Al escribir este artículo, espero unirme a dos comunidades: la gente del software que está ansiosa por participar en el hardware y el hardware que desea escribir un programa mejor. Para la gente de programación, estos ejemplos están aquí para mostrarle lo que ya sabe y darles un contexto de hardware real. Para el hardware, estas plantillas de proyecto están aquí para mejorar su conjunto de técnicas de programación con sensores que probablemente haya visto antes.
Para establecer la línea de base para aprovechar al máximo lo que vendrá a continuación, asumiré dos cosas. (1) Ha probado la programación orientada a objetos escribiendo algunas clases en Python y (2) comprende los conceptos básicos de la herencia de clases. Construiremos tipos de herencia más avanzados en los siguientes ejemplos.
Siéntete cómodo con el hardware
Hoy los calentaré con nuestras plantillas de proyectos leyendo varios sensores de temperatura. ¿El plan? Necesito una base de código que me brinde una manera de leer fácilmente dos tipos de sensores de temperatura diferentes: un termopar y un termistor. Las ideas clave detrás de esto son tres:
- aislar el comportamiento que pueda sostenerse por sí solo.
- ocultar (con abstracción) los detalles que son innecesarios para el usuario final.
- no te repitas a ti mismo; compartir comportamientos siguiendo la primera cláusula.
- aislar los comportamientos específicos del dispositivo necesarios para leer un sensor especial.
- proporcionan una interfaz de programación común para leer la temperatura independientemente del tipo sensible a la temperatura.
Al principio, puede parecer que solo estamos escribiendo algunos controladores para leer algunos sensores de temperatura. De hecho, ¡tenemos que pensar en un nivel superior! Preguntémonos, "¿cuál es la característica principal que proporciona nuestro software? ¿Cómo podría alguien más con un diseño diferente adaptar este código para obtener la misma característica principal sin tener que reescribirlo?" En nuestro caso, proporcionamos una forma común de realizar temperatura: una medida con una vía de expansión a otros sensores de temperatura.
Sin más preámbulos, presento los arreglos de hardware de hoy.
Configuración de termopar
La primera configuración es un Beaglebone configurado para leer un convertidor analógico-digital incomprensible basado en I2C que lee un amplificador de termopar.
Configuración del termistor
La segunda configuración es que Beaglebone lee el mismo convertidor analógico-digital basado en I2C, que ahora lee un divisor de voltaje con un termistor.
Teniendo en cuenta el hardware real, analicemos ahora nuestras plantillas de proyecto mientras construimos una base de código flexible para leer ambos sensores.
Polimorfismo
Comenzamos excitando los objetos subyacentes que conformarán nuestro sistema. Primero, tenemos dos sensores del mundo real separados que requieren una forma diferente de leerse como entrada. Más adelante también sabemos que, aunque estos sensores requieren disposiciones ligeramente diferentes, ambos miden lo mismo: ¡temperatura!
Con eso en mente, anotemos algunos requisitos. Digamos que necesitamos:
Teniendo en cuenta los requisitos, parece que queremos escribir dos clases separadas, cada una con un comportamiento específico para leer datos de temperatura. También parece que debemos asegurarnos de ser consistentes en las convenciones de nomenclatura para leer la temperatura. Digamos que escribimos una clase para cada sensor.
class Thermistor: ... def read_temperature_c(self): # thermistor-specific details here
class AD8495ThermocoupleAmplifier: ... def read_temperature_c(self): # thermocouple-specific details here
Aquí nos burlamos de algo que hace precisamente eso. El comportamiento específico del dispositivo se agrupa en clases y todos nuestros métodos de lectura de temperatura tienen el mismo nombre. Hemos terminado, ¿no es así?
Si bien esta idea funciona, es un poco ingenua. En el código anterior, no tenemos un contrato de proyecto para dar a estos métodos el mismo nombre. Diablos, podríamos llamar a uno tiempo de lectura y otro Hola madrey nadie puede detenernos, ¡ni siquiera nuestras madres! Afortunadamente para nosotros, podemos usar una plantilla de proyecto para asegurarnos de que estos métodos sean idénticos.
En cuanto a nuestro problema, tenemos dos tipos diferentes de sensores de temperatura, pero, en realidad, solo quiero una forma coherente de preguntar la temperatura de cada uno de ellos. Este es un polimorfismo con clases básicas abstractas. El polimorfismo es un patrón proyectivo que promete una interfaz común para entidades que pueden no ser todas iguales. Aquí tenemos tres clases de sensores diferentes. El polimorfismo puede garantizar que todos utilicen el mismo método para leer la temperatura.
Para implementar el polimorfismo, usamos una clase base abstracta. Una clase base abstracta es un tipo especial de clase base que no puede sostenerse por sí sola. Eso necesidades heredota. De hecho, si intentamos crear la clase base por sí mismo, Python arrojará un error. Las clases básicas abstractas tienen una característica adicional adicional: métodos abstractos. Estos son métodos de mantenimiento de posición establecidos en la clase base abstracta que no hacen nada. En su lugar, actúan como etiquetas y obligan a la clase derivada a definirlas. Si intentamos crear una clase secundaria a partir de la clase base abstracta sin definir todos los métodos abstractos, Python también arrojará un error.
Con dos nuevas formas de lanzar errores, vale la pena preguntarse, "¿cuál es la ventaja de limitarnos así?" Resulta que estos límites son en realidad una forma de hacer una promesa. Aquí prometemos que cada clase secundaria que escribamos para nuestros sensores de temperatura definirá algunos métodos abstractos en la clase base.
Dado que ambos sensores de temperatura son solo eso, una especie de temperatura, usaremos TemperatureSensor como nuestro nombre de clase base.
from abc import abstractmethod, ABCMeta # abstract base class library tools class TemperatureSensor(object, metaclass=ABCMeta): ... @abstractmethod def read_temperature_c(self): """ returns the temperature in Celsius """ pass
Arriba está la forma de Python3 de definir una clase base abstracta. Aqui esta el metodo read_temperature_c es solo un marcador de posición. No necesitamos definir qué hace aquí, porque los detalles son específicos de nuestros sensores. Aquí simplemente lo declaramos y lo decoramos con el @abstraktametodo decorador para decirle a Python que cualquier clase secundaria de TemperatureSensor deber definir el read_temperature_c método.
Para concluir este ejemplo, hagamos que nuestra clase Thermistor herede nuestra clase base abstracta:
class Thermistor(TemperatureSensor): ... def read_temperature_k(self): """ returns the temperature in Kelvin """ voltage_v = self.voltage_input.read() r1_ohms = self._read_resistance(voltage_v) return (1/298.15 + 1/self.b * log(r1_ohms/self.thermistor_ohms))**(-1) def read_temperature_c(self): return self.read_temperature_k() - 273.15
Primero observe que nuestra clase Thermistor hereda nuestra clase TemperatureSensor. Les decimos a nuestros usuarios: "Oye, esta es una clase de termistor, pero puedes leerla como un sensor de temperatura". También nos dice, los escritores, "oye, prometo definir un read_temperature_c método. "Finalmente escribimos todo el código necesario para leer un termistor y convertirlo a grados Celsius. ¡Y aquí está!
Demos un paso atrás y consideremos por qué esto es poderoso. Nuestro usuario puede ser alguien que puede no saber nada sobre cómo funcionan los termistores, pero aún así pueden ser medidos por el general read_temperature_c método. Encapsular información específica del dominio es exactamente lo que queremos hacer aquí, por lo que nuestros usuarios no necesitan conocer todos los detalles de nuestro software para que algo funcione. Aquí nuestro código oculta los detalles desordenados de la conversión de la resistencia a la temperatura de una línea apropiada polinomial. Al ocultar esos detalles, les permitimos pasar al siguiente paso y escribir algunas aplicaciones interesantes de detección de temperatura.
Puentes
En la última sección, cruzamos una línea sin discutirlo.
voltage_v = self.voltage_input.read()
¡No tengas miedo! Cubriremos esa parte ahora.
En este punto, tenemos una interfaz común para solicitar la temperatura, pero podríamos tener docenas de formas diferentes de recopilar los datos sin procesar. ¿Cómo escribimos una clase única que encapsule adecuadamente los detalles de cada sensor de temperatura sin preocuparnos demasiado por los dispositivos posteriores necesarios para leerlos?
Para tener una mejor idea del problema, veamos nuevamente un diagrama de bloques de nuestro hardware físico:
Pero esa no es nuestra única configuración posible. Debido a que uso Beaglebone en estos ejemplos, también podría usar el ADC integrado en el propio chip BeagleBone ARM. Esta configuración de hardware podría verse así:
Lo que necesitamos escribir es una serie de clases para nuestro diseño que de alguna manera desatar el termopar del hardware adicional utilizado para leerlo. Para hacer esto, necesitamos decidir qué elementos se necesitan específicamente para leer este termopar y qué elementos se pueden reemplazar sin cambiar el comportamiento del sistema final. En este caso, nuestro AD8495 está estrictamente conectado al termopar. ¡Sin él, simplemente no podemos leerlo en absoluto! Por otro lado, este ADS1015 es solo un convertidor de analógico a digital básico. Diablos, podríamos reemplazarlo con cualquier AD, y aún obtendríamos nuestras medidas de termopar. Es por eso que comenzaremos escribiendo dos clases: una clase que encapsula el comportamiento de AD8495 y una clase que maneja la interfaz analógica.
Ahora es un buen momento para mapear nuestro software con un diagrama de bloques para obtener una imagen del código que vamos a escribir. Cómo palabra clave: estos diagramas no están certificados como "diagramas de clases UML"; más bien, para mayor claridad, he esbozado una simplificación.
Veamos el diagrama de bloques de la izquierda. Estos cuadros representan la relación entre clases. Las cajas dentro de las cajas representan la herencia. A esto lo llamamos la relación "es". Las flechas de una casilla a otra representan un atributo de clase que resulta ser otra clase. A esto lo llamamos la relación de "tener".
En nuestro diagrama de bloques, el amplificador AD8495TCA Es TemperatureSensor porque hereda de la clase TemperatureSensor. (Aquí está el polimorfismo que describimos anteriormente). También el amplificador AD8495TCA posee VoltageInputInterface. En otras palabras, nuestro amplificador AD8495TCA tiene una referencia a otra clase por la que podemos llamar mem.tensia_enigo (mencionado anteriormente).
Entonces, ¿qué es exactamente esa clase VoltageInputInterface y cómo interactúa con el mundo real? Bueno, la respuesta corta es que depende en nuestra configuración de hardware! En la primera configuración, VoltageInputInterface es una de las cuatro entradas analógicas disponibles en el AD1015 A-to-D. En la segunda entrada, VoltageInputInterface es un solo pin en el puerto analógico expuesto de Beaglebone. Sin embargo, en todos los casos, se debe proporcionar cada interfaz de entrada de voltaje. leer un método que hace el trabajo real de leer una entrada analógica y devolver el valor en voltios. ¿Cómo podemos garantizar eso? ¡Con una clase básica abstracta como hicimos en la sección anterior! Ahora que entendemos la relación, podemos refinar el diagrama de bloques de nuestro programa con ambos arreglos opcionales.
Observe cómo, en ambos diagramas, el amplificador AD8495TCA posee una subclase diferente de VoltageInputInterface, pero los trata como un VoltageInputInterface de vainilla. Incluso si la clase inferior es diferente, todavía podemos tratar de manera idéntica utilizando la clase básica leer método.
Lo que describimos es el puente plantilla de diseño. La puente, según Band of Four, tiene como objetivo "separar un resumen de su implementación para que los dos puedan variar de forma independiente". Aquí nuestra abstracción es una única entrada analógica. Debido a que hay varias formas de proporcionar entrada analógica a nuestro Beaglebone, necesitamos una abstracción para poder tratar todo tipo de dispositivos ADC por igual.
Este patrón de diseño es bastante similar al polimorfismo, que describí por primera vez. De hecho, como antes, usaremos la biblioteca de clases básica abstracta de Python para implementarlo. La diferencia viene en nuestro caso de uso. Anteriormente, necesitábamos una forma común de tratar todo tipo de sensores de temperatura. Ahora estamos tratando de separar un sensor de temperatura de cualquier hardware que no sea específico del ajuste de temperatura.
Ahora que tenemos nuestro diagrama, podemos dividir el comportamiento sistémico entre clases. Todo lo relacionado con la conversión de voltaje a temperatura vivirá en nuestra clase de amplificador AD8495TCA. Todo lo relacionado con la lectura de nuestra entrada analógica vivirá en una clase separada.
Sin más preámbulos, escribamos nuestra clase de amplificador AD8495TCA:
class AD8495TCAmplifier(TemperatureSensor): def __init__(self, voltage_input_interface, v1_v=1.25, t1_c=0.0, v2_v=1.5, t2_c=50.0): # generate slop and y-intercept for line formula based on two data points: self.gain = (t1_c - t2_c)/(v1_v - v2_v) self.offset = self.gain * (0 - v1_v) + t1_c self.voltage_input = voltage_input_interface def read_temperature_c(self): voltage_v = self.voltage_input.read() return self.gain * voltage_v + self.offset
Tal como se describió anteriormente, nuestro amplificador AD8495TCA es solo una clase secundaria de TemperatureSensor, lo que significa que tendremos que implementar read_temperature_c método. A continuación, para crear esta clase, tenga en cuenta que pasamos una referencia a interface_engine_tension. Aquí está nuestro objeto VoltageInputInterface descrito anteriormente. Observe cómo, en el read_temperature_c método, lo llamamos leer método de salida de voltaje. (No exagere los otros parámetros: v1_v, t1_c, v2_v y t2_c. Estos son solo dos puntos de datos que podemos extraer de la hoja de datos y el voltaje de referencia del circuito que dictará la ganancia y la compensación).
A continuación, viene la clase secundaria VoltageInputInterface y ADS1015VoltageInputInterface que la hereda.
from abc import abstractmethod, ABCMeta # abstract base class library tools class VoltageInputInterface(object, metaclass=ABCMeta): ... @abstractmethod def read(self): """ returns the analog voltage in volts """ pass class ADS1015VoltageInputInterface(VoltageInputInterface): # Gain to Volts-per-bit conversion from From datasheet Table 1 volts_per_bit= { 2/3: 0.003, 1: 0.002, 2: 0.001, 4: 0.5, 8: 0.25, 16: 0.125 } def __init__(self, ads1x15, channel_index, gain=2/3): super().__init__() self.ads1015 = ads1x15 self.gain = gain self.channel_index = channel_index self.ads1015.start_adc(channel_index, gain) def read(self): raw_bits = self.ads1015.get_last_result() return raw_bits * self.__class__.volts_per_bit[self.gain]
Observe cómo ADS1015VoltageInputInterface realiza un levantamiento lo suficientemente pesado como para ocultar todos los detalles del controlador ADS1015 real solo para exponerlo con uno leer método. Eso es exactamente lo que queremos: intercambiar todos los detalles innecesarios específicos del dispositivo de nuestra entrada de voltaje a través de una interfaz simple.
Ahora solo escribimos un script de Python para "conectarlos todos", conectando nuestros objetos dependientes.
import time from object_oriented_hardware.ads1x15 import ADS1015 from object_oriented_hardware.ads1x15 import ADS1015VoltageInputInterface from object_oriented_hardware.temperature_sensors import AD8495TCAmplifier from object_oriented_hardware.beaglebone_i2c import BBI2CBus2 i2c_bus_2 = BBI2CBus2() adc_bank = ADS1015(i2c_bus_2) voltage_input = ADS1015VoltageInputInterface(adc_bank, channel_index=0) thermocouple = AD8495TCAmplifier(voltage_input) while True: print(thermocouple.read_temperature_c()) time.sleep(0.5)
¡Y aquí está! Sobre eso lo cubre durante la primera semana. Esta vez tratamos con un polimorfismo que proporciona diferentes clases con una interfaz común y puentes que separan nuestras clases de algunos elementos específicos de la implementación. Ambas plantillas hacen que nuestro código sea más generalizable y flexible en varios tipos de hardware. Si busca más contexto o desea ejecutar estos ejemplos, pruebe el código fuente. Hasta entonces, ¡sintonízate la próxima vez donde hablaremos sobre solteros y tocones!
Libro dice:
{escrito por autores cuyos nombres son tan difíciles de recordar que los llamaremos colectivamente los "Cuatro Grandes"}.
¿Se te ocurrió eso? Si es así, ¿por qué? ¡Han sido conocidos como “The Band of Four” durante mucho tiempo!
No. dice:
No, lo arruiné con Metallica, Slayer, Megadeth y Anthrax.
Cory dice:
De acuerdo, no pude dejar de leer sin comentar ... Tal vez solo enumeraste ejemplos de programas de hardware y no buenos programas de hardware cuando mencionaste las impresoras 3D. De acuerdo, sus capacidades son increíbles, y les doy crédito a los desarrolladores por eso, pero ¿la calidad del código? Atroca. Solo quería rehacer todo en unidades lógicas ... y desenredar los espaguetis. Tal vez la mención de LinuxCNC sea el buen código ... No miré eso. De cualquier manera, no llamaría a un programa de impresora 3D como bastiones de excelente código orientado a objetos usando plantillas de proyecto. Tal vez usan una gran colección de plantillas de diseño sin una plantilla de diseño arquitectónico, lo que realmente equivale a nombrar hilos espaguetis, y lo que he visto que la industria cambia a las plantillas de diseño anteriores se ha convertido en un tema realmente candente y separó a los expertos de los idiotas. Spaghetti, con cualquier otro nombre, sigue siendo spaghetti, y la última vez que verifiqué un código de impresora 3D que se ejecutaba en la impresora (el llamado firmware ... Aunque sabemos por una publicación anterior de la-tecnologia que no es realmente firmware), ¡Definitivamente era espagueti!
Ostraco dice:
Estoy esperando el patrón de tomate.
Redhatter (VK4MSL) dice:
Obtienes eso en CI cuando alguien cambia una línea de código en el espagueti y todos los paneles de resultados de la prueba se vuelven rojos.
Moryc dice:
El firmware y la programación orientada a objetos no se mezclan realmente. A menos que realmente odie a los futuros desarrolladores de un código fuente determinado. Y mientras los programadores orientados a objetos todavía están acariciando sus objetos, un mundo ha continuado moviéndose y la programación funcional es un problema ahora. Y la mayoría de esos objetos queridos seguirán todos los patrones contrarios posibles. Y lo que hacen, ni siquiera espaguetis. Los espaguetis son divertidos y deliciosos. Escriben un código que parece pelo en ese perro que vi una vez en "Animal Cops" en la televisión que básicamente se ha vuelto muy sucio, muy maloliente, sin aliento ...
fede.tft dice:
Si bien he visto a personas escribir código OOP C para microcontroladores con malos resultados, el código OOP C ++ para microcontroladores puede ser bastante bueno.
Cory dice:
Si bien estoy de acuerdo con la mayor parte de lo que dijo, uno, funcional estaba por todas partes, y "era un problema" antes de la programación orientada a objetos. C ++ se llamó una vez C (lenguaje funcional) con una clase.
Lo que probablemente nunca hayas visto es un BUEN código objeto. He visto una buena cantidad de basura y, claro, hay más basura que diamantes ... pero eso también se aplica a la programación funcional.
El comentario de espagueti refleja a mi propio hombre que genera estas plantillas de microproyectos que no son plantillas de proyectos en absoluto, pero las llaman así, por lo que también pueden ser parte del buen grupo de proyectos que usan plantillas. En su ejemplo, nombraría los pelos individuales de ese perro que vio en la policía de animales.
Lo mío es que, habiendo escrito buenos sistemas OO, marcos en los que he visto que los programadores de nivel de entrada "malos" logran ser productivos, sabrá si ha visto un buen código OO. Un buen código OO será modular, escalable y mucho más claro. Las cosas ocultas serán las cosas complejas que obstaculizan a las personas, pero no estarán ocultas detrás de un montón de código retorcido, están ocultas detrás de una interfaz, de la manera correcta, para que cualquiera que quiera entenderlo no tenga que mirar a través . 1000 clases para encontrar la pieza que están buscando, y si alguien tiene una mejor manera, puede realizarla fácilmente o implementarla para otro sistema ... No es difícil, pero hay muchos desarrolladores que nunca han experimentado nada bueno. OO, ni siquiera saben qué hacer, por lo que siguen haciendo la basura que la mayoría de nosotros vemos y en la que tenemos que trabajar.
me dice:
"Para facilitar la lectura, todos los ejemplos funcionan en Python3"
¿Pitón? ¿PITÓN? Dejé de leer ahí mismo.
Redhatter (VK4MSL) dice:
Cuando comencé el trabajo que tengo ahora, tenía un proyecto para leer datos de un medidor de energía Satec BFM usando Modbus y descartar los datos en un formato similar a CSV ... Me dieron dos opciones de idioma ...
* Python
* PHPMe consideraba mejor desarrollador de PHP y Perl que Python en ese momento, pero elegí Python ... y lo encontré un placer. Años más tarde, todavía hago un trabajo similar, y aunque también hacemos algo de NodeJS, y hago mucho C, todavía hago mucho Python. Si necesito crear un script para generar algún código C, por ejemplo, me encontraré buscando el intérprete de Python para hacerlo. De esa forma no me apresuraría a criticar.
Ostraco dice:
Quedan 2 días en la colección Humble Bundle Python. El de la robótica se ve bien.
Pierre Doucet dice:
Una cosa que recomendaría con las plantillas de proyecto es sopesar cuidadosamente si realmente las necesita, cada plantilla ha sido diseñada para abordar un problema específico, si ese problema no está presente, no use una plantilla, eso sería solo un desperdicio de hora.
También me gusta el principio YAGNI, no use una plantilla solo porque podría necesitarla en el futuro (pero hágalo si está seguro de que surgirá un problema).
Sé que es difícil decir con certeza cómo será el software en 6 meses. En el trabajo, la forma específica de clasificar los archivos del proyecto se nos impuso como obligatoria hace unos 8 años "por si", puedo decirles que este caso nunca sucedió en ningún sitio web o software de escritorio en el que estaba trabajando.Tucson Tom dice:
Todos los que conozco, un programador que no puede programar para beans, pasa tiempo en seminarios sobre plantillas de proyectos, mientras que el resto de nosotros escribimos programas.
Ostraco dice:
Tipo de diferencia entre el arquitecto y el artesano.
François Otis dice:
Yahoo, las plantillas están de vuelta !! Estoy bromeando, por supuesto. Los patrones nunca aparecen realmente en la imagen (las universidades no lo enseñan ...). Desafío a cualquier programador a afirmar que lo usa durante el día. ¿Cómo es que nada ha cambiado en una empresa de software después de 30 años? Las palabras son diversas, pero son las mismas técnicas de codificación que antes. Lo que llamamos Deep Intelligence son las mismas redes neuronales poco confiables que usamos para codificar en los 90, exactamente los mismos algoritmos, la misma mierda con mínimos locales y puntos ciegos, todo. ¿Por qué en 30 años no hemos inventado nada en la programación de software hasta ahora?
Luke Weston dice:
Este artículo no tiene nada que ver con el uso de plantillas de diseño de software para hardware. Se trata de usar plantillas de proyectos de software para ... programas.
rosmianto dice:
El autor ha dicho implícitamente que este artículo será una serie ... Espera.
Paul G. dice:
Vengo de as / w, y obtuve plantillas hace unos 10 años, me parece interesante que las plantillas se mencionen en este sitio. Sin embargo, eventualmente asocié tales enfoques de OOP con Unit Testing y Trial Development (TDD) y toda la escena de "refacturación del código OOP" (es decir, Fowler, etc.).
Una picazón que todavía tengo que eliminar es: ¿cómo puedo probar un dispositivo? Nunca he descubierto cómo hacer eso, y realmente espero que alguien pueda darme algunas ideas sobre qué enfoque podría tomar.
Quiero decir, si imagina que está produciendo 10 mini sensores de tiempo profesionales con radios NRF24. Comenzaría con uno y tendría pruebas como TestNRFIsConnectedProperly () Entonces, para su segunda y segunda placa, primero ejecutaría el código de prueba de la unidad como una doble verificación de su cableado y los componentes funcionaron como se esperaba, en lugar de agregar inmediatamente solo su implementación de código.
¿Alguien hace esto o algo por el estilo?
Puede usar el término pruebas unitarias, pero nuevamente podría llamarse una prueba de cableado o una prueba prudente, supongo, o primero hace lo que parezco hacer, y simplemente preste atención a los patrones de LED parpadeantes o salida en serie. Mi código parece desbordarse con ese código
if (DEBUG) sprintf (somevar);
Knut E dice:
En mi experiencia, suele haber una separación entre las pruebas de producción (de hardware) y las pruebas de software. Las pruebas de producción se ejecutan durante la producción para verificar que el hardware funciona correctamente. Las pruebas de producción podrían reemplazarse en un firmware separado con el firmware de la aplicación después de que las pruebas hayan pasado. A veces también hay autopruebas que funcionan al inicio o a voluntad, que intentan verificar que el hardware sigue funcionando correctamente. Luego, tenemos las pruebas de software que verifican que su software en particular se esté ejecutando en el hardware dado. Tanto las pruebas de software como las de productos pueden ser parte de la compilación, pero en este caso desea asegurarse de que la prueba de producción en sí funcione y pueda usarse para calificar cada hardware en producción.
Paul G. dice:
Gracias por su respuesta [Knut E]. Intenté buscar los términos pruebas de producción y arduino, pero se me ocurrieron algunas ideas, pero no muchas. Me gusta su idea de la autocomprobación del dispositivo durante el inicio, el único problema que veo es el de encontrar espacio para el código adicional requerido.
Perdóname por ceñirme al tema de arduino aquí, pero sirve como ejemplo. Hay un "marco" de una prueba para el código arduino-s / w (arduinounit), con algunas declaraciones de comparación de datos útiles muy simples y declaraciones "verdaderas" booleanas.
Esto probablemente prueba lo que imagino que entregaría un marco de prueba de producción:
#include genericProductionTests.h
void setup () // establecer baudios en serie, etc.
bool test_NRF24_SPI_is_connected () {
// extender / ejecutar test_SPI (LIST_OF_uC_PINS_HERE);
}El código de tiempo de ejecución de la prueba del producto se desarrolla a partir de la biblioteca de pruebas básicas conocidas (p. Ej., SPI, I2C), que contiene la secuencia normal y el marco de informes asociado con un probador. Lo siento si eso suena a vagabundeo sin sentido.
Daniel dice:
Aprendí cosas nuevas con información muy detallada.