¿Qué es KIVA?
Kiva es una organización sin ánimo de lucro que permite a cualquier persona prestar dinero (desde 25 dolares en adelante), a través de internet, a pequeñas empresas o emprendedores en regiones pobres para ayudarlos a salir de la pobreza.
Kiva, o alguna de sus organizaciones "socias" alrededor del mundo, analiza el plan de negocios presentado por el microempresario y si ve que tiene buenas posibilidades de prosperar en vez de hundir al microempresario en deudas, lo aprueba y lo pone en su página para que algún prestamista preste la cantidad de dinero que le parezca (a partir de 25 dolares) para ese emprendimiento.
Una vez que se ha juntado todo el dinero para ese emprendimiento, se le otorga el préstamo al microempresario.
Un ejemplo.
Este video, hecho por Kieran Ball, sigue el camino de un préstamo de $25, que va desde Londres hasta el pueblo de Preak Tamao en Camboya.
Un Puñado de Dólares: La Historia de un Préstamo Kiva de Kieran Ball en Vimeo.
¿Y después?
A medida que el préstamo se va devolviendo, los prestamistas van recuperando el dinero en su cuenta.
Pueden extraer ese dinero, volver a prestarlo para otros proyectos o donárselo a Kiva.
Mas información.
Para más información puede visitar:
Lista actual de emprendedores que necesitan un préstamo (en la página de Kiva, en inglés).
FEVAL – HP 50g/49g+ EVAL a 203 MHz.
Hice una librería, SOLAMENTE para la HP 49g+ o la 50g, que permite evaluar un objeto a 203 MHz. O sea, hace lo mismo que el comando EVAL de la calculadora pero a 203 MHz en vez de los 12 MHz o 75 MHz a los que trabaja normalmente.
La librería está basada en las herramientas ClockSpeed Adjustment Tools de Alistair Borowski.
Como dice Al, parece que no daña la calculadora. Pero usalo bajo tu propio riesgo.
Aclaro que el procesador de la calculadora está fabricado para funcionar a 203 MHz. HP lo "underclockeó" a un máximo de 75 MHz (o 12 MHz cuando inactiva). Pero aunque el procesador está diseñado para esa velocidad, tal vez los circuitos no.
De todas formas aquí está la librería para los que, como yo, quieren usarla para algunos cálculos largos:
FEVAL.rar
Ejemplo:
<< '500!' FEVAL >>
Como instalar la librería.
Extraer el contenido de FEVAL.rar y copiar el archivo LIBFEVAL.hp (Ctrl+C):
Pegarlo en algún directorio de la calculadora, por ejemplo el HOME.
Ir a HOME en la calculadora (o donde lo hayas copiado):
Seleccionar LIBFEVAL y copiarla (COPY):
Pegar en el puerto 2: FLASH o también puede ser en el "0: IRAM" o el "1: ERAM":
Reiniciar la calculadora presionando +
.
Poner en el stack algún objeto para evaluar y presionar ,
:
Presionar FEVAL para entrar al menú de la librería y FEVAL otra vez para ejecutar el comando:
También se podría haber escrito la palabra FEVAL simplemente y el comando se ejecutaría igual. El comando FEVAL ya funciona como cualquier otro comando de la calculadora. Inclusive aparece en el catálogo (,
):
Inclusive se puede presionar el HELP desde el catálogo para ver la descripción:
Asignar a la tecla EVAL (para doble clic).
Instalá el archivo Keymanplus de la librería Keyman de la misma forma que instalaste LIBFEVAL (acordate de reiniciar la calcu al finalizar).
Andá al menú LIB (,
). Poné en el stack lo que se muestra en la imágen:
Presioná IFD y luego ->TO? (una sola vez):
Ahora presioná A?D y luego la tecla .
Ya está. Cada vez que hagas doble clic en la tecla , en vez de ejecutarse EVAL se ejecutará FEVAL.
Lo podés comprobar:
- Poné '500!' en el stack.
- Presioná
para duplicarlo.
- Presioná la tecla
y fijate cuanto tarda.
- Borrá el resultado (no el '500!').
- Doble clic en la tecla
para volver a evaluarlo pero ahora con FEVAL.
Más rápido ¿no?
FEVAL – HP 50g/49g+ EVAL at 203 MHz.
I've just made a library, for HP 49g+ or 50g ONLY, that allows you tu evaluate an object on the stack at 203 MHz. It does the same than the EVAL built-in command but at 203 MHz instead of 12 MHz or 75 MHz normal speeds.
The library is based on Alistair Borowski's ClockSpeed Adjustment Tools.
As Al says, it seems not to damage the calculator. But use it at your own risk.
The real speed of the calculator's processor is 203 MHz, but HP underclocked it to 75 MHz or 12 MHz (when idle). But the calculator is not designed for that speed so it could get damaged.
Get it:
FEVAL.rar
Example of use:
<< '500!' FEVAL >>
How to install it.
Extract the content of FEVAL.rar and copy the file LIBFEVAL.hp (Ctrl+C):
Paste it on the HOME folder of your calculator.
Go to HOME folder on your calc:
Select LIBFEVAL and COPY it:
Paste it on port 2: FLASH (or port 0 or 1, it doesn't matter):
Warm start your calculator pressing ON+F3.
Put something on the stack to evaluate and press right shift / LIB:
Press FEVAL to enter the library menu. Press FEVAL again to execute the command:
You could have just written FEVAL and the command would execute as any built-in command. In fact, you can find it on the catalog now (right clic / CAT):
You can see the help for it too. Press HELP being FEVAL selected on the catalog:
Assign it to EVAL key (if double-clicked).
Install Keymanplus file (library) from Keyman library the same way you installed LIBFEVAL.
Go to LIB menu (righ shift / LIB). Put on the stack what you see in the image:
Press IFD and then ->TO? (when you press ->TO? you won't note any change):
Now press A?D and then the EVAL key.
Done. Now every time you double clic the EVAL key, FEVAL will be executed instead of EVAL.
You can test it:
- Put '500!' on the stack.
- Press ENTER to duplicate it.
- Press EVAL key and see how much it takes to evaluate.
- Erase the result (not the '500!').
- Double clic on the EVAL key and see how much it takes to evaluate.
Faster, hum?
Programación defensiva en C++.
post está basado en el libro "Thinking in C++ - Volume Two" de Bruce Eckel y Chuck Allison.
A medida que un programa va creciendo en tamaño y complejidad, se hace cada vez más difícil rastrear los errores a través del código. Peor todavía con los errores que no saltan a simple vista.
Una de las ventajas de la programación modular es que, al dividir un problema en muchos problemas mas pequeños, si cada parte funciona bien, todo funciona bien. Y es más fácil hacer que una pequeña parte funcione bien, cada vez que se diseña una, que diseñar todo un programa enorme y tratar de que funcione bien.
Entonces vamos a ver algunas técnicas para ir asegurándonos que cada parte de nuestro programa funcione bien a medida que lo vamos haciendo. Así, cuando unamos todas las partes, el programa entero va a funcionar bien.
Aserciones.
Un algoritmo es, entre otras cosas, una expresión de nuestro intento por resolver un problema. Éste debería dejar en claro al lector (inclusive el escritor mismo) exactamente qué se estaba pensando cuando se diseñó esa porción de código así. En ciertos puntos del programa, se deberían poder hacer ciertas aserciones o aseveraciones.
Algunas ventajas[1]:
- Hace que, lo que de otra forma hubieras puesto como comentario, sea verificado en tiempo de ejecución para ver si realmente es así.
- Te permite expresar en el código lo que vos considerás como verdadero en un cierto punto de ejecución.
- Si no se cumple inmediatamente termina el programa.
- Probablemente ya hayas escrito comentarios en tu código. ¿Porqué no convertirlo en aserciones y hacer tu código más robusto?
- Podés estar seguro de que, si el código llegó ahí, todo lo que habías asumido como verdadero hasta ese punto se cumplió.
- No hacen, en absoluto, el código más lento (una vez compilado en release). Sólo existen para compilaciones "debug". Para "release" desaparecen.
Un programa sin aserciones se vería así:
#include <iostream> #include <algorithm> int main() { int v[]={0,1,2,3,4,5,6,7,8,9}; int tam=sizeof v / sizeof *v; int min=*std::min_element(v,v+tam); int max=*std::max_element(v,v+tam); std::cout<<"Min: "<<min<<std::endl; std::cout<<"Max: "<<max; return 0; }
Uno con aserciones sería:
#include <iostream> #include <algorithm> #include <cassert> int main() { int v[]={0,1,2,3,4,5,6,7,8,9}; int tam=sizeof v / sizeof *v; assert(tam==10); int min=*std::min_element(v,v+tam); assert(min==0); int max=*std::max_element(v,v+tam); assert(max==9); std::cout<<"Min: "<<min<<std::endl; std::cout<<"Max: "<<max; return 0; }
Cuando escribí este ejemplo, me confundí en varias cosas. Por ejemplo, al principio supuse "sizeof v" como 10, cuando en realidad era 40 ya que cada int me ocupaba 4 bytes. Saltó el assert.
También puse "assert(max==10)". Otra vez saltó, porque distraído no me dí cuenta que aunque el tamaño era 10, el mayor elemento era 9.
Como verás, es muy útil para ayudar al programador. Llená tu código de aserciones. Por ejemplo antes de evaluar la raiz cuadrada de un número, verificá que sea >=0. O antes de dividir por x verificá que "assert(x!=0). Te va a ahorrar dolores de cabeza.
La sintaxis de assert es:
void assert (int expression);
Está definida en la cabecera "<cassert>".
Para la versión final de tu programa, luego de haber probado que anda todo bien, eliminá automáticamente todas aserciones definiendo, antes de la inclusión de <cassert>, lo siguiente:
#define NDEBUG
La definición de assert, en <cassert> es equivalente a esta:
#ifdef NDEBUG #define assert(cond) ((void)0) #else void assertImpl(const char*, const char*,long); #define assert(cond)\ ((cond)?(void)0:assertImpl(???)) #endif
Estructura Automatizada de pruebas (Automated Test Framework).
Muchas veces los programadores, luego de haber construido una función, la probamos llamándola con algunos valores y fijándonos que nos retorna.
Esto es tedioso y muy propenso a errores. Las máquinas lo hacen mejor que los humanos. Entonces ¿porqué no automatizar el proceso?
El libro Thinking In C++ - Volume 2: Practical Programming presenta "la unidad de test automatizado más simple que pueda funcionar".
El código lo podés descargar de acá o copiarlo del final del post.
Uso[2]:
A continuación pongo como un ejemplo un programa que pasa de numeros decimales a romanos (El framwork está en el espacio de nombres "TestSuite").
#include <iostream> #include <string> #include "Test.h" std::string ARomano(int Decimal ) { static char *unidades[] = // Unidades de 0 a 9 { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; static char *decenas[] = // Decenas de 0 a 90 { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; static char *centenas[] = // Centenas de 0 a 900 { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; static char *millares[] = // Millares de 0 a 3000 { "", "M", "MM", "MMM" }; return std::string(millares[ (Decimal / 1000) ]) + std::string(centenas[ (Decimal / 100) % 10 ]) + std::string(decenas[ (Decimal / 10) % 10 ]) + std::string(unidades[ (Decimal) % 10 ]); } // Para probar la función de arriba creamos una clase, derivada de Test, que pruebe algunos valores. class TestARomano : public TestSuite::Test { public: void run() { test_(ARomano(1)=="I"); test_(ARomano(5)=="V"); test_(ARomano(13)=="XIII"); test_(ARomano(99)=="XCIX"); test_(ARomano(444)=="CDXLIV"); test_(ARomano(720)=="DCCXX"); test_(ARomano(2803)=="MMDCCCIII"); test_(ARomano(3888)=="MMMDCCCLXXXVIII"); test_(ARomano(3999)=="MMMCMXCIX"); } }; int main() { int n; TestARomano mitest; // Instancio la clase. mitest.run(); // Pruebo si hubieron errors. assert(mitest.report()==0); // Muestro los resultados. No tienen que haber habido errores. std::cout<<std::endl<<"Ingrese numeros en decimal entre 1 y 3999 para transformarlos en romanos. Ingrese <1 o >3999 para salir."<<std::endl; do { std::cout<<"Decimal: "; std::cin>>n; if((n<=0)||(n>3999)) break; std::cout<<"Romano: "<<ARomano(n)<<std::endl; }while(1); return 0; }
La salida del programa es la siguiente:
Test "class TestARomano":
Passed: 9 Failed: 0
Ingrese numeros en decimal entre 1 y 3999 para transformarlos en romanos. Ingres
e <1 o >3999 para salir.
Decimal: 453
Romano: CDLIII
Decimal: 43
Romano: XLIII
Decimal: 0
Un ejemplo donde probamos más de una función se muestra a continuación. Para eso utilizamos el framework, para integrar los diferentes tests.
#include <iostream> #include <string> #include "Test.h" #include "Suite.h" // Funciones "tontas" para mostrar el uso del framework. int suma(int a,int b){return a+b;} int resta(int a,int b){return a-b;} int multiplicacion(int a,int b){return a*b;} int division(int a,int b){return a/b;} // Probamos. class TestSuma : public TestSuite::Test { public: void run() { test_(suma(1,1)==2); test_(suma(2,1)==3); test_(suma(1,2)==3); test_(suma(2,5)==7); test_(suma(100,29)==129); test_(suma(5,1)>5); test_(suma(1,3)<5); } }; class TestResta : public TestSuite::Test { public: void run() { test_(resta(1,1)==0); test_(resta(2,1)==1); test_(resta(1,2)==-1); test_(resta(2,5)==-3); test_(resta(100,29)==71); test_(resta(5,1)>3); test_(resta(1,3)<1); } }; class TestMultiplicacion : public TestSuite::Test { public: void run() { test_(multiplicacion(1,1)==1); test_(multiplicacion(2,1)==2); test_(multiplicacion(1,2)==2); test_(multiplicacion(2,5)==10); test_(multiplicacion(100,29)==2900); test_(multiplicacion(5,1)>4); test_(multiplicacion(1,3)<4); } }; class TestDivision : public TestSuite::Test { public: void run() { test_(multiplicacion(1,1)==1); test_(multiplicacion(2,1)==2); test_(multiplicacion(1,2)==0); test_(multiplicacion(2,5)==0); test_(multiplicacion(100,29)==3); test_(multiplicacion(5,1)>4); test_(multiplicacion(1,3)<1); } }; int main() { TestSuite::Suite basicas("Operaciones basicas"); basicas.addTest(new TestSuma); basicas.addTest(new TestResta); basicas.addTest(new TestMultiplicacion); basicas.addTest(new TestDivision); basicas.run(); std::cout<<std::endl<<"Fallos: "<<basicas.report(); basicas.free(); // Le digo que los punteros estaban en el heap y que los borre. return 0; }
La salida del ejemplo anterior es:
class TestDivisionfailure: (multiplicacion(1,2)==0) , c:\documents and settings\
daniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (lin
e 65)
class TestDivisionfailure: (multiplicacion(2,5)==0) , c:\documents and settings\
daniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (lin
e 66)
class TestDivisionfailure: (multiplicacion(100,29)==3) , c:\documents and settin
gs\daniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (
line 67)
class TestDivisionfailure: (multiplicacion(1,3)<1) , c:\documents and settings\d
aniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (line
69)
Suite "Operaciones basicas"
===========================
Test "class TestSuma":
Passed: 7 Failed: 0
Test "class TestResta":
Passed: 7 Failed: 0
Test "class TestMultiplicacion":
Passed: 7 Failed: 0
Test "class TestDivision":
Passed: 3 Failed: 4
===========================
Fallos: 4
Claramente se puede ver cuales fueron los fallos. Me olvidé de cambiar "multiplicación" por " división en el test de división (porque copié y pegué el código.
El siguiente código ya funciona bien:
#include <iostream> #include <string> #include "Test.h" #include "Suite.h" // Funciones "tontas" para mostrar el uso del framework. int suma(int a,int b){return a+b;} int resta(int a,int b){return a-b;} int multiplicacion(int a,int b){return a*b;} int division(int a,int b){return a/b;} // Probamos. class TestSuma : public TestSuite::Test { public: void run() { test_(suma(1,1)==2); test_(suma(2,1)==3); test_(suma(1,2)==3); test_(suma(2,5)==7); test_(suma(100,29)==129); test_(suma(5,1)>5); test_(suma(1,3)<5); } }; class TestResta : public TestSuite::Test { public: void run() { test_(resta(1,1)==0); test_(resta(2,1)==1); test_(resta(1,2)==-1); test_(resta(2,5)==-3); test_(resta(100,29)==71); test_(resta(5,1)>3); test_(resta(1,3)<1); } }; class TestMultiplicacion : public TestSuite::Test { public: void run() { test_(multiplicacion(1,1)==1); test_(multiplicacion(2,1)==2); test_(multiplicacion(1,2)==2); test_(multiplicacion(2,5)==10); test_(multiplicacion(100,29)==2900); test_(multiplicacion(5,1)>4); test_(multiplicacion(1,3)<4); } }; class TestDivision : public TestSuite::Test { public: void run() { test_(division(1,1)==1); test_(division(2,1)==2); test_(division(1,2)==0); test_(division(2,5)==0); test_(division(100,29)==3); test_(division(5,1)>4); test_(division(1,3)<1); } }; int main() { TestSuite::Suite basicas("Operaciones basicas"); basicas.addTest(new TestSuma); basicas.addTest(new TestResta); basicas.addTest(new TestMultiplicacion); basicas.addTest(new TestDivision); basicas.run(); std::cout<<std::endl<<"Fallos: "<<basicas.report(); basicas.free(); // Le digo que los punteros estaban en el heap y que los borre. return 0; }
La salida es:
Suite "Operaciones basicas"
===========================
Test "class TestSuma":
Passed: 7 Failed: 0
Test "class TestResta":
Passed: 7 Failed: 0
Test "class TestMultiplicacion":
Passed: 7 Failed: 0
Test "class TestDivision":
Passed: 7 Failed: 0
===========================
Fallos: 0
En el libro online hay un ejemplo más completo[2]. Pero esto ya nos sirve para empezar a trabajar con pruebas automáticas.
A continuación el código del framework:
Test.h:
//: TestSuite:Test.h // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #ifndef TEST_H #define TEST_H #include <string> #include <iostream> #include <cassert> using std::string; using std::ostream; using std::cout; // fail_() has an underscore to prevent collision with // ios::fail(). For consistency, test_() and succeed_() // also have underscores. #define test_(cond) \ do_test(cond, #cond, __FILE__, __LINE__) #define fail_(str) \ do_fail(str, __FILE__, __LINE__) namespace TestSuite { class Test { ostream* osptr; long nPass; long nFail; // Disallowed: Test(const Test&); Test& operator=(const Test&); protected: void do_test(bool cond, const string& lbl, const char* fname, long lineno); void do_fail(const string& lbl, const char* fname, long lineno); public: Test(ostream* osptr = &cout) { this->osptr = osptr; nPass = nFail = 0; } virtual ~Test() {} virtual void run() = 0; long getNumPassed() const { return nPass; } long getNumFailed() const { return nFail; } const ostream* getStream() const { return osptr; } void setStream(ostream* osptr) { this->osptr = osptr; } void succeed_() { ++nPass; } long report() const; virtual void reset() { nPass = nFail = 0; } }; } // namespace TestSuite #endif // TEST_H ///:~
Test.cpp:
//: TestSuite:Test.cpp {O} // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #include "Test.h" #include <iostream> #include <typeinfo> using namespace std; using namespace TestSuite; void Test::do_test(bool cond, const std::string& lbl, const char* fname, long lineno) { if(!cond) do_fail(lbl, fname, lineno); else succeed_(); } void Test::do_fail(const std::string& lbl, const char* fname, long lineno) { ++nFail; if(osptr) { *osptr << typeid(*this).name() << "failure: (" << lbl << ") , " << fname << " (line " << lineno << ")" << endl; } } long Test::report() const { if(osptr) { *osptr << "Test \"" << typeid(*this).name() << "\":\n\tPassed: " << nPass << "\tFailed: " << nFail << endl; } return nFail; } ///:~
Suite.h:
//: TestSuite:Suite.h // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #ifndef SUITE_H #define SUITE_H #include <vector> #include <stdexcept> #include "Test.h" using std::vector; using std::logic_error; namespace TestSuite { class TestSuiteError : public logic_error { public: TestSuiteError(const string& s = "") : logic_error(s) {} }; class Suite { string name; ostream* osptr; vector<Test*> tests; void reset(); // Disallowed ops: Suite(const Suite&); Suite& operator=(const Suite&); public: Suite(const string& name, ostream* osptr = &cout) : name(name) { this->osptr = osptr; } string getName() const { return name; } long getNumPassed() const; long getNumFailed() const; const ostream* getStream() const { return osptr; } void setStream(ostream* osptr) { this->osptr = osptr; } void addTest(Test* t) throw(TestSuiteError); void addSuite(const Suite&); void run(); // Calls Test::run() repeatedly long report() const; void free(); // Deletes tests }; } // namespace TestSuite #endif // SUITE_H ///:~
Suite.cpp:
//: TestSuite:Suite.cpp {O} // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #include "Suite.h" #include <iostream> #include <cassert> #include <cstddef> using namespace std; using namespace TestSuite; void Suite::addTest(Test* t) throw(TestSuiteError) { // Verify test is valid and has a stream: if(t == 0) throw TestSuiteError("Null test in Suite::addTest"); else if(osptr && !t->getStream()) t->setStream(osptr); tests.push_back(t); t->reset(); } void Suite::addSuite(const Suite& s) { for(size_t i = 0; i < s.tests.size(); ++i) { assert(tests[i]); addTest(s.tests[i]); } } void Suite::free() { for(size_t i = 0; i < tests.size(); ++i) { delete tests[i]; tests[i] = 0; } } void Suite::run() { reset(); for(size_t i = 0; i < tests.size(); ++i) { assert(tests[i]); tests[i]->run(); } } long Suite::report() const { if(osptr) { long totFail = 0; *osptr << "Suite \"" << name << "\"\n======="; size_t i; for(i = 0; i < name.size(); ++i) *osptr << '='; *osptr << "=" << endl; for(i = 0; i < tests.size(); ++i) { assert(tests[i]); totFail += tests[i]->report(); } *osptr << "======="; for(i = 0; i < name.size(); ++i) *osptr << '='; *osptr << "=" << endl; return totFail; } else return getNumFailed(); } long Suite::getNumPassed() const { long totPass = 0; for(size_t i = 0; i < tests.size(); ++i) { assert(tests[i]); totPass += tests[i]->getNumPassed(); } return totPass; } long Suite::getNumFailed() const { long totFail = 0; for(size_t i = 0; i < tests.size(); ++i) { assert(tests[i]); totFail += tests[i]->getNumFailed(); } return totFail; } void Suite::reset() { for(size_t i = 0; i < tests.size(); ++i) { assert(tests[i]); tests[i]->reset(); } } ///:~
FOOTNOTES
1. The benefits of programming with assertions.↑
2. Para más detalles consultar el título "A simple unit test framework", en el libro online. (en inglés)↑
2. post está basado en el libro "Thinking in C++ - Volume Two" de Bruce Eckel y Chuck Allison.
A medida que un programa va creciendo en tamaño y complejidad, se hace cada vez más difícil rastrear los errores a través del código. Peor todavía con los errores que no saltan a simple vista.
Una de las ventajas de la programación modular es que, al dividir un problema en muchos problemas mas pequeños, si cada parte funciona bien, todo funciona bien. Y es más fácil hacer que una pequeña parte funcione bien, cada vez que se diseña una, que diseñar todo un programa enorme y tratar de que funcione bien.
Entonces vamos a ver algunas técnicas para ir asegurándonos que cada parte de nuestro programa funcione bien a medida que lo vamos haciendo. Así, cuando unamos todas las partes, el programa entero va a funcionar bien.
Aserciones.
Un algoritmo es, entre otras cosas, una expresión de nuestro intento por resolver un problema. Éste debería dejar en claro al lector (inclusive el escritor mismo) exactamente qué se estaba pensando cuando se diseñó esa porción de código así. En ciertos puntos del programa, se deberían poder hacer ciertas aserciones o aseveraciones.
Algunas ventajas[1]:
- Hace que, lo que de otra forma hubieras puesto como comentario, sea verificado en tiempo de ejecución para ver si realmente es así.
- Te permite expresar en el código lo que vos considerás como verdadero en un cierto punto de ejecución.
- Si no se cumple inmediatamente termina el programa.
- Probablemente ya hayas escrito comentarios en tu código. ¿Porqué no convertirlo en aserciones y hacer tu código más robusto?
- Podés estar seguro de que, si el código llegó ahí, todo lo que habías asumido como verdadero hasta ese punto se cumplió.
- No hacen, en absoluto, el código más lento (una vez compilado en release). Sólo existen para compilaciones "debug". Para "release" desaparecen.
Un programa sin aserciones se vería así:
#include <iostream> #include <algorithm> int main() { int v[]={0,1,2,3,4,5,6,7,8,9}; int tam=sizeof v / sizeof *v; int min=*std::min_element(v,v+tam); int max=*std::max_element(v,v+tam); std::cout<<"Min: "<<min<<std::endl; std::cout<<"Max: "<<max; return 0; }
Uno con aserciones sería:
#include <iostream> #include <algorithm> #include <cassert> int main() { int v[]={0,1,2,3,4,5,6,7,8,9}; int tam=sizeof v / sizeof *v; assert(tam==10); int min=*std::min_element(v,v+tam); assert(min==0); int max=*std::max_element(v,v+tam); assert(max==9); std::cout<<"Min: "<<min<<std::endl; std::cout<<"Max: "<<max; return 0; }
Cuando escribí este ejemplo, me confundí en varias cosas. Por ejemplo, al principio supuse "sizeof v" como 10, cuando en realidad era 40 ya que cada int me ocupaba 4 bytes. Saltó el assert.
También puse "assert(max==10)". Otra vez saltó, porque distraído no me dí cuenta que aunque el tamaño era 10, el mayor elemento era 9.
Como verás, es muy útil para ayudar al programador. Llená tu código de aserciones. Por ejemplo antes de evaluar la raiz cuadrada de un número, verificá que sea >=0. O antes de dividir por x verificá que "assert(x!=0). Te va a ahorrar dolores de cabeza.
La sintaxis de assert es:
void assert (int expression);
Está definida en la cabecera "<cassert>".
Para la versión final de tu programa, luego de haber probado que anda todo bien, eliminá automáticamente todas aserciones definiendo, antes de la inclusión de <cassert>, lo siguiente:
#define NDEBUG
La definición de assert, en <cassert> es equivalente a esta:
#ifdef NDEBUG #define assert(cond) ((void)0) #else void assertImpl(const char*, const char*,long); #define assert(cond)\ ((cond)?(void)0:assertImpl(???)) #endif
Estructura Automatizada de pruebas (Automated Test Framework).
Muchas veces los programadores, luego de haber construido una función, la probamos llamándola con algunos valores y fijándonos que nos retorna.
Esto es tedioso y muy propenso a errores. Las máquinas lo hacen mejor que los humanos. Entonces ¿porqué no automatizar el proceso?
El libro Thinking In C++ - Volume 2: Practical Programming presenta "la unidad de test automatizado más simple que pueda funcionar".
El código lo podés descargar de acá o copiarlo del final del post.
Uso[2]:
A continuación pongo como un ejemplo un programa que pasa de numeros decimales a romanos (El framwork está en el espacio de nombres "TestSuite").
#include <iostream> #include <string> #include "Test.h" std::string ARomano(int Decimal ) { static char *unidades[] = // Unidades de 0 a 9 { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" }; static char *decenas[] = // Decenas de 0 a 90 { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" }; static char *centenas[] = // Centenas de 0 a 900 { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" }; static char *millares[] = // Millares de 0 a 3000 { "", "M", "MM", "MMM" }; return std::string(millares[ (Decimal / 1000) ]) + std::string(centenas[ (Decimal / 100) % 10 ]) + std::string(decenas[ (Decimal / 10) % 10 ]) + std::string(unidades[ (Decimal) % 10 ]); } // Para probar la función de arriba creamos una clase, derivada de Test, que pruebe algunos valores. class TestARomano : public TestSuite::Test { public: void run() { test_(ARomano(1)=="I"); test_(ARomano(5)=="V"); test_(ARomano(13)=="XIII"); test_(ARomano(99)=="XCIX"); test_(ARomano(444)=="CDXLIV"); test_(ARomano(720)=="DCCXX"); test_(ARomano(2803)=="MMDCCCIII"); test_(ARomano(3888)=="MMMDCCCLXXXVIII"); test_(ARomano(3999)=="MMMCMXCIX"); } }; int main() { int n; TestARomano mitest; // Instancio la clase. mitest.run(); // Pruebo si hubieron errors. assert(mitest.report()==0); // Muestro los resultados. No tienen que haber habido errores. std::cout<<std::endl<<"Ingrese numeros en decimal entre 1 y 3999 para transformarlos en romanos. Ingrese <1 o >3999 para salir."<<std::endl; do { std::cout<<"Decimal: "; std::cin>>n; if((n<=0)||(n>3999)) break; std::cout<<"Romano: "<<ARomano(n)<<std::endl; }while(1); return 0; }
La salida del programa es la siguiente:
Test "class TestARomano":
Passed: 9 Failed: 0
Ingrese numeros en decimal entre 1 y 3999 para transformarlos en romanos. Ingres
e <1 o >3999 para salir.
Decimal: 453
Romano: CDLIII
Decimal: 43
Romano: XLIII
Decimal: 0
Un ejemplo donde probamos más de una función se muestra a continuación. Para eso utilizamos el framework, para integrar los diferentes tests.
#include <iostream> #include <string> #include "Test.h" #include "Suite.h" // Funciones "tontas" para mostrar el uso del framework. int suma(int a,int b){return a+b;} int resta(int a,int b){return a-b;} int multiplicacion(int a,int b){return a*b;} int division(int a,int b){return a/b;} // Probamos. class TestSuma : public TestSuite::Test { public: void run() { test_(suma(1,1)==2); test_(suma(2,1)==3); test_(suma(1,2)==3); test_(suma(2,5)==7); test_(suma(100,29)==129); test_(suma(5,1)>5); test_(suma(1,3)<5); } }; class TestResta : public TestSuite::Test { public: void run() { test_(resta(1,1)==0); test_(resta(2,1)==1); test_(resta(1,2)==-1); test_(resta(2,5)==-3); test_(resta(100,29)==71); test_(resta(5,1)>3); test_(resta(1,3)<1); } }; class TestMultiplicacion : public TestSuite::Test { public: void run() { test_(multiplicacion(1,1)==1); test_(multiplicacion(2,1)==2); test_(multiplicacion(1,2)==2); test_(multiplicacion(2,5)==10); test_(multiplicacion(100,29)==2900); test_(multiplicacion(5,1)>4); test_(multiplicacion(1,3)<4); } }; class TestDivision : public TestSuite::Test { public: void run() { test_(multiplicacion(1,1)==1); test_(multiplicacion(2,1)==2); test_(multiplicacion(1,2)==0); test_(multiplicacion(2,5)==0); test_(multiplicacion(100,29)==3); test_(multiplicacion(5,1)>4); test_(multiplicacion(1,3)<1); } }; int main() { TestSuite::Suite basicas("Operaciones basicas"); basicas.addTest(new TestSuma); basicas.addTest(new TestResta); basicas.addTest(new TestMultiplicacion); basicas.addTest(new TestDivision); basicas.run(); std::cout<<std::endl<<"Fallos: "<<basicas.report(); basicas.free(); // Le digo que los punteros estaban en el heap y que los borre. return 0; }
La salida del ejemplo anterior es:
class TestDivisionfailure: (multiplicacion(1,2)==0) , c:\documents and settings\
daniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (lin
e 65)
class TestDivisionfailure: (multiplicacion(2,5)==0) , c:\documents and settings\
daniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (lin
e 66)
class TestDivisionfailure: (multiplicacion(100,29)==3) , c:\documents and settin
gs\daniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (
line 67)
class TestDivisionfailure: (multiplicacion(1,3)<1) , c:\documents and settings\d
aniel\mis documentos\visual studio 2008\projects\romano\romano\romanos.cpp (line
69)
Suite "Operaciones basicas"
===========================
Test "class TestSuma":
Passed: 7 Failed: 0
Test "class TestResta":
Passed: 7 Failed: 0
Test "class TestMultiplicacion":
Passed: 7 Failed: 0
Test "class TestDivision":
Passed: 3 Failed: 4
===========================
Fallos: 4
Claramente se puede ver cuales fueron los fallos. Me olvidé de cambiar "multiplicación" por " división en el test de división (porque copié y pegué el código.
El siguiente código ya funciona bien:
#include <iostream> #include <string> #include "Test.h" #include "Suite.h" // Funciones "tontas" para mostrar el uso del framework. int suma(int a,int b){return a+b;} int resta(int a,int b){return a-b;} int multiplicacion(int a,int b){return a*b;} int division(int a,int b){return a/b;} // Probamos. class TestSuma : public TestSuite::Test { public: void run() { test_(suma(1,1)==2); test_(suma(2,1)==3); test_(suma(1,2)==3); test_(suma(2,5)==7); test_(suma(100,29)==129); test_(suma(5,1)>5); test_(suma(1,3)<5); } }; class TestResta : public TestSuite::Test { public: void run() { test_(resta(1,1)==0); test_(resta(2,1)==1); test_(resta(1,2)==-1); test_(resta(2,5)==-3); test_(resta(100,29)==71); test_(resta(5,1)>3); test_(resta(1,3)<1); } }; class TestMultiplicacion : public TestSuite::Test { public: void run() { test_(multiplicacion(1,1)==1); test_(multiplicacion(2,1)==2); test_(multiplicacion(1,2)==2); test_(multiplicacion(2,5)==10); test_(multiplicacion(100,29)==2900); test_(multiplicacion(5,1)>4); test_(multiplicacion(1,3)<4); } }; class TestDivision : public TestSuite::Test { public: void run() { test_(division(1,1)==1); test_(division(2,1)==2); test_(division(1,2)==0); test_(division(2,5)==0); test_(division(100,29)==3); test_(division(5,1)>4); test_(division(1,3)<1); } }; int main() { TestSuite::Suite basicas("Operaciones basicas"); basicas.addTest(new TestSuma); basicas.addTest(new TestResta); basicas.addTest(new TestMultiplicacion); basicas.addTest(new TestDivision); basicas.run(); std::cout<<std::endl<<"Fallos: "<<basicas.report(); basicas.free(); // Le digo que los punteros estaban en el heap y que los borre. return 0; }
La salida es:
Suite "Operaciones basicas"
===========================
Test "class TestSuma":
Passed: 7 Failed: 0
Test "class TestResta":
Passed: 7 Failed: 0
Test "class TestMultiplicacion":
Passed: 7 Failed: 0
Test "class TestDivision":
Passed: 7 Failed: 0
===========================
Fallos: 0
En el libro online hay un ejemplo más completo[2]. Pero esto ya nos sirve para empezar a trabajar con pruebas automáticas.
A continuación el código del framework:
Test.h:
//: TestSuite:Test.h // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #ifndef TEST_H #define TEST_H #include <string> #include <iostream> #include <cassert> using std::string; using std::ostream; using std::cout; // fail_() has an underscore to prevent collision with // ios::fail(). For consistency, test_() and succeed_() // also have underscores. #define test_(cond) \ do_test(cond, #cond, __FILE__, __LINE__) #define fail_(str) \ do_fail(str, __FILE__, __LINE__) namespace TestSuite { class Test { ostream* osptr; long nPass; long nFail; // Disallowed: Test(const Test&); Test& operator=(const Test&); protected: void do_test(bool cond, const string& lbl, const char* fname, long lineno); void do_fail(const string& lbl, const char* fname, long lineno); public: Test(ostream* osptr = &cout) { this->osptr = osptr; nPass = nFail = 0; } virtual ~Test() {} virtual void run() = 0; long getNumPassed() const { return nPass; } long getNumFailed() const { return nFail; } const ostream* getStream() const { return osptr; } void setStream(ostream* osptr) { this->osptr = osptr; } void succeed_() { ++nPass; } long report() const; virtual void reset() { nPass = nFail = 0; } }; } // namespace TestSuite #endif // TEST_H ///:~
Test.cpp:
//: TestSuite:Test.cpp {O} // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #include "Test.h" #include <iostream> #include <typeinfo> using namespace std; using namespace TestSuite; void Test::do_test(bool cond, const std::string& lbl, const char* fname, long lineno) { if(!cond) do_fail(lbl, fname, lineno); else succeed_(); } void Test::do_fail(const std::string& lbl, const char* fname, long lineno) { ++nFail; if(osptr) { *osptr << typeid(*this).name() << "failure: (" << lbl << ") , " << fname << " (line " << lineno << ")" << endl; } } long Test::report() const { if(osptr) { *osptr << "Test \"" << typeid(*this).name() << "\":\n\tPassed: " << nPass << "\tFailed: " << nFail << endl; } return nFail; } ///:~
Suite.h:
//: TestSuite:Suite.h // From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison. // (c) 1995-2004 MindView, Inc. All Rights Reserved. // See source code use permissions stated in the file 'License.txt', // distributed with the code package available at www.MindView.net. #ifndef SUITE_H #define SUITE_H #include <vector> #include <stdexcept> #include "Test.h" using std::vector; using std::logic_error; namespace TestSuite { class TestSuiteError : public logic_error { public: TestSuiteError(const string& s = "") : logic_error(s) {} }; class Suite { string name; ostream* osptr; vector<Test*> tests; void reset(); // Disallowed ops: Suite(const Suite&); Suite& operator=(const Suite&); public: Suite(const string& name, ostream* osptr = &cout) : name(name) { this->osptr = osptr; } string getName() const { return name; } long getNumPassed() const; long getNumFailed() const; const ostream* getStream() const { return osptr; } void setStream(ostream* osptr) { this->osptr = osptr; } void addTest(Test* t) throw(TestSuiteError); void addSuite(const Suite&); void run(); // Calls Test::run() repeatedly long report() const; void free(); // Deletes tests }; } // namespace TestSuite #endif // SUITE_H ///:~
Suite.cpp:
//: TestSuite:Suite.cpp {O}
// From "Thinking in C++, Volume 2", by Bruce Eckel & Chuck Allison.
// (c) 1995-2004 MindView, Inc. All Rights Reserved.
// See source code use permissions stated in the file 'License.txt',
// distributed with the code package available at www.MindView.net.
#include "Suite.h"
#include <iostream>
#include <cassert>
#include <cstddef>
using namespace std;
using namespace TestSuite;void Suite::addTest(Test* t) throw(TestSuiteError) {
// Verify test is valid and has a stream:
if(t == 0)
throw TestSuiteError("Null test in Suite::addTest");
else if(osptr && !t->getStream())
t->setStream(osptr);
tests.push_back(t);
t->reset();
}void Suite::addSuite(const Suite& s) {
for(size_t i = 0; i < s.tests.size(); ++i) {
assert(tests[i]);
addTest(s.tests[i]);
}
}void Suite::free() {
for(size_t i = 0; i < tests.size(); ++i) {
delete tests[i];
tests[i] = 0;
}
}void Suite::run() {
reset();
for(size_t i = 0; i < tests.size(); ++i) {
assert(tests[i]);
tests[i]->run();
}
}long Suite::report() const {
if(osptr) {
long totFail = 0;
*osptr << "Suite \"" << name
<< "\"\n=======";
size_t i;
for(i = 0; i < name.size(); ++i)
*osptr << '=';
*osptr << "=" << endl;
for(i = 0; i < tests.size(); ++i) {
assert(tests[i]);
totFail += tests[i]->report();
}
*osptr << "=======";
for(i = 0; i < name.size(); ++i)
*osptr << '=';
*osptr << "=" << endl;
return totFail;
}
else
return getNumFailed();
}long Suite::getNumPassed() const {
long totPass = 0;
for(size_t i = 0; i < tests.size(); ++i) {
assert(tests[i]);
totPass += tests[i]->getNumPassed();
}
return totPass;
}long Suite::getNumFailed() const {
long totFail = 0;
for(size_t i = 0; i < tests.size(); ++i) {
assert(tests[i]);
totFail += tests[i]->getNumFailed();
}
return totFail;
}void Suite::reset() {
for(size_t i = 0; i < tests.size(); ++i) {
assert(tests[i]);
tests[i]->reset();
}
} ///:~<↑
Selective Adsense – Plugin para WordPress.
[English version]
Este plugin está basado en los plugins Adsense Deluxe y Whydowork Adsense.
Me gustó la opción de Adsense Deluxe que permite incluir comentarios dentro del post para ser reemplazado por anuncios, de forma que uno puede controlar los mejores lugares para los anuncios. El problema con ese plugin es que existe un máximo de 3 bloques de anuncios y de 3 bloques de links por página y el plugin no discrimina entre uno y otro ni establece un tope para dejar de imprimir los anuncios, incurriendo así en falta con las normas de Adsense.
Así que quería poder controlar que solo se mostraran 3 anuncios de cada categoría para cada impresión de página.
El pluging Whydowork Adsense, por otro lado, muestra solamente 3 anuncios por ágina, pero no discrimina entre categorías (bloques de anuncios y de links) ni tampoco me deja escoger exáctamente en que parte del texto poner los anuncios. Solo permite elegir entre arriba, al medio y abajo, y la alineación que tendrá.
Con el plugin Selective Adsense (el primer plugin que he hecho), que desarrollé siguiendo el código de Whydowork Adsense para guiarme, puedo controlar en que parte del post se mostrará el anuncio. También controlo la alineación y el anuncio que quiero mostrar. Con la posibilidad de dar diferentes probabilidades a cada anuncio y alineación.
También puedo poner anuncios en los templates, insertando una función php, y puedo configurar que los anuncios roten dandoles cierta probabilidad.
El plugin puede tener bugs o agujeros de seguridad. Lo proveo "tal cual" al que le sirva. Espero sus sugerencias, comentarios y reportes de bugs o agujeros de seguridad.
Uso:
Adentro de los posts:
Inserte este comentario html:
<!--SelectiveAdsense(3,1,1,2,3,1)[1,3,2,3,1,3]-->
El comentario será reemplazado por el anuncio. ¿Qué anuncio? Tres probabilidades para el Anuncio#1, una para el #2 y dos para el #3 (3,1,1,2,3,1). ¿Con qué alineación. Dos probabilidades para izquierda, una para ninguna alineación y tres para derecha [1,3,2,3,1,3].
Se reemplaza hasta que se muestren 3 anuncios de la misma categoría (bloque, links o refs). Luego se siguen reemplazando los de las otras categorías hasta que se muestren 3 de cada una como máximo.
En los Templates:
Inserte esta función php: SelectiveAdsense('4,1,1,2,3,1');
El anuncio será mostrado en esa poscición. No se le da ningún alineamiento, se debe alinearlo en el template. Tres probabilidades para #1, una para #2, una para #3 y una para #4.
Aumentar el número de anuncios para elegir:
Si querés elegir entre más de 20 anuncios podés cambiar el valor en:
$MAX_ADS=20;
Descargar plugin.
http://www.danielmunoz.com.ar/blog/2009/05/27/selective-adsense-wordpress-plugin/
Selective Adsense – WordPress Plugin
[Versión en Español]
This plugin is based on Adsense Deluxe and Whydowork Adsense plugins.
I liked the Adsense Deluxe option to put comments inside my posts to be replaced by the ads, so I could control the best places for the ads. The problem with it was the max-3-blocks and max-3-links per page impresion. Adsense Deluxe didn't distinguished between them and would print them all (in pages like home page where there is one post below another).
So I wanted to control that only 3 ads of each category would show on each page impression.
Whydowork Adsense would show only 3 per page, but it didn't distinguish between categories neither let me choose where in the post to place the ads. Only top, middle and bottom, and the alingment.
With Selective Adsense (my first plugin ever), wich I developed following the Whydowork Adsense plugin code, I can control where in the post the Ad will display, the alignment (left, none, right) of it, and which Ad. I also can make it choose randomly which Ad giving it a list to choose from. With the posibility to give more probabilities for some Ads.
Selective Adsense also let me give it a list of probable alignments to choose from. And also allows a php tag to put Ads on templates, giving a list of Ads to choose from (it doesn't support alignments on php tags).
This could containg bugs or security holes, but I provide it "as is". And I wait for your suggestions, comments and bugs/security holes reports.
Usage:
Inside Posts:
Insert this html comment:
<!--SelectiveAdsense(3,1,1,2,3,1)[1,3,2,3,1,3]-->
The comment will be replaced by the Ad. Wich Ad? Three probabilities for Ad#1, one for Ad#2, two for Ad#3 (3,1,1,2,3,1). Aligned how? Two probabilities for left, one for none and three for right [1,3,2,3,1,3].
It replaces until three ads of the same category (Block, Link or Ref) are shown. After that, comments for this cagegory are replaced by null string. The other categories continues to be raplaced.
On Template:
Insert this php function: SelectiveAdsense('4,1,1,2,3,1');
The Ad will be shown at that place as is (no align, nothing). Three probabilities for Ad#1, one for Ad#2, one for Ad#3 and one for Ad#4.
Supporting more Ads:
If you want more than 20 Ads, you can change this value on the code:
$MAX_ADS=20;
Download plugin.
http://www.danielmunoz.com.ar/blog/2009/05/27/selective-adsense-wordpress-plugin/
Puerto Paralelo en LabVIEW.
Acá va, especialmente para Oliver, un VI para trabajar con puerto paralelo. Espero que les sirva y cualquier cosa comenten. Saludos.
Paralelo.vi
Permite ver las tensiones en los pines del Puerto paralelo y leer/escribir a sus registros de entrada/salida (DATA, STATUS y CONTROL). Los colores oscuros indican tensiones bajas en los pines (FALSO) y los colores claros tensiones altas (VERDADERO). Algunas de estas tensiones se muestran/controlan invertidas en los registros DATA, STATUS y CONTROL.
Los registros DATA, STATUS y CONTROL se muestran como los ve el software, mientras que los colores se muestran como los ve el hardware.

(Imagen tomada de PC Parallel Port)
![]() |
DATA El valor que el usuario introduzca aquí se escribirá al registro DATA del puerto (PORT+0). Se muestra en formato binario. |
![]() |
CONTROL El valor que el usuario introduzca aquí se escribirá al registro CONTROL del puerto (PORT+2). Se muestra en formato binario. |
![]() |
Actualizar cada (ms) Establece cada cuanto se actualizarán los valores, o sea, cada cuanto se leerá y escribirá el puerto.Recuerde que luego de actualizar el valor en algún control numérico debe presionar ENTER o hacer clic afuera de éste para que el valor se tome por actualizado. |
![]() |
Puerto Dirección del puerto a utilizar.Aunque solo hay 3 puertos predefinidos, se pueden agregar más en caso la PC tuviera otros. |
![]() |
Salir Presione para terminar la ejecución del VI. |
![]() |
13 STATUS_4. |
![]() |
12 STATUS_5. |
![]() |
-11 NOT_STATUS_7 (entra invertido). |
![]() |
10 STATUS_6. |
![]() |
9 DATA_7. |
![]() |
8 DATA_6. |
![]() |
7 DATA_5. |
![]() |
6 DATA_4. |
![]() |
5 DATA_3. |
![]() |
4 DATA_2. |
![]() |
3 DATA_1. |
![]() |
2 DATA_0. |
![]() |
-1 NOT_CONTROL_0 (sale invertido). |
![]() |
25 GND. |
![]() |
24 GND. |
![]() |
23 GND. |
![]() |
22 GND. |
![]() |
21 GND. |
![]() |
20 GND. |
![]() |
19 GND. |
![]() |
18GND. |
![]() |
-17 NOT_CONTROL_3 (sale invertido). |
![]() |
16 CONTROL_2. |
![]() |
15 STATUS_3. |
![]() |
-14 NOT_CONTROL_1 (sale invertido). |
![]() |
STATUS Muestra el valor que hay en el registro STATUS del puerto (PORT+1). Se muestra en formato binario. |

![]() |
In Port.vi C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Platform\portaccess.llb\In Port.vi |
![]() |
In Port8.vi C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Platform\portaccess.llb\In Port8.vi |
![]() |
Out Port.vi C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Platform\portaccess.llb\Out Port.vi |
![]() |
Out Port8.vi C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Platform\portaccess.llb\Out Port8.vi |
Position in Hierarchy

LabVIEW – DataSocket
Para configurar el DataSocket Server hay que ir a:
Inicio->Programas->National Instruments->DataSocket->DataSocket Server Manager.
Una vez ahí hay pararse sobre (por ejemplo) Predefined Data Items, y presionar el botón New Item. La descripción no importa. Hay que llamarlo: waveform_str para que funcione con el VI de ejemplo. O ponerle otro nombre pero después especificar ese nombre en la URL de los instrumentos. Debe ser de tipo String.
Una vez terminada esa configuración hay que cerrar esa ventana y abrir:
Inicio->Programas->National Instruments->DataSocket->DataSocket Server.
Este servidor debe estar abierto todo el tiempo que querramos transmitir los datos, pues se transmiten a través de este servidor. El generador envía los datos a este servidor y el osciloscopio los lee de acá.
1.7.2a.vi
Generador de señales con DataSocket.
Se conecta al DataSocket Server y envía los datos a éste servidor como una string llamada waveform_str.
El programa inicia intentando conectarse con el DataSocket Server en modo write. Si no puede conectarse espera 100ms y vuelve a intentarlo. Esto lo hace hasta que se pueda conectar.
Una vez conectado, genera una señal y la envía al DataSocket Server. Espera 100ms y vuelve a hacer lo mismo.
Si hay algún error al enviar los datos al DataSocket Server o si se presiona el botón stop (SALIR), sale del bucle WHILE, cierra la conexión y termina el programa.
Nota: Debe haber un DataSocket Server funcionando además de este generador y del osciloscopio 1.7.2b para poder conectarse. Ver DataSocket Server para información de como configurarlo.


![]() |
OFF reset signal, if TRUE, resets the phase to the phase control value and the time stamp to zero. The default is FALSE. |
![]() |
OFFSET offset is the DC offset of the signal. The default is 0.0. |
![]() |
FRECUENCIA frequency is the frequency of the waveform in units of hertz. The default is 10. |
![]() |
AMPLITUD amplitude is the amplitude of the waveform. The amplitude is also the peak voltage. The default is 1.0. |
![]() |
FASE phase is the initial phase, in degrees, of the waveform. The default is 0. The VI ignores phase if reset signal is FALSE. |
![]() |
DUTY CICLE square wave duty cycle is the percentage of time a square wave remains high versus low over one period. The VI uses this parameter only if the signal type is a square wave. The default is 50. |
![]() |
Fs Fs is the sampling rate in samples per second. The default is 1000. |
![]() |
NRO. MUESTRAS #s is the number of samples in the waveform. The default is 1000. |
![]() |
SEÑAL Tipo de señal: Senoidal, Triangular, Cuadrada o Diente de Sierra. |
![]() |
stop Salir del programa. |
![]() |
URLDirección del servidor DataSocket y nombre de la variable. |

![]() |
NI_MABase.lvlib:Basic Function Generator.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\measure\masignal.llb\Basic Function Generator.vi |
1.7.2b.vi
Osciloscopio con DataSocket.
Se conecta al DataSocket Server y recibe los datos desde este servidor como una string llamada waveform_str.
El programa inicia intentando conectarse con el DataSocket Server en modo BufferedRead. Si no puede conectarse espera 100ms y vuelve a intentarlo. Esto lo hace hasta que se pueda conectar.
Una vez conectado, lee la señal desde el DataSocket Server y la muestra en el osciloscopio. Luego vuelve a repetir el mismo proceso hasta que haya algún error al recibir los datos del DataSocket Servero si se presiona el botón stop (SALIR). Si ocurre alguna de estas dos cosas sale del bucle WHILE, cierra la conexión y termina el programa.
Nota: Debe haber un DataSocket Server funcionando además de este osciloscopior y del generador 1.7.2a para poder conectarse. Ver DataSocket Server para información de como configurarlo.


![]() |
stop Termina el programa. |
![]() |
Ch1 VOLTS/DIV Canal 1. |
![]() |
Tiempo SEC/DIV de ambos canales (solo se usa el CH1). |
![]() |
Ch1 CH1 ON. |
![]() |
Ch2 CH 2 ON (no se usa). |
![]() |
Ch2 VOLTS/DIV Canal 2 (no se usa). |
![]() |
URL URL del DataSocket Server y nombre de la variable. |
![]() |
Waveform Graph Onda leída del DataSocket. |

![]() |
Osciloscopio.vi
C:\Documents and Settings\Daniel\Mis documentos\facu\5to\Digitales 3\1\Practico\Osciloscopio.vi |
LabVIEW – TCP/IP.
1.7.1a.vi
Servidor TCP/IP.
Envía la onda generada a la IP y puerto especificados.
En primer lugar intenta, cada 100ms, establecer una comunicación a la IP y puerto especificados. Cuando se establece la conexión empieza a enviar los datos.
Para enviar la onda primero la transforma a formato cadena (que es lo que se puede enviar por TCP/IP). Luego envía, también en formato cadena, el tamaño de esa cadena. La cadena que contiene el tamaño es un cast de int a vector de chars. Son 4 bytes. A continuación envía la cadena con los datos.
Este proceso (de envío) se repite cada 100ms siempre y cuando no haya error en la comunicación ni se presione el botón STOP.
Al finalizar, cierra la conexión TCP/IP.


![]() |
OFF reset signal, if TRUE, resets the phase to the phase control value and the time stamp to zero. The default is FALSE. |
![]() |
OFFSET offset is the DC offset of the signal. The default is 0.0. |
![]() |
FRECUENCIA frequency is the frequency of the waveform in units of hertz. The default is 10. |
![]() |
AMPLITUD amplitude is the amplitude of the waveform. The amplitude is also the peak voltage. The default is 1.0. |
![]() |
FASE phase is the initial phase, in degrees, of the waveform. The default is 0. The VI ignores phase if reset signal is FALSE. |
![]() |
DUTY CICLE square wave duty cycle is the percentage of time a square wave remains high versus low over one period. The VI uses this parameter only if the signal type is a square wave. The default is 50. |
![]() |
Fs Fs is the sampling rate in samples per second. The default is 1000. |
![]() |
NRO. MUESTRAS #s is the number of samples in the waveform. The default is 1000. |
![]() |
SEÑAL |
![]() |
stop Se presiona este botón para salir. |
![]() |
Puerto Puerto remoto al que se enviarán los datos. |
![]() |
ip IP remota donde se enviarán los datos. |

![]() |
NI_MABase.lvlib:Basic Function Generator.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\measure\masignal.llb\Basic Function Generator.vi |
1.7.1b.vi
Cliente TCP/IP.
Escucha por conexiones entrantes TCP/IP en el puerto 2056.
Cuando se establece una conexión (supone que quien la estableció es el servidor 1.7.1a), lee primero los 4 bytes, que se supone contienen el tamaño de los datos que van a ser recibidos (porque así los envió 1.7.1a). Luego lee esa cantidad de bytes y transforma la cadena leída en un dato tipo waveform, que era el tipo de datos original en el servidor. El tipo de datos (la constante waveform) se crea afuera del bucle, para que no se tenga que estar creando una constante en cada repetición del bucle (para optimizar nomás). Una vez obtenida la forma de onda se la muestra en el osciloscopio.
Esto se repite hasta que:
1- Falle la lectura del tamaño de los datos.
2- Falle la lectura de los datos.
3- Falle la conversión de los datos a formato waveform.
4- Se presione el botón stop (SALIR).
Cuando ocurre alguna de las 4 cosas arriba mencionadas se cierra la conexión y termina el programa.


![]() |
stop SALIR: Presionando este botón se termina el programa. |
![]() |
Ch1 VOLTS/DIV de CH1. |
![]() |
Tiempo SEG/DIV de ambos canales. |
![]() |
Ch1 CH1 ON. |
![]() |
Ch2 CH2 ON. |
![]() |
Ch2 VOLTS/DIV de CH2. |
![]() |
Waveform Graph Gráfico de la forma de onda recibida. |

![]() |
Osciloscopio.vi
C:\Documents and Settings\Daniel\Mis documentos\facu\5to\Digitales 3\1\Practico\Osciloscopio.vi |
![]() |
TCP Listen.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Utility\tcp.llb\TCP Listen.vi |
LabVIEW – Comunicación RS-232 entre PCs.
1.5.1.vi
Envía o Recibe un archivo por puerto serie.
La acción predeterminada es Recibir. Cuando se está en modo recepción, se comprueba cada 500ms a ver si hay datos en el puerto. Como la transmisión se hace con el protocolo DTR/DSR, cuando se llena el buffer de entrada, la PC transmisora espera para seguir enviando. El cable debe ser FULL DUPLEX.
Cuando se cambia el modo a Enviar se abre un cuadro de diálogo pidiendo el archivo a enviar y lo envía.
El transmisor también envía el hash MD5 para que el receptor verifique que la transferencia fue correcta.


![]() |
stop |
![]() |
Puerto VISA resource name specifies the resource to be opened. This control also specifies the session and class. |
![]() |
Acción Acción: Enviar o Recibir el archivo. |
![]() |
Baudios Velocidad de transmisión. Ambos programas, emisor y receptor, deben tener configurada la misma velocidad de transimisión. |



![]() |
Merge Errors.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Utility\error.llb\Merge Errors.vi |
![]() |
File Dialog
File Dialog |
![]() |
RecibirArchivoPuerto.vi
C:\Documents and Settings\Daniel\Mis documentos\facu\5to\Digitales 3\1\Practico\RecibirArchivoPuerto.vi |
![]() |
EnviarArchivoPuerto.vi
C:\Documents and Settings\Daniel\Mis documentos\facu\5to\Digitales 3\1\Practico\EnviarArchivoPuerto.vi |
EnviarArchivoPuerto.vi
Envía un archivo por puerto serie.
Abre el archivo en modo binario y extrae su contenido. Si hay error al leer el archivo, termina el programa. Sino, extrae el nombre y MD5 del archivo y lo une con el contenido en un cluster.
En primer lugar se envía el tamaño del cluster recién armado y luego el cluster.
Se cierra la conexión y termina el programa.

![]() |
Archivo a enviar Ruta del archivo a enviar. |
![]() |
Puerto (COM1) VISA resource name specifies the resource to be opened. This control also specifies the session and class. |
![]() |
Baudios (57600) Velocidad de transferencia. Debe ser la misma que en la otra PC. |
![]() |
error in error in can accept error information wired from VIs previously called. Use this information to decide if any functionality should be bypassed in the event of errors from other VIs.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
status status is TRUE (X) if an error occurred or FALSE (checkmark) to indicate a warning or that no error occurred.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
code code is the error or warning code.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
source source describes the origin of the error or warning.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
error out error in can accept error information wired from VIs previously called. Use this information to decide if any functionality should be bypassed in the event of errors from other VIs.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
status status is TRUE (X) if an error occurred or FALSE (checkmark) to indicate a warning or that no error occurred.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
code code is the error or warning code.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
source source describes the origin of the error or warning.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |



![]() |
MD5Checksum File.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Utility\MD5Checksum.llb\MD5Checksum File.vi |
![]() |
VISA Configure Serial Port (Instr).vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Instr\_visa.llb\VISA Configure Serial Port (Instr).vi |
![]() |
VISA Configure Serial Port
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Instr\_visa.llb\VISA Configure Serial Port |
RecibirArchivoPuerto.vi
Recibe un archivo por puerto serie.
Se abre la conexión y se comprueba si hay bytes esperando para ser leídos en el puerto (estarían almacenados en el caché).
Si no hay bytes esperando, se cierra la conexión y termina.
Si hay bytes esperando, se leen 4 bytes, que representa un int y que contiene el tamaño del cluster, en bytes, que se espera recibir. Luego se lee esa cantidad de bytes desde el puerto y se arma el cluster. Se pide al usuario un nombre de archivo, sugiriendole el mismo nombre que tenía en la otra PC. Se crea el archivo con el nombre sugerido, se guardan en él los datos y se cierra el archivo. Se comprueba si el hash MD5 del archivo recién creado es el mismo que el recibido de la otra PC. Sino, se envía un mensaje de error.
Se podrían hacer otras comprobaciones, como por ejemplo si el usuario canceló la operación presionando cancelar cuando se le pidió un nombre para el archivo. Pero si ocurre esto, simplemente habrá un error al crear el archivo con un nombre no válido en el siguiente paso y se propagará por los siguientes SubVIs. Y simplemente se terminará con un error.

![]() |
Puerto (COM1) VISA resource name specifies the resource to be opened. This control also specifies the session and class. |
![]() |
Baudios (57600) Velocidad de transferencia. Debe ser la misma que en la otra PC. |
![]() |
error in error in describes error conditions that occur before this VI or function runs. |
![]() |
status status is TRUE (X) if an error occurred before this VI or function ran or FALSE (checkmark) to indicate a warning or that no error occurred before this VI or function ran. The default is FALSE. |
![]() |
code code is the error or warning code. The default is 0. |
![]() |
source source specifies the origin of the error or warning and is, in most cases, the name of the VI or function that produced the error or warning. The default is an empty string. |
![]() |
error out error in can accept error information wired from VIs previously called. Use this information to decide if any functionality should be bypassed in the event of errors from other VIs.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
status status is TRUE (X) if an error occurred or FALSE (checkmark) to indicate a warning or that no error occurred.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
code code is the error or warning code.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |
![]() |
source source describes the origin of the error or warning.
Right-click the error in control on the front panel and select Explain Error or Explain Warning from the shortcut menu for more information about the error. |





![]() |
VISA Configure Serial Port
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Instr\_visa.llb\VISA Configure Serial Port |
![]() |
VISA Configure Serial Port (Instr).vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Instr\_visa.llb\VISA Configure Serial Port (Instr).vi |
![]() |
Merge Errors.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Utility\error.llb\Merge Errors.vi |
![]() |
MD5Checksum File.vi
C:\Archivos de programa\National Instruments\LabVIEW 8.6\vi.lib\Utility\MD5Checksum.llb\MD5Checksum File.vi |
![]() |
File Dialog
File Dialog |
































i.png)

