/* Read tag name and value from a database table and write to the * corresponding real-time data points * The table has at least two columns: * pointname - a character string (VARCHAR) * pointvalue - a number or a character string */ require ("Application"); require ("ODBCSupport"); class DatabaseRead Application { /* User-defined values, may be changed as needed. */ DSN = "test"; user = "test"; password = "test"; tablename = "points"; /* These values get defined by the program.*/ conn; env; tableclass; is_connected; } /* Connect to the DSN and create a class that maps the table. */ method DatabaseRead.Connect () { /* Create the ODBC environment and connection */ .env = ODBC_AllocEnvironment(); .conn = .env.AllocConnection(); /* Attempt the connection. */ ret = .conn.Connect (.DSN, .user, .password); if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) error (.conn.GetDiagRec()); /* Create a class from the table */ .tableclass = .conn.ClassFromTable (#DatabaseReadClass, nil, .tablename); .is_connected = t; princ ("DatabaseRead connection established.\n"); } method DatabaseRead.Disconnect () { .is_connected = nil; if (instance_p(.conn)) { princ ("DatabaseRead connection lost.\n"); .conn.Disconnect(); destroy (.conn); } if (instance_p(.env)) { destroy (.env); } } method DatabaseRead.TryConnect() { if (!.is_connected) { try { .Connect(); } catch { princ ("DatabaseRead connection failed.\n"); .is_connected = nil; } } } /* Reload information from a database record into the DataHub. This is being called on a timer. We re-query the database for the given record, then update the DataHub points simply by assigning them. */ method DatabaseRead.Update () { local result; if (.is_connected) { try { result = .conn.QueryToClass (.tableclass, string ("select * from ", .tablename)); with x in result do { datahub_write (x.pointname, x.pointvalue); } } catch { .Disconnect(); } } } /* Write the 'main line' of the program here. */ method DatabaseRead.constructor () { .TimerEvery (1, `(@self).Update()); //SETUP A TIMER THAT WILL RETRY TE DATABASE CONNECTION IF IT GOES OFFLINE .TimerEvery (5, `(@self).TryConnect()); .TryConnect(); } /* Any code to be run when the program gets shut down. */ method DatabaseRead.destructor () { .Disconnect(); } /* Start the program by instantiating the class. If your * constructor code does not create a persistent reference to * the instance (self), then it will be destroyed by the * garbage collector soon after creation. If you do not want * this to happen, assign the instance to a global variable, or * create a static data member in your class to which you assign * 'self' during the construction process. ApplicationSingleton() * does this for you automatically. */ ApplicationSingleton (DatabaseRead);