/* * This script demonstrates the use of the threaded ODBC interface to insert * data into a database based on a timer. */ require ("Application"); require ("ODBCThreadSupport"); class ODBCThreadDemo Application { DSN = "test"; // The DSN name to use for the database connection username = "test"; // The user name for connecting to the database password = "test"; // The password for connecting to the database tablename = "andrew5"; // The name of the database table cachefile = "c:/tmp/testcache.txt"; // Base name for the disk cache file tableclass; thread; } /* This method will be called every time the connection is established to the database. * If there is something we only want to perform on the first connection, we can test * is_first_connect to perform the code only once. */ method ODBCThreadDemo.onConnect() { princ ("Connection succeeded\n"); if (.thread.is_first_connect) { // Start the sequence defined by the AddInitStage calls in the constructor .thread.BeginAsyncInit(); } } /* If we get a connection attempt failure, or the connection fails after having been * connected, this method is called. */ method ODBCThreadDemo.onConnectFail() { princ ("Connection closed: ", SQLResult.Description, "\n"); } /* Map the table in the set of table definitions that matches the name in .tablename * into a Gamma class. This lets us easily convert between class instances and rows * in the table. */ method ODBCThreadDemo.mapTable(name, tabledefinitions) { .tableclass = .thread.ClassFromTable(name, tabledefinitions); } /* Set up the timer or event handler functions to write to the table. */ method ODBCThreadDemo.startLogging() { .TimerEvery(1, `(@self).writeData(#$DataSim:Sine)); .OnChange(#$DataSim:Square, `(@self).writeData(this)); } method ODBCThreadDemo.writeData(pointsymbol) { local row = new (.tableclass); local pttime, ptltime; local timestring; // Generate a timestamp in database-independent format to the millisecond. // Many databases strip the milliseconds from a timestamp, but it is harmless // to provide them in case the database can store them. pttime = WindowsTimeToUnixTime(PointMetadata(pointsymbol).timestamp); ptltime = localtime(pttime); timestring = format("{ts '%04d-%02d-%02d %02d:%02d:%02d.%03d'}", ptltime.year+1900, ptltime.mon+1, ptltime.mday, ptltime.hour, ptltime.min, ptltime.sec, (pttime % 1) * 1000); // Fill the row. Since we mapped the table into a Gamma class, we can access // the columns in the row as member variables of the mapped class. row.ptname = string(pointsymbol); row.ptvalue = eval(pointsymbol); row.pttime = timestring; // Perform the insertion. In this case we are providing no callback on completion. .thread.Insert(row, nil); } /* Print some statistics for the database thread. */ method ODBCThreadDemo.printStats () { // NCommands = the number of commands successfully transmitted to the database // thread or stored in cache for later transmission // NRejected = the number of commands that could not be transmitted or stored // NStoredPrimary = the number of commands stored in level 1 cache // NStoredSecondary = the number of commands stored in level 2 cache // NForwarded = the number of commands executed from level 1 or level 2 cache // NForwardFail = the number of commands that failed while forwarding from cache princ ("Commands sent: ", .thread.NCommands, " (", .thread.NRejected, " rejected) Stored: ", .thread.NStoredPrimary, "/", .thread.NStoredSecondary, " Forwarded: ", .thread.NForwarded, " (", .thread.NForwardFail, " failed)", "\n"); } /* Write the 'main line' of the program here. */ method ODBCThreadDemo.constructor () { // Create and configure the database connection object .thread = new ODBCThread(); .thread.Configure(.DSN, .username, .password, STORE_AND_FORWARD, .cachefile); // If we wanted to delete the table on the first connection after the script starts, // do this. Be careful - re-running the script will start over and delete the table // again. .thread.AddInitStage(format("drop table %s", .tablename), nil, t); // Specify the initialization steps that must be executed in sequence // First, create the table if it does not exist. The 't' in the onFail argument says // to ignore errors and continue with the next stage. .thread.AddInitStage(format("create table %s (ptid int auto_increment primary key, ptname varchar(64), ptvalue double, pttime datetime )", .tablename), nil, t); // Query the table and map it to a class for eash insertion. We want to run an asynchronous event // within the asynchronous initialization stage, so to do that we specify the special method // cbInitStage as the callback function of our asynchronous event (GetTableInfo). We deal with // the return from the GetTableInfo in the onSuccess argument of the init stage. .thread.AddInitStage(`(@.thread).GetTableInfo("", "", (@.tablename), "TABLE,VIEW", `(@.thread).cbInitStage()), `(@self).mapTable(@.tablename, SQLTables), nil); // Do not start writing data to the table until we have successfully created and mapped // the table to a class. If we wanted to start writing data immediately, then we would // create the table class beforehand instead of querying the database for the table // definition. Then, even if the database were unavailable we could still cache to the // local disk until the database was ready. .thread.AddInitStage(nil, `(@self).startLogging(), nil); // Set up the callback functions for various events from the database thread .thread.OnConnectionSucceeded = `(@self).onConnect(); .thread.OnConnectionFailed = `(@self).onConnectFail(); .thread.OnFileSystemError = `princ("File System Error: ", SQLResult, "\n"); .thread.OnODBCError = `princ("ODBC Error: ", SQLResult, "\n"); .thread.OnExecuteStored = nil; // Now that everything is configured, start the thread and begin connecting. All of the // logic now will be driven through the onConnect callback and then through the init // stages. .thread.Start(); // Print statistics about the database thread every 2 seconds. .TimerEvery(2, `(@self).printStats()); } /* Any code to be run when the program gets shut down. */ method ODBCThreadDemo.destructor () { } /* Start the program by instantiating the class. */ ApplicationSingleton (ODBCThreadDemo);
Copyright © 1995-2010 by Cogent Real-Time Systems, Inc. All rights reserved.