Beispiel #1
0
static void process_create_schedule(const char * string, const jsmntok_t * tokens)
{
   char zs[128];
   char query[2048];
   char uid[16], stp_indicator[2], tsc[16];
   MYSQL_RES * result0;
   MYSQL_ROW row0;

   stats[ScheduleCreate]++;

   EXTRACT("CIF_stp_indicator", stp_indicator);
   if(stp_indicator[0] == 'C') return;
   EXTRACT("CIF_train_uid", uid);
   EXTRACT("schedule_start_date", zs);
   time_t start_date = parse_datestamp(zs);
   EXTRACT("schedule_end_date", zs);
   time_t end_date   = parse_datestamp(zs);
   EXTRACT("CIF_train_service_code", tsc);

   sprintf(query, "SELECT id FROM cif_schedules WHERE CIF_stp_indicator = '%s' AND CIF_train_uid = '%s' AND schedule_start_date = %ld AND schedule_end_date = %ld AND CIF_train_service_code = '' AND update_id = %s", stp_indicator, uid, start_date, end_date, last_update_id);

   if(!db_query(query))
   {
      result0 = db_store_result();
      if((row0 = mysql_fetch_row(result0)))
      {
         sprintf(query, "UPDATE cif_schedules SET CIF_train_service_code = '%s' WHERE id = %s", tsc, row0[0]);
         db_query(query);
         stats[TSCUpdate]++;
         _log(GENERAL, "Updated schedule %s (%s %s) with TSC \"%s\".", row0[0], uid, stp_indicator, tsc);
      }
      mysql_free_result(result0);
   }        
}
Beispiel #2
0
static word table_exists(const char * const table_like)
{
   MYSQL_RES * result;
   char query[256];
   sprintf(query, "SHOW TABLES LIKE '%s'", table_like);
   if(!db_query(query))
   {
      result = db_store_result();
      word num_rows = mysql_num_rows(result);
      mysql_free_result(result);
      if(num_rows) return true;
   }
   return false;
}
Beispiel #3
0
static void process_update_schedule(const char * string, const jsmntok_t * tokens)
{
   char query[1024], CIF_train_uid[16], schedule_start_date[16], schedule_end_date[16], CIF_stp_indicator[8]; 
   dword id;
   MYSQL_RES * result0;
   MYSQL_ROW row0;

   EXTRACT("CIF_train_uid", CIF_train_uid);
   EXTRACT("schedule_start_date", schedule_start_date);
   EXTRACT("schedule_end_date", schedule_end_date);
   EXTRACT("CIF_stp_indicator", CIF_stp_indicator);

   time_t schedule_start_date_stamp = parse_datestamp(schedule_start_date);
   // time_t schedule_end_date_stamp   = parse_datestamp(schedule_end_date);

   sprintf(query, "SELECT id FROM cif_schedules WHERE update_id = 0 AND CIF_train_uid = '%s' AND CIF_stp_indicator = '%s' AND schedule_start_date = %ld AND deleted > %ld", CIF_train_uid, CIF_stp_indicator, schedule_start_date_stamp, time(NULL));
   if(!db_query(query))
   {
      result0 = db_store_result();
      word num_rows = mysql_num_rows(result0);
      if(num_rows != 1)
      {
         _log(MAJOR, "Update for schedule \"%s\" found %d existing records.  Delete phase skipped.", CIF_train_uid, num_rows);
         jsmn_dump_tokens(string, tokens, 0);
         if(num_rows) stats[UpdateDeleteMulti]++; else stats[UpdateDeleteMiss]++;
      }
      else
      {
         row0 = mysql_fetch_row(result0);
         id = atol(row0[0]);
         //sprintf(query, "DELETE FROM cif_schedule_locations WHERE cif_schedule_id = %ld", id);
         //db_query(query);
         sprintf(query, "UPDATE cif_schedules SET deleted = %ld WHERE id = %ld", time(NULL), id);
         db_query(query);
      }
      mysql_free_result(result0);
   }

   // Create phase.
   process_create_schedule(string, tokens, true);

   _log(DEBUG, "Updated schedule \"%s\".", CIF_train_uid);
}
Beispiel #4
0
static char * tiploc_name(const char * const tiploc)
{
   // Not re-entrant
   char query[256];
   MYSQL_RES * result0;
   MYSQL_ROW row0;
   static char result[128];

   sprintf(query, "select fn from corpus where tiploc = '%s'", tiploc);
   db_query(query);
   result0 = db_store_result();
   if((row0 = mysql_fetch_row(result0)) && row0[0][0]) 
   {
      strncpy(result, row0[0], 127);
      result[127] = '\0';
   }
   else
   {
      strcpy(result, tiploc);
   }
   mysql_free_result(result0);

   return result;
}
Beispiel #5
0
static void process_create_schedule(const char * string, const jsmntok_t * tokens, const word update)
{
   // update true indicates this is as the result of a VSTP update.
   char zs[128], zs1[128];
   char query[2048];
   word i;
   char uid[16], stp_indicator[2];
   char signalling_id[8];

   if(debug) jsmn_dump_tokens(string, tokens, 0);

   time_t now = time(NULL);
   sprintf(query, "INSERT INTO cif_schedules VALUES(0, %ld, %lu", now, NOT_DELETED); // update_id == 0 => VSTP

   EXTRACT_APPEND_SQL("CIF_bank_holiday_running");

   //EXTRACT_APPEND_SQL("CIF_stp_indicator");
   EXTRACT("CIF_stp_indicator", stp_indicator);
   sprintf(zs1, ", '%s'", stp_indicator); strcat(query, zs1); 

   //EXTRACT_APPEND_SQL("CIF_train_uid");
   EXTRACT("CIF_train_uid", uid);
   sprintf(zs1, ", '%s'", uid); strcat(query, zs1); 

   EXTRACT_APPEND_SQL("applicable_timetable");

   EXTRACT_APPEND_SQL("atoc_code");
   // EXTRACT_APPEND_SQL("traction_class");
   EXTRACT_APPEND_SQL("uic_code");
   EXTRACT("schedule_days_runs", zs);
   for(i=0; i<7; i++)
   {
      strcat(query, ", ");
      strcat(query, (zs[i]=='1')?"1":"0");
   }

   EXTRACT("schedule_end_date", zs);
   time_t z = parse_datestamp(zs);
   sprintf(zs1, ", %ld", z);
   strcat(query, zs1);

   EXTRACT("signalling_id", signalling_id);
   sprintf(zs1, ", '%s'", signalling_id); strcat(query, zs1);

   EXTRACT_APPEND_SQL("CIF_train_category");
   EXTRACT_APPEND_SQL("CIF_headcode");
   //EXTRACT_APPEND_SQL("CIF_course_indicator");
   EXTRACT_APPEND_SQL("CIF_train_service_code");
   EXTRACT_APPEND_SQL("CIF_business_sector");
   EXTRACT_APPEND_SQL("CIF_power_type");
   EXTRACT_APPEND_SQL("CIF_timing_load");
   EXTRACT_APPEND_SQL("CIF_speed");
   EXTRACT_APPEND_SQL("CIF_operating_characteristics");
   EXTRACT_APPEND_SQL("CIF_train_class");

   EXTRACT_APPEND_SQL("CIF_sleepers");

   EXTRACT_APPEND_SQL("CIF_reservations");
   EXTRACT_APPEND_SQL("CIF_connection_indicator");
   EXTRACT_APPEND_SQL("CIF_catering_code");
   EXTRACT_APPEND_SQL("CIF_service_branding");
   
   EXTRACT("schedule_start_date", zs);
   z = parse_datestamp(zs);
   sprintf(zs1, ", %ld", z);
   strcat(query, zs1);

   EXTRACT_APPEND_SQL("train_status");

   strcat(query, ", 0, '', '')"); // id filled by MySQL

   if(!db_query(query))
   {
      stats[update?UpdateCreate:Create]++;
   }
      
   dword id = db_insert_id();

   word index = jsmn_find_name_token(string, tokens, 0, "schedule_location");
   word locations = tokens[index+1].size;

   huyton_flag = false;
   index += 2;
   for(i = 0; i < locations; i++)
   {
      index = process_create_schedule_location(string, tokens, index, id);
   }

   if(stp_indicator[0] == 'O' && (signalling_id[0] == '\0' || signalling_id[0] == ' '))
   {
      // Search db for schedules with a deduced headcode, and add it to this one, status = D
      // Bug:  Really this should also look for schedules with a signalling_id
      MYSQL_RES * result;
      MYSQL_ROW row;
      sprintf(query, "SELECT deduced_headcode FROM cif_schedules WHERE CIF_train_uid = '%s' AND deduced_headcode != '' AND schedule_end_date > %ld ORDER BY created DESC", uid, now - (64L * 24L * 60L * 60L));
      if(!db_query(query))
      {
         result = db_store_result();
         if((row = mysql_fetch_row(result)))
         {
            sprintf(query, "UPDATE cif_schedules SET deduced_headcode = '%s', deduced_headcode_status = 'D' WHERE id = %ld", row[0], id);
            db_query(query);
            _log(DEBUG, "Deduced headcode \"%s\" applied to overlay schedule %ld, uid \"%s\".", row[0], id, uid);
            stats[HeadcodeDeduced]++;
         }
         else
         {
            _log(DEBUG, "Deduced headcode not found for overlay schedule %ld, uid \"%s\".", id, uid);
         }
         mysql_free_result(result);
      }
   }

   sprintf(query, "UPDATE status SET last_vstp_processed = %ld", now);
   db_query(query);

   if(huyton_flag) 
   {
      _log(DEBUG, "Created schedule %ld%s.  +++ Passes Huyton +++", id, update?" as a result of an Update transaction":"");
   }

   if(huyton_flag)
   {
      char title[64], message[512];
      MYSQL_RES * result0;
      MYSQL_ROW row0;
      char stp[4];
      sprintf(title, "Huyton Schedule Created.");
      sprintf(message, "Created schedule which passes Huyton.");
      if(update) strcat(message, "  Due to a VSTP Update transaction.");
      strcat(message, "\n\n");
      EXTRACT("CIF_train_uid", zs1);
      EXTRACT("CIF_stp_indicator", stp);
      sprintf(zs, "%ld (%s %s) ", id, zs1, stp);
      EXTRACT("signalling_id", zs1);
      strcat(zs, zs1);
      sprintf(query, "SELECT tiploc_code, departure FROM cif_schedule_locations WHERE record_identity = 'LO' AND cif_schedule_id = %ld", id);
      if(!db_query(query))
      {
         result0 = db_store_result();
         if((row0 = mysql_fetch_row(result0)))
         {
            sprintf(zs1, " %s %s to ", show_time_text(row0[1]), tiploc_name(row0[0]));
            strcat(zs, zs1);
         }
         mysql_free_result(result0);
      }
      sprintf(query, "SELECT tiploc_code FROM cif_schedule_locations WHERE record_identity = 'LT' AND cif_schedule_id = %ld", id);
      if(!db_query(query))
      {
         result0 = db_store_result();
         if((row0 = mysql_fetch_row(result0)))
         {
            strcat (zs, tiploc_name(row0[0]));
         }
         mysql_free_result(result0);
      }

      strcat(message, zs);

      sprintf(query, "SELECT schedule_start_date, schedule_end_date, CIF_stp_indicator FROM cif_schedules WHERE id = %ld", id);
      if(!db_query(query))
      {
         result0 = db_store_result();
         if((row0 = mysql_fetch_row(result0)))
         {
            dword from = atol(row0[0]);
            dword to   = atol(row0[1]);
            if(from == to)
            {
               strcat(message, "  Runs on ");
               strcat(message, date_text(from, true));
            }
            else
            {
               strcat(message, "  Runs from ");
               strcat(message, date_text(from, true));
               strcat(message, " to ");
               strcat(message, date_text(to,   true));
            }
            if(row0[2][0] == 'C') strcat(message, "  CANCELLED");
            strcat(message, "\n");
         }
         mysql_free_result(result0);
      }
      sprintf(query, "SELECT departure, arrival, pass, tiploc_code FROM cif_schedule_locations WHERE (tiploc_code = 'HUYTON' OR tiploc_code = 'HUYTJUN') AND cif_schedule_id = %ld", id);
      if(!db_query(query))
      {
         result0 = db_store_result();
         while((row0 = mysql_fetch_row(result0)))
         {
            char where[32], z[128];
            if(row0[3][4] == 'J') strcpy(where, "Huyton Junction"); else strcpy(where, "Huyton"); 
            if(row0[0][0]) 
            {
               sprintf(z, "Depart %s at %s.\n", where, row0[0]);
               strcat(message, z);
            }
            else if(row0[1][0])
            {
               sprintf(z, "Arrive %s at %s.\n", where, row0[1]); 
               strcat(message, z);
            }
            else if(row0[2][0])
            {
               sprintf(z, "Pass %s at %s.\n", where, row0[2]);
               strcat(message, z);
            }
         }
         mysql_free_result(result0);
      }
      email_alert(NAME, BUILD, title, message);
   }
}
Beispiel #6
0
static void process_delete_schedule(const char * string, const jsmntok_t * tokens)
{
   char query[1024], CIF_train_uid[16], schedule_start_date[16], schedule_end_date[16], CIF_stp_indicator[8]; 
   dword id;
   word update_id;
   MYSQL_RES * result0;
   MYSQL_ROW row0;

   word deleted = 0;

   EXTRACT("CIF_train_uid", CIF_train_uid);
   EXTRACT("schedule_start_date", schedule_start_date);
   EXTRACT("schedule_end_date", schedule_end_date);
   EXTRACT("CIF_stp_indicator", CIF_stp_indicator);

   time_t schedule_start_date_stamp = parse_datestamp(schedule_start_date);
   time_t schedule_end_date_stamp   = parse_datestamp(schedule_end_date);

   // Find the id
   sprintf(query, "SELECT id, update_id FROM cif_schedules where CIF_train_uid = '%s' and schedule_start_date = '%ld' and schedule_end_date = %ld and CIF_stp_indicator = '%s' AND update_id = 0 AND deleted > %ld",
           CIF_train_uid, schedule_start_date_stamp, schedule_end_date_stamp, CIF_stp_indicator, time(NULL));

   // DO WE NEED DAYS RUNS AS WELL????
   // Note:  Only find VSTP ones.

   if (db_query(query))
   {
      db_disconnect();
      return;
   }

   result0 = db_store_result();
   word num_rows = mysql_num_rows(result0);

   if(num_rows > 1)
   {
      char zs[256];
      sprintf(zs, "Delete schedule found %d matches.", num_rows);
      _log(MAJOR, zs);
      jsmn_dump_tokens(string, tokens, 0);
      stats[DeleteMulti]++;
      // Bodge!
      query[7] = '*'; query[8] = ' ';
      dump_mysql_result_query(query);
   }
 
   while((row0 = mysql_fetch_row(result0)) && row0[0]) 
   {
      id = atol(row0[0]);
      update_id = atoi(row0[1]);

      //sprintf(query, "DELETE FROM cif_schedule_locations WHERE cif_schedule_id = %ld", id);
      //if(!db_query(query))
      //{
      //}

      sprintf(query, "UPDATE cif_schedules SET deleted = %ld where id = %ld", time(NULL), id);
   
      if(!db_query(query))
      {
         deleted++;
         if(update_id)
         {
            // Can never happen!
            _log(MAJOR, "Deleted non-VSTP schedule %ld.", id);
         }
         else
         {
            _log(DEBUG, "Deleted VSTP schedule %ld \"%s\".", id, CIF_train_uid);
         }
      }
   }
   mysql_free_result(result0);

   if(deleted) 
   {
      stats[DeleteHit]++;
   }
   else
   {
      _log(MAJOR, "Delete schedule miss.");
      jsmn_dump_tokens(string, tokens, 0);
      stats[DeleteMiss]++;
   }
}
Beispiel #7
0
int main(int argc, char **argv)
{
   char config_file_path[256];
   opt_filename = NULL;
   opt_url = NULL;
   fetch_all = false;
   test_mode = false;
   verbose = false;
   opt_insecure = false;
   used_insecure = false;

   strcpy(config_file_path, "/etc/openrail.conf");
   word usage = false;
   int c;
   while ((c = getopt (argc, argv, ":c:u:f:tpih")) != -1)
      switch (c)
      {
      case 'c':
         strcpy(config_file_path, optarg);
         break;
      case 'u':
         if(!opt_filename) opt_url = optarg;
         break;
      case 'f':
         if(!opt_url) opt_filename = optarg;
         break;
      case 'a':
         fetch_all = true;
         break;
      case 't':
         test_mode = true;
         break;
      case 'p':
         verbose = true;
         break;
      case 'i':
         opt_insecure = true;
         break;
      case 'h':
         usage = true;
         break;
      case ':':
         break;
      case '?':
      default:
         usage = true;
      break;
      }

   char * config_fail;
   if((config_fail = load_config(config_file_path)))
   {
      printf("Failed to read config file \"%s\":  %s\n", config_file_path, config_fail);
      usage = true;
   }

   if(usage) 
   {
      printf("%s %s  Usage: %s [-c /path/to/config/file.conf] [-u <url> | -f <path> | -a] [-t | -r] [-p][-i]\n", NAME, BUILD, argv[0]);
      printf(
             "-c <file>  Path to config file.\n"
             "Data source:\n"
             "default    Fetch latest update.\n"
             "-u <url>   Fetch from specified URL.\n"
             "-f <file>  Use specified file.  (Must already be decompressed.)\n"
             "Actions:\n"
             "default    Apply data to database.\n"
             "-t         Report datestamp on download or file, do not apply to database.\n"
             "Options:\n"
             "-i         Insecure.  Circumvent certificate checks if necessary.\n"
             "-p         Print activity as well as logging.\n"
             );
      exit(1);
   }

   char zs[1024];

   start_time = time(NULL);

   debug = *conf[conf_debug];
  
   _log_init(debug?"/tmp/tscdb.log":"/var/log/garner/tscdb.log", (debug?1:(verbose?4:0)));
   
   _log(GENERAL, "");
   _log(GENERAL, "%s %s", NAME, BUILD);
   
   // Enable core dumps
   struct rlimit limit;
   if(!getrlimit(RLIMIT_CORE, &limit))
   {
      limit.rlim_cur = RLIM_INFINITY;
      setrlimit(RLIMIT_CORE, &limit);
   }
   
   int i;
   for(i = 0; i < MATCHES; i++)
   {
      if(regcomp(&match[i], match_strings[i], REG_ICASE + REG_EXTENDED))
      {
         sprintf(zs, "Failed to compile regex match %d", i);
         _log(MAJOR, zs);
      }
   }
   
   // Initialise database
   if(db_init(conf[conf_db_server], conf[conf_db_user], conf[conf_db_password], conf[conf_db_name])) exit(1);

   {
      word e;
      if((e=database_upgrade(cifdb)))
      {
         _log(CRITICAL, "Error %d in upgrade_database().  Aborting.", e);
         exit(1);
      }
   }

   run = 1;
   tiploc_ignored = false;

   // Zero the stats
   {
      word i;
      for(i = 0; i < MAXStats; i++) { stats[i] = 0; }
   }

   if(fetch_file())
   {
      if(opt_url || opt_filename)
      {
         _log(GENERAL, "Failed to find data.");
         exit(1);
      }
      {
         char report[256];
         _log(GENERAL, "Failed to fetch file.");
         
         sprintf(report, "Failed to collect timetable update after %lld attempts.", stats[Fetches]);
         email_alert(NAME, BUILD, "Timetable Update Failure Report", report);
      }
      exit(1);
   }

   char in_q = 0;
   char b_depth = 0;

   size_t ibuf = 0;
   size_t iobj = 0;
   size_t buf_end;

   // Determine applicable update
   {
      MYSQL_RES * result0;
      MYSQL_ROW row0;
      last_update_id[0] = '\0';
      if(!db_query("SELECT MAX(id) FROM updates_processed"))
      {
         result0 = db_store_result();
         if((row0 = mysql_fetch_row(result0)))
         {
            strcpy(last_update_id, row0[0]);
         }
         mysql_free_result(result0);
      }
   }

   if(last_update_id[0] == '\0')
   {
      _log(CRITICAL, "Failed to determine last update id from database.");
      exit(1);
   }


   if(test_mode)
   {
   }
   else
   {
      char c, pc;
      pc = 0;
      // Run through the file splitting off each JSON object and passing it on for processing.

      // DB may have dropped out due to long delay
      (void) db_connect();
      if(db_start_transaction()) _log(CRITICAL, "Failed to initiate database transaction.");
      while((buf_end = fread(buffer, 1, MAX_BUF, fp_result)) && run && !db_errored)
      {
         for(ibuf = 0; ibuf < buf_end && run && !db_errored; ibuf++)
         {
            c = buffer[ibuf];
            if(c != '\r' && c != '\n') 
            {
               obj[iobj++] = c;
               if(iobj >= MAX_OBJ)
               {
                  _log(CRITICAL, "Object buffer overflow!");
                  exit(1);
               }
               if(c == '"' && pc != '\\') in_q = ! in_q;
               if(!in_q && c == '{') b_depth++;
               if(!in_q && c == '}' && b_depth-- && !b_depth)
               {
                  obj[iobj] = '\0';
                  process_object(obj);
                  iobj = 0;
               }
            }
            pc = c;
         }
      }
      fclose(fp_result);
      if(db_errored)
      {
         _log(CRITICAL, "Update rolled back due to database error.");
         (void) db_rollback_transaction();
      }
      else
      {
         _log(GENERAL, "Committing database updates...");
         if(db_commit_transaction())
         {
            _log(CRITICAL, "Database commit failed.");
         }
         else
         {
            _log(GENERAL, "Committed.");
         }
      }
   }

#define REPORT_SIZE 16384
   char report[REPORT_SIZE];
   report[0] = '\0';

   _log(GENERAL, "");
   _log(GENERAL, "End of run:");

   if(used_insecure)
   {
      strcat(report, "*** Warning: Insecure download used.\n");
      _log(GENERAL, "*** Warning: Insecure download used.");
   }

   sprintf(zs, "             Elapsed time: %ld minutes", (time(NULL) - start_time + 30) / 60);
   _log(GENERAL, zs);
   strcat(report, zs); strcat(report, "\n");
   if(test_mode)
   {
      sprintf(zs, "Test mode.  No database changes made.");
      _log(GENERAL, zs);
      strcat(report, zs); strcat(report, "\n");
      exit(0);
   }

   for(i=0; i<MAXStats; i++)
   {
      sprintf(zs, "%25s: %s", stats_category[i], commas_q(stats[i]));
      if(i == DBError && stats[i]) strcat(zs, " ****************");
      _log(GENERAL, zs);
      strcat(report, zs);
      strcat(report, "\n");
   }

   db_disconnect();

   email_alert(NAME, BUILD, "Timetable Update Report", report);

   exit(0);
}
Beispiel #8
0
static void create_database(void)
{
   MYSQL_RES * result0;
   MYSQL_ROW row0;
   word create_trust_activation, create_trust_cancellation, create_trust_movement;
   word create_trust_changeorigin, create_trust_changeid;

   create_trust_activation = create_trust_cancellation = create_trust_movement = true;
   create_trust_changeorigin = create_trust_changeid = true;

   db_query("show tables");
   result0 = db_store_result();
   while((row0 = mysql_fetch_row(result0))) 
   {
      if(!strcasecmp(row0[0], "trust_activation")) create_trust_activation = false;
      if(!strcasecmp(row0[0], "trust_cancellation")) create_trust_cancellation = false;
      if(!strcasecmp(row0[0], "trust_movement")) create_trust_movement = false;
      if(!strcasecmp(row0[0], "trust_changeorigin")) create_trust_changeorigin = false;
      if(!strcasecmp(row0[0], "trust_changeid")) create_trust_changeid = false;
   }
   mysql_free_result(result0);

   if(create_trust_activation)
   {
      db_query(
"CREATE TABLE trust_activation "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"cif_schedule_id INT UNSIGNED NOT NULL, "
"deduced         TINYINT UNSIGNED NOT NULL, "
"INDEX(cif_schedule_id), INDEX(trust_id), INDEX(created)"
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table trust_activation.");
   }
   if(create_trust_cancellation)
   {
      db_query(
"CREATE TABLE trust_cancellation "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"reason VARCHAR(8) NOT NULL, "
"type   VARCHAR(32) NOT NULL, "
"loc_stanox VARCHAR(8) NOT NULL, "
"reinstate TINYINT UNSIGNED NOT NULL, "
"INDEX(trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table trust_cancellation.");
   }
   if(create_trust_movement)
   {
      db_query(
"CREATE TABLE trust_movement "
"(created            INT UNSIGNED NOT NULL, "
"trust_id            VARCHAR(16) NOT NULL, "
"event_type          VARCHAR(16) NOT NULL, "
"planned_event_type  VARCHAR(16) NOT NULL, "
"platform            VARCHAR(8) NOT NULL, "
"loc_stanox          VARCHAR(8) NOT NULL, "
"actual_timestamp    INT UNSIGNED NOT NULL, "
"gbtt_timestamp      INT UNSIGNED NOT NULL, "
"planned_timestamp   INT UNSIGNED NOT NULL, "
"timetable_variation INT NOT NULL, "
"event_source        VARCHAR(16) NOT NULL, "
"offroute_ind        BOOLEAN NOT NULL, "
"train_terminated    BOOLEAN NOT NULL, "
"variation_status    VARCHAR(16) NOT NULL, "
"next_report_stanox  VARCHAR(8) NOT NULL, "
"next_report_run_time INT UNSIGNED NOT NULL, "
"INDEX(trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table trust_movement.");
   }
   if(create_trust_changeorigin)
   {
      db_query(
"CREATE TABLE trust_changeorigin "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"reason VARCHAR(8) NOT NULL, "
"loc_stanox VARCHAR(8) NOT NULL, "
"INDEX(trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table trust_changeorigin.");
   }
   if(create_trust_changeid)
   {
      db_query(
"CREATE TABLE trust_changeid "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"new_trust_id VARCHAR(16) NOT NULL, "
"INDEX(trust_id), INDEX(new_trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table trust_changeid.");
   }
}
Beispiel #9
0
static void process_trust_0003(const char * string, const jsmntok_t * tokens, const int index)
{
   char query[1024], zs[32], zs1[32], train_id[128], loc_stanox[128];
   time_t planned_timestamp, actual_timestamp, timestamp;

   time_t now = time(NULL);
   
   sprintf(query, "INSERT INTO trust_movement VALUES(%ld, '", now);
   jsmn_find_extract_token(string, tokens, index, "train_id", train_id, sizeof(train_id));
   strcat(query, train_id);
   strcat(query, "', '");
   jsmn_find_extract_token(string, tokens, index, "event_type", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, "', '");
   jsmn_find_extract_token(string, tokens, index, "planned_event_type", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, "', '");
   jsmn_find_extract_token(string, tokens, index, "platform", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, "', '");
   jsmn_find_extract_token(string, tokens, index, "loc_stanox", loc_stanox, sizeof(loc_stanox));
   strcat(query, loc_stanox);
   strcat(query, "', ");
   jsmn_find_extract_token(string, tokens, index, "actual_timestamp", zs, sizeof(zs));
   zs[10] = '\0';
   actual_timestamp = correct_trust_timestamp(atol(zs));
   sprintf(zs, "%ld", actual_timestamp);
   strcat(query, zs);
   strcat(query, ", ");
   jsmn_find_extract_token(string, tokens, index, "gbtt_timestamp", zs, sizeof(zs));
   zs[10] = '\0';
   timestamp = correct_trust_timestamp(atol(zs));
   sprintf(zs, "%ld", timestamp);
   strcat(query, zs);
   strcat(query, ", ");
   jsmn_find_extract_token(string, tokens, index, "planned_timestamp", zs, sizeof(zs));
   zs[10] = '\0';
   planned_timestamp = correct_trust_timestamp(atol(zs));
   sprintf(zs, "%ld", planned_timestamp);
   strcat(query, zs);
   strcat(query, ", ");
   jsmn_find_extract_token(string, tokens, index, "timetable_variation", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, ", '");
   jsmn_find_extract_token(string, tokens, index, "event_source", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, "', ");
   jsmn_find_extract_token(string, tokens, index, "offroute_ind", zs, sizeof(zs));
   strcat(query, (zs[0] == 't')?"1":"0");
   strcat(query, ", ");
   jsmn_find_extract_token(string, tokens, index, "train_terminated", zs, sizeof(zs));
   strcat(query, (zs[0] == 't')?"1":"0");
   strcat(query, ", '");
   jsmn_find_extract_token(string, tokens, index, "variation_status", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, "', '");
   jsmn_find_extract_token(string, tokens, index, "next_report_stanox", zs, sizeof(zs));
   strcat(query, zs);
   strcat(query, "', ");
   jsmn_find_extract_token(string, tokens, index, "next_report_run_time", zs, sizeof(zs));
   sprintf(zs1, "%d", atoi(zs));
   strcat(query, zs1);
   strcat(query, ")");

   db_query(query);

   // Old one?
   if(planned_timestamp && now - actual_timestamp > 6*60*60)
   {
      sprintf(query, "Late movement message received, actual timestamp %s.", time_text(actual_timestamp, true));
      _log(MINOR, query);
   }
   sprintf(query, "SELECT * from trust_activation where trust_id = '%s' and created > %ld and cif_schedule_id > 0", train_id, now - (4*24*60*60));
   if(!db_query(query))
   {
      MYSQL_RES * result0 = db_store_result();

      word num_rows = mysql_num_rows(result0);
      mysql_free_result(result0);
      if(num_rows > 1)
      {
         // This is not actually invalid, if there's some cancellations as well
         // sprintf(query, "Movement message received with %d matching activations, train_id = \"%s\".", num_rows, train_id);
         // _log(MAJOR, query);
      }
      else if(num_rows < 1)
      {
         MYSQL_ROW row0;
         char tiploc[128], reason[128];
         word sort_time;
         time_t now = time(NULL);

         strcpy(reason, "");
         tiploc[0] = '\0';

         sprintf(query, "Movement message received with %d matching activations, train_id = \"%s\".", num_rows, train_id);
         _log(MINOR, query);
         stats[MovtNoAct]++;
         if(planned_timestamp == 0)
         {
            strcpy(reason, "No planned timestamp");
         }
         if(high_load) strcpy(reason, "Message load too high");

         if(!reason[0])
         {
            sprintf(query, "SELECT tiploc FROM corpus WHERE stanox = %s AND tiploc != ''", loc_stanox);
            if(!db_query(query))
            {
               result0 = db_store_result();
               num_rows = mysql_num_rows(result0);
               if(num_rows)
               {
                  row0 = mysql_fetch_row(result0);
                  strcpy(tiploc, row0[0]);
               }
               else
               {
                  strcpy(reason, "Unable to determine TIPLOC");
               }
               mysql_free_result(result0);
            }
         }

         if(!reason[0])
         {
            char query1[256];
            struct tm * broken = localtime(&planned_timestamp);
            sort_time = broken->tm_hour * 4 * 60 + broken->tm_min * 4;
            // Select the day
            word day = broken->tm_wday;
            word yest = (day + 6) % 7;
            // word tom = (day + 1) % 7;
            broken->tm_hour = 12;
            broken->tm_min = 0;
            broken->tm_sec = 0;
            time_t when = timegm(broken);
            sprintf(query, "SELECT cif_schedules.id, cif_schedules.CIF_train_uid, signalling_id, CIF_stp_indicator FROM cif_schedules INNER JOIN cif_schedule_locations ON cif_schedules.id = cif_schedule_locations.cif_schedule_id WHERE cif_schedule_locations.tiploc_code = '%s'",
                    tiploc);
            sprintf(query1, " AND cif_schedule_locations.sort_time > %d AND cif_schedule_locations.sort_time < %d",
                    sort_time - 1, sort_time + 4);
            strcat(query, query1);
            strcat(query, " AND (cif_schedules.CIF_stp_indicator = 'N' OR cif_schedules.CIF_stp_indicator = 'P' OR cif_schedules.CIF_stp_indicator = 'O')");


            static const char * days_runs[8] = {"runs_su", "runs_mo", "runs_tu", "runs_we", "runs_th", "runs_fr", "runs_sa", "runs_su"};

            //
            sprintf(query1, " AND (((%s) AND (schedule_start_date <= %ld) AND (schedule_end_date >= %ld) AND (NOT next_day))",   days_runs[day],  when + 12*60*60, when - 12*60*60);
            strcat(query, query1);
            sprintf(query1, " OR   ((%s) AND (schedule_start_date <= %ld) AND (schedule_end_date >= %ld) AND (    next_day)))",  days_runs[yest], when - 12*60*60, when - 36*60*60);
            strcat(query, query1);

            sprintf(query1, " AND deleted > %ld ORDER BY LOCATE(CIF_stp_indicator, 'NPO')", planned_timestamp);
            strcat(query, query1);

            if(!db_query(query))
            {
               char save_uid[16], save_stp;
               save_uid[0] = save_stp = '\0';
               dword cif_schedule_id = 0;
               result0 = db_store_result();
               num_rows = mysql_num_rows(result0);
               if(!num_rows)
               {
                  strcpy(reason, "No schedules found");
               }

               while((row0 = mysql_fetch_row(result0)))
               {
                  sprintf(query, "   Found potential match:%8s (%s) %s STP=%s", row0[0], row0[1], row0[2], row0[3]);
                  _log(MINOR, query);
                  if (!reason[0])
                  {
                     if(save_uid[0] && strcmp(save_uid, row0[1]))  strcpy(reason, "Multiple matching schedule UIDs");
                     else if (save_stp == 'O' && row0[3][0] =='O') strcpy(reason, "Multiple matching overlay schedules");
                     else
                     {
                        cif_schedule_id = atol(row0[0]);
                        strcpy(save_uid, row0[1]);
                        save_stp = row0[3][0];
                     }
                  }
               }
               mysql_free_result(result0);

               if(!reason[0])
               {
                  sprintf(query, "INSERT INTO trust_activation VALUES(%ld, '%s', %ld, 1)", now, train_id, cif_schedule_id);
                  db_query(query);
                  sprintf(query, "   Successfully deduced activation %ld", cif_schedule_id);
                  _log(MINOR, query);
               }
            }
         }

         if(!reason[0])
         {
            stats[DeducedAct]++;
         }
         else
         {
            sprintf(query, "   Failed to deduce an activation - Reason:  %s.", reason );
            _log(MINOR, query);
            
            sprintf(query, "      stanox = %s, tiploc = \"%s\", planned_timestamp %s, derived sort time = %d", loc_stanox, tiploc, time_text(planned_timestamp, true), sort_time);
            _log(MINOR, query);
            // jsmn_dump_tokens(string, tokens, index);
         }
      }
   }
   return;
}
Beispiel #10
0
static void process_trust_0001(const char * const string, const jsmntok_t * const tokens, const int index)
{
   char zs[128], zs1[128], report[1024];
   char train_id[64], train_uid[64];
   char query[1024];
   dword cif_schedule_id;
   MYSQL_RES * result0;
   MYSQL_ROW row0;
   
   sprintf(report, "Activation message:");

   jsmn_find_extract_token(string, tokens, index, "train_id", train_id, sizeof(train_id));
   sprintf(zs1, " train_id=\"%s\"", train_id);
   strcat(report, zs1);

   jsmn_find_extract_token(string, tokens, index, "schedule_start_date", zs, sizeof(zs));
   time_t schedule_start_date_stamp = parse_datestamp(zs);
   sprintf(zs1, " schedule_start_date=\"%s\" %ld", zs, schedule_start_date_stamp);
   strcat(report, zs1);

   jsmn_find_extract_token(string, tokens, index, "schedule_end_date", zs, sizeof(zs));
   time_t schedule_end_date_stamp   = parse_datestamp(zs);
   sprintf(zs1, " schedule_end_date=\"%s\" %ld", zs, schedule_end_date_stamp);
   strcat(report, zs1);

   jsmn_find_extract_token(string, tokens, index, "train_uid", train_uid, sizeof(train_uid));
   sprintf(zs1, " train_uid=\"%s\"", train_uid);
   strcat(report, zs1);

   jsmn_find_extract_token(string, tokens, index, "schedule_source", zs, sizeof(zs));
   sprintf(zs1, " schedule_source=\"%s\"", zs);
   strcat(report, zs1);

   jsmn_find_extract_token(string, tokens, index, "schedule_wtt_id", zs, sizeof(zs));
   sprintf(zs1, " schedule_wtt_id=\"%s\"", zs);
   strcat(report, zs1);

   // Bodge.  The ORDER BY here will *usually* get the correct one out first!
   // Idea:  If we store and index on cif_train_uid, we can guarantee to get the right one.
   sprintf(query, "select id from cif_schedules where cif_train_uid = '%s' AND schedule_start_date = %ld AND schedule_end_date = %ld AND deleted > %ld ORDER BY LOCATE(CIF_stp_indicator, 'OCPN')", train_uid, schedule_start_date_stamp, schedule_end_date_stamp, time(NULL));
   if(!db_query(query))
   {
      result0 = db_store_result();
      word num_rows = mysql_num_rows(result0);
      sprintf(zs, "  Schedule hit count %d.  Message contents:", num_rows);
      strcat(report, zs);
      if(num_rows < 1) 
      {
         stats[Mess1Miss]++;
         _log(MINOR, report);
         time_t now = time(NULL);
         sprintf(query, "INSERT INTO trust_activation VALUES(%ld, '%s', %ld, 0)", now, train_id, 0L);
         db_query(query);
         jsmn_dump_tokens(string, tokens, index);
      }
      else
      {
         row0 = mysql_fetch_row(result0);
         cif_schedule_id = atol(row0[0]);
         time_t now = time(NULL);
         sprintf(query, "INSERT INTO trust_activation VALUES(%ld, '%s', %ld, 0)", now, train_id, cif_schedule_id);
         db_query(query);
      }
      mysql_free_result(result0);
   }
   return;
}
Beispiel #11
0
word database_upgrade(const word caller)
{
   char query[256];
   MYSQL_RES * res;
   MYSQL_ROW row;
   word lock, result, old_version;

   result = 0;

   // Lock the upgrade process.
   // NOTE - This DOES NOT lock the database from normal data processing, it merely prevents
   // simultaneous database_upgrade processing.
   lock = 0;
   while(!lock)
   {
      if(!db_query("SELECT GET_LOCK('openrail.database_upgrade', 1)"))
      {
         res = db_store_result();
         row = mysql_fetch_row(res);
         lock = atoi(row[0]);
         mysql_free_result(res);
      }
      if(!lock)
      {
         _log(MINOR, "Waiting for database upgrade lock.");
         sleep(8);
      }
   }

   old_version = 0;
   if(table_exists("database_version"))
   {
      sprintf(query, "SELECT * FROM database_version");
      if(!db_query(query))
      {
         res = db_store_result();
         row = mysql_fetch_row(res);
         old_version = atoi(row[0]);
         mysql_free_result(res);
      }
   }

   // Note:  old > NEW is no-op, not error.  If a particular program is not changed during an upgrade, it will 
   // not need to be re-compiled just for the database change.
   // Note:  If a change creates a new table, there's no need to do it in this upgrade section, just leave it
   // to the table creation below.
   if(old_version < NEW_VERSION)
   {
      _log(GENERAL, "Upgrading database from version %d to version %d.", old_version, NEW_VERSION);

      // Upgrade to 1
      if(old_version < 1)
      {
         db_query("CREATE TABLE database_version (v INT UNSIGNED NOT NULL ) ENGINE = InnoDB");
         db_query("INSERT INTO  database_version VALUES(0)");
         _log(GENERAL, "Created database table \"database_version\".");
      }

      // Upgrade to x
      // if(old_version < x)
      //    if(table_exists(y)  You must have this in case the table hasn't been created yet, e.g. on a brand new platform.
      //        ALTER TABLE

      sprintf(query, "UPDATE database_version SET v = %d", NEW_VERSION);
      db_query(query);
      _log(GENERAL, "Database upgraded to version %d.", NEW_VERSION);     
   }

   // Upgrade completed.  Now create any missing tables

   ///////////////////////////////////////////////////////////////////////////////////////////////////
   // corpus table
   if(!table_exists("corpus"))
   {
      db_query(
"CREATE TABLE corpus(                         "
"id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, "
"fn                     VARCHAR(255) NOT NULL,"
"stanox                 CHAR(16) NOT NULL,    "
"uic                    CHAR(16) NOT NULL,    "
"3alpha                 CHAR(16) NOT NULL,    "
"nlcdesc16              CHAR(20) NOT NULL,    "
"tiploc                 CHAR(16) NOT NULL,    "
"nlc                    CHAR(16) NOT NULL,    "
"nlcdesc                VARCHAR(255) NOT NULL,"
"PRIMARY KEY (id), INDEX(TIPLOC)              "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"corpus\".");
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////
   // smart table
   if(caller == smartdb && !table_exists("smart"))
   {
      db_query(
"CREATE TABLE smart(                          "
"id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, "
"  `fromberth` char(4) NOT NULL,"
"  `td` char(2) NOT NULL,"
"  `stanox` int NOT NULL,"
"  `route` smallint NOT NULL,"
"  `steptype` char(1) NOT NULL,"
"  `toberth` char(4) NOT NULL,"
"  `toline` char(1) NOT NULL,"
"  `berthoffset` int NOT NULL,"
"  `platform` smallint NOT NULL,"
"  `event` char(1) NOT NULL,"
"  `comment` varchar(64) NOT NULL,"
"  `stanme` char(9) NOT NULL,"
"  `fromline` char(1) NOT NULL,"
"  PRIMARY KEY (id)"
") ENGINE = InnoDB DEFAULT CHARSET=latin1"
               );
      _log(GENERAL, "Created database table \"smart\".");
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////
   // Schedule tables
   if((caller == cifdb || caller == vstpdb || caller == trustdb) && !table_exists("updates_processed"))
   {
      db_query(
"CREATE TABLE updates_processed"
"("
"id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, "
"time INT UNSIGNED NOT NULL,"
"PRIMARY KEY (id) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"updates_processed\".");
   }

   if((caller == cifdb || caller == vstpdb || caller == trustdb) && !table_exists("cif_associations"))
   {
      db_query(
"CREATE TABLE cif_associations                   "
"(                                               "
"update_id               SMALLINT UNSIGNED NOT NULL,        "
"created                 INT UNSIGNED NOT NULL,       "
"deleted                 INT UNSIGNED NOT NULL,       "
"main_train_uid          CHAR(6) NOT NULL,       "
"assoc_train_uid         CHAR(6) NOT NULL,       "
"assoc_start_date        INT UNSIGNED NOT NULL,       "
"assoc_end_date          INT UNSIGNED NOT NULL,       "
"assoc_days              CHAR(7) NOT NULL,       "
"category                CHAR(2) NOT NULL,       "
"date_indicator          CHAR(1) NOT NULL,       "
"location                CHAR(7) NOT NULL,       "
"base_location_suffix    CHAR(1) NOT NULL,       "
"assoc_location_suffix   CHAR(1) NOT NULL,       "
"diagram_type            CHAR(1) NOT NULL,       "
"CIF_stp_indicator       CHAR(1) NOT NULL        "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_associations\".");
   }

   if((caller == cifdb || caller == vstpdb || caller == trustdb) && !table_exists("cif_schedules"))
   {
      db_query(
"CREATE TABLE cif_schedules                      "
"(                                               "
"update_id                     SMALLINT UNSIGNED NOT NULL,  "
"created                       INT UNSIGNED NOT NULL, "
"deleted                       INT UNSIGNED NOT NULL, "
"CIF_bank_holiday_running      CHAR(1) NOT NULL, "
"CIF_stp_indicator             CHAR(1) NOT NULL, "
"CIF_train_uid                 CHAR(6) NOT NULL, "
"applicable_timetable          char(1) NOT NULL, "
"atoc_code                     CHAR(2) NOT NULL, "
   //"traction_class                CHAR(8) NOT NULL, " // "Not used" in CIF spec so omitted here.
"uic_code                      CHAR(5) NOT NULL, "
"runs_mo                       BOOLEAN NOT NULL, "
"runs_tu                       BOOLEAN NOT NULL, "
"runs_we                       BOOLEAN NOT NULL, "
"runs_th                       BOOLEAN NOT NULL, "
"runs_fr                       BOOLEAN NOT NULL, "
"runs_sa                       BOOLEAN NOT NULL, "
"runs_su                       BOOLEAN NOT NULL, "
"schedule_end_date             INT UNSIGNED NOT NULL, "
"signalling_id                 CHAR(4) NOT NULL, "
"CIF_train_category            CHAR(2) NOT NULL, "
"CIF_headcode                  CHAR(4) NOT NULL, "
   //"CIF_course_indicator          CHAR(8) NOT NULL, " // "Not used" in CIF spec, so omitted here.
"CIF_train_service_code        CHAR(8) NOT NULL, "
"CIF_business_sector           CHAR(1) NOT NULL, "
"CIF_power_type                CHAR(3) NOT NULL, "
"CIF_timing_load               CHAR(4) NOT NULL, "
"CIF_speed                     CHAR(3) NOT NULL, "
"CIF_operating_characteristics CHAR(6) NOT NULL, "
"CIF_train_class               CHAR(1) NOT NULL, "
"CIF_sleepers                  CHAR(1) NOT NULL, "
"CIF_reservations              CHAR(1) NOT NULL, "
"CIF_connection_indicator      CHAR(1) NOT NULL, "
"CIF_catering_code             CHAR(4) NOT NULL, "
"CIF_service_branding          CHAR(4) NOT NULL, "
"schedule_start_date           INT UNSIGNED NOT NULL, "
"train_status                  CHAR(1) NOT NULL, "
"id                            INT UNSIGNED NOT NULL AUTO_INCREMENT, "
"deduced_headcode              CHAR(4) NOT NULL DEFAULT '', "
"deduced_headcode_status       CHAR(1) NOT NULL DEFAULT '', "
"PRIMARY KEY (id), INDEX(schedule_end_date), INDEX(schedule_start_date), INDEX(CIF_train_uid), INDEX(CIF_stp_indicator) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_schedules\".");
   }

   if((caller == cifdb || caller == vstpdb || caller == trustdb) && !table_exists("cif_schedule_locations"))
   {
      db_query(
"CREATE TABLE cif_schedule_locations             "
"(                                               "
"update_id                     SMALLINT UNSIGNED NOT NULL, "
"cif_schedule_id               INT UNSIGNED NOT NULL, "
"location_type                 CHAR(12) NOT NULL, " // MISNAMED!  contains activity
"record_identity               CHAR(2) NOT NULL, " // LO, LI, LT, etc
"tiploc_code                   CHAR(7) NOT NULL, "
"tiploc_instance               CHAR(1) NOT NULL, "
"arrival                       CHAR(5) NOT NULL, "
"departure                     CHAR(5) NOT NULL, "
"pass                          CHAR(5) NOT NULL, "
"public_arrival                CHAR(4) NOT NULL, "
"public_departure              CHAR(4) NOT NULL, "
"sort_time                     SMALLINT UNSIGNED NOT NULL, "
"next_day                      BOOLEAN NOT NULL, "
"platform                      CHAR(3) NOT NULL, "
"line                          CHAR(3) NOT NULL, "
"path                          CHAR(3) NOT NULL, "
"engineering_allowance         CHAR(2) NOT NULL, "
"pathing_allowance             CHAR(2) NOT NULL, "
"performance_allowance         CHAR(2) NOT NULL, "
"INDEX(cif_schedule_id), INDEX(tiploc_code) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_schedule_locations\".");
   }

   if((caller == cifdb || caller == vstpdb || caller == trustdb) && !table_exists("cif_changes_en_route"))
   {
      db_query(
"CREATE TABLE cif_changes_en_route               "
"(                                               "
"cif_schedule_id               INT UNSIGNED NOT NULL, "
"tiploc_code                   CHAR(7) NOT NULL, "
"tiploc_instance               CHAR(1) NOT NULL, "
"CIF_train_category            CHAR(2) NOT NULL, "
"signalling_id                 CHAR(4) NOT NULL, "
"CIF_headcode                  CHAR(4) NOT NULL, "
"CIF_train_service_code        CHAR(8) NOT NULL, "
// Portion ID not used
"CIF_power_type                CHAR(3) NOT NULL, "
"CIF_timing_load               CHAR(4) NOT NULL, "
"CIF_speed                     CHAR(3) NOT NULL, "
"CIF_operating_characteristics CHAR(6) NOT NULL, "
"CIF_train_class               CHAR(1) NOT NULL, "
"CIF_sleepers                  CHAR(1) NOT NULL, "
"CIF_reservations              CHAR(1) NOT NULL, "
"CIF_connection_indicator      CHAR(1) NOT NULL, "
"CIF_catering_code             CHAR(4) NOT NULL, "
"CIF_service_branding          CHAR(4) NOT NULL, "
// Traction class Not used.
"uic_code                      CHAR(5) NOT NULL, "
"INDEX(cif_schedule_id), INDEX(tiploc_code)      "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_changes_en_route\".");
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////
   // trust tables
   if((caller == trustdb) && !table_exists("trust_activation"))
   {
      db_query(
"CREATE TABLE trust_activation "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"cif_schedule_id INT UNSIGNED NOT NULL, "
"deduced         TINYINT UNSIGNED NOT NULL, "
"INDEX(cif_schedule_id), INDEX(trust_id), INDEX(created)"
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_activation\".");
   }
   if((caller == trustdb) && !table_exists("trust_cancellation"))
   {
      db_query(
"CREATE TABLE trust_cancellation "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"reason VARCHAR(8) NOT NULL, "
"type   VARCHAR(32) NOT NULL, "
"loc_stanox VARCHAR(8) NOT NULL, "
"reinstate TINYINT UNSIGNED NOT NULL, "
"INDEX(trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_cancellation\".");
   }

   if((caller == trustdb) && !table_exists("trust_movement"))
   {
      db_query(
"CREATE TABLE trust_movement "
"(created            INT UNSIGNED NOT NULL, "
"trust_id            VARCHAR(16) NOT NULL, "
"event_type          VARCHAR(16) NOT NULL, "
"planned_event_type  VARCHAR(16) NOT NULL, "
"platform            VARCHAR(8) NOT NULL, "
"loc_stanox          VARCHAR(8) NOT NULL, "
"actual_timestamp    INT UNSIGNED NOT NULL, "
"gbtt_timestamp      INT UNSIGNED NOT NULL, "
"planned_timestamp   INT UNSIGNED NOT NULL, "
"timetable_variation INT NOT NULL, "
"event_source        VARCHAR(16) NOT NULL, "
"offroute_ind        BOOLEAN NOT NULL, "
"train_terminated    BOOLEAN NOT NULL, "
"variation_status    VARCHAR(16) NOT NULL, "
"next_report_stanox  VARCHAR(8) NOT NULL, "
"next_report_run_time INT UNSIGNED NOT NULL, "
"INDEX(trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_movement\".");
   }

   if((caller == trustdb) && !table_exists("trust_changeorigin"))
   {
      db_query(
"CREATE TABLE trust_changeorigin "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"reason VARCHAR(8) NOT NULL, "
"loc_stanox VARCHAR(8) NOT NULL, "
"INDEX(trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_changeorigin\".");
   }

   if((caller == trustdb) && !table_exists("trust_changeid"))
   {
      db_query(
"CREATE TABLE trust_changeid "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"new_trust_id VARCHAR(16) NOT NULL, "
"INDEX(trust_id), INDEX(new_trust_id), INDEX(created) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_changeid\".");
   }

   if((caller == vstpdb || caller == trustdb || caller == tddb) && !table_exists("status"))
   {
      db_query(
"CREATE TABLE status "
"(last_trust_processed INT UNSIGNED NOT NULL, "
"last_trust_actual     INT UNSIGNED NOT NULL, "
"last_vstp_processed   INT UNSIGNED NOT NULL, "
"last_td_processed     INT UNSIGNED NOT NULL  "
") ENGINE = InnoDB"
               );
      db_query(
"INSERT INTO status VALUES(0, 0, 0, 0)"
               );
      _log(GENERAL, "Created database table \"status\".");
   }

   if((caller == vstpdb || caller == trustdb || caller == tddb) && !table_exists("message_count"))
   {
      db_query(
"CREATE TABLE message_count "
"(application          CHAR(16) NOT NULL,     "
"time                  INT UNSIGNED NOT NULL, "
"count                 INT UNSIGNED NOT NULL, "
"INDEX(time)                                  "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"message_count\".");
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////
   // td tables
   if(caller == tddb && !table_exists("td_updates"))
   {
      db_query(
"CREATE TABLE td_updates "
"(created INT UNSIGNED NOT NULL, "
"handle   INT UNSIGNED NOT NULL, "
"k        CHAR(8) NOT NULL, "
"v        CHAR(8) NOT NULL, "
"PRIMARY KEY(k) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"td_updates\".");
   }

   if((caller == tddb || caller == limed) && !table_exists("td_states"))
   {
      db_query(
"CREATE TABLE td_states "
"(updated INT UNSIGNED NOT NULL, "
"k        CHAR(8) NOT NULL, "
"v        CHAR(8) NOT NULL, "
"PRIMARY KEY(k) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"td_states\".");
   }

   if(caller == tddb && !table_exists("td_status"))
   {
      db_query(
"CREATE TABLE td_status "
"(d             CHAR(4) NOT NULL, "
"last_timestamp INT UNSIGNED NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"td_status\".");
   }

   if(caller == tddb && !table_exists("friendly_names_20"))
   {
      db_query(
"CREATE TABLE friendly_names_20 "
"(tiploc CHAR(8) NOT NULL, "
"name    VARCHAR(32), "
"PRIMARY KEY (tiploc) "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"friendly_names_20\".");
   }

   ///////////////////////////////////////////////////////////////////////////////////////////////////
   // Archive tables
   if(caller == archdb && !table_exists("trust_activation_arch"))
   {
      db_query(
"CREATE TABLE trust_activation_arch "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"cif_schedule_id INT UNSIGNED NOT NULL, "
"deduced         TINYINT UNSIGNED NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_activation_arch\".");
   } 

   if(caller == archdb && !table_exists("trust_cancellation_arch"))
   {
      db_query(
"CREATE TABLE trust_cancellation_arch "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"reason VARCHAR(8) NOT NULL, "
"type   VARCHAR(32) NOT NULL, "
"loc_stanox VARCHAR(8) NOT NULL, "
"reinstate TINYINT UNSIGNED NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_cancellation_arch\".");
   } 

   if(caller == archdb && !table_exists("trust_movement_arch"))
   {
      db_query(
"CREATE TABLE trust_movement_arch "
"(created            INT UNSIGNED NOT NULL, "
"trust_id            VARCHAR(16) NOT NULL, "
"event_type          VARCHAR(16) NOT NULL, "
"planned_event_type  VARCHAR(16) NOT NULL, "
"platform            VARCHAR(8) NOT NULL, "
"loc_stanox          VARCHAR(8) NOT NULL, "
"actual_timestamp    INT UNSIGNED NOT NULL, "
"gbtt_timestamp      INT UNSIGNED NOT NULL, "
"planned_timestamp   INT UNSIGNED NOT NULL, "
"timetable_variation INT NOT NULL, "
"event_source        VARCHAR(16) NOT NULL, "
"offroute_ind        BOOLEAN NOT NULL, "
"train_terminated    BOOLEAN NOT NULL, "
"variation_status    VARCHAR(16) NOT NULL, "
"next_report_stanox  VARCHAR(8) NOT NULL, "
"next_report_run_time INT UNSIGNED NOT NULL "
") ENGINE = InnoDB"               );
      _log(GENERAL, "Created database table \"trust_movement_arch\".");
   } 

   if(caller == archdb && !table_exists("trust_changeorigin_arch"))
   {
      db_query(
"CREATE TABLE trust_changeorigin_arch "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"reason VARCHAR(8) NOT NULL, "
"loc_stanox VARCHAR(8) NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_changeorigin_arch\".");
   } 

   if(caller == archdb && !table_exists("trust_changeid_arch"))
   {
      db_query(
"CREATE TABLE trust_changeid_arch "
"(created INT UNSIGNED NOT NULL, "
"trust_id VARCHAR(16) NOT NULL, "
"new_trust_id VARCHAR(16) NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"trust_changeid_arch\".");
   } 

   if(caller == archdb && !table_exists("cif_associations_arch"))
   {
      db_query(
"CREATE TABLE cif_associations_arch              "
"(                                               "
"update_id               SMALLINT UNSIGNED NOT NULL,        "
"created                 INT UNSIGNED NOT NULL,       "
"deleted                 INT UNSIGNED NOT NULL,       "
"main_train_uid          CHAR(6) NOT NULL,       "
"assoc_train_uid         CHAR(6) NOT NULL,       "
"assoc_start_date        INT UNSIGNED NOT NULL,       "
"assoc_end_date          INT UNSIGNED NOT NULL,       "
"assoc_days              CHAR(7) NOT NULL,       "
"category                CHAR(2) NOT NULL,       "
"date_indicator          CHAR(1) NOT NULL,       "
"location                CHAR(7) NOT NULL,       "
"base_location_suffix    CHAR(1) NOT NULL,       "
"assoc_location_suffix   CHAR(1) NOT NULL,       "
"diagram_type            CHAR(1) NOT NULL,       "
"CIF_stp_indicator       CHAR(1) NOT NULL        "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_associations_arch\".");
   } 

   if(caller == archdb && !table_exists("cif_schedules_arch"))
   {
      db_query(
"CREATE TABLE cif_schedules_arch                 "
"(                                               "
"update_id                     SMALLINT UNSIGNED NOT NULL,  "
"created                       INT UNSIGNED NOT NULL, "
"deleted                       INT UNSIGNED NOT NULL, "
"CIF_bank_holiday_running      CHAR(1) NOT NULL, "
"CIF_stp_indicator             CHAR(1) NOT NULL, "
"CIF_train_uid                 CHAR(6) NOT NULL, "
"applicable_timetable          char(1) NOT NULL, "
"atoc_code                     CHAR(2) NOT NULL, "
   //"traction_class                CHAR(8) NOT NULL, " // "Not used" in CIF spec so omitted here.
"uic_code                      CHAR(5) NOT NULL, "
"runs_mo                       BOOLEAN NOT NULL, "
"runs_tu                       BOOLEAN NOT NULL, "
"runs_we                       BOOLEAN NOT NULL, "
"runs_th                       BOOLEAN NOT NULL, "
"runs_fr                       BOOLEAN NOT NULL, "
"runs_sa                       BOOLEAN NOT NULL, "
"runs_su                       BOOLEAN NOT NULL, "
"schedule_end_date             INT UNSIGNED NOT NULL, "
"signalling_id                 CHAR(4) NOT NULL, "
"CIF_train_category            CHAR(2) NOT NULL, "
"CIF_headcode                  CHAR(4) NOT NULL, "
   //"CIF_course_indicator          CHAR(8) NOT NULL, " // "Not used" in CIF spec, so omitted here.
"CIF_train_service_code        CHAR(8) NOT NULL, "
"CIF_business_sector           CHAR(1) NOT NULL, "
"CIF_power_type                CHAR(3) NOT NULL, "
"CIF_timing_load               CHAR(4) NOT NULL, "
"CIF_speed                     CHAR(3) NOT NULL, "
"CIF_operating_characteristics CHAR(6) NOT NULL, "
"CIF_train_class               CHAR(1) NOT NULL, "
"CIF_sleepers                  CHAR(1) NOT NULL, "
"CIF_reservations              CHAR(1) NOT NULL, "
"CIF_connection_indicator      CHAR(1) NOT NULL, "
"CIF_catering_code             CHAR(4) NOT NULL, "
"CIF_service_branding          CHAR(4) NOT NULL, "
"schedule_start_date           INT UNSIGNED NOT NULL, "
"train_status                  CHAR(1) NOT NULL, "
"id                            INT UNSIGNED NOT NULL, "
"deduced_headcode              CHAR(4) NOT NULL DEFAULT '', "
"deduced_headcode_status       CHAR(1) NOT NULL DEFAULT ''  "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_schedules_arch\".");
   } 

   if(caller == archdb && !table_exists("cif_schedule_locations_arch"))
   {
      db_query(
"CREATE TABLE cif_schedule_locations_arch        "
"(                                               "
"update_id                     SMALLINT UNSIGNED NOT NULL, "
"cif_schedule_id               INT UNSIGNED NOT NULL, "
"location_type                 CHAR(12) NOT NULL, " // Expected to become activity ?????
"record_identity               CHAR(2) NOT NULL, " // LO, LI, LT, etc
"tiploc_code                   CHAR(7) NOT NULL, "
"tiploc_instance               CHAR(1) NOT NULL, "
"arrival                       CHAR(5) NOT NULL, "
"departure                     CHAR(5) NOT NULL, "
"pass                          CHAR(5) NOT NULL, "
"public_arrival                CHAR(4) NOT NULL, "
"public_departure              CHAR(4) NOT NULL, "
"sort_time                     SMALLINT UNSIGNED NOT NULL, "
"next_day                      BOOLEAN NOT NULL, "
"platform                      CHAR(3) NOT NULL, "
"line                          CHAR(3) NOT NULL, "
"path                          CHAR(3) NOT NULL, "
"engineering_allowance         CHAR(2) NOT NULL, "
"pathing_allowance             CHAR(2) NOT NULL, "
"performance_allowance         CHAR(2) NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_schedule_locations_arch\".");
   } 

   if(caller == archdb && !table_exists("cif_changes_en_route_arch"))
   {
      db_query(
"CREATE TABLE cif_changes_en_route_arch               "
"(                                               "
"cif_schedule_id               INT UNSIGNED NOT NULL, "
"tiploc_code                   CHAR(7) NOT NULL, "
"tiploc_instance               CHAR(1) NOT NULL, "
"CIF_train_category            CHAR(2) NOT NULL, "
"signalling_id                 CHAR(4) NOT NULL, "
"CIF_headcode                  CHAR(4) NOT NULL, "
"CIF_train_service_code        CHAR(8) NOT NULL, "
// Portion ID not used
"CIF_power_type                CHAR(3) NOT NULL, "
"CIF_timing_load               CHAR(4) NOT NULL, "
"CIF_speed                     CHAR(3) NOT NULL, "
"CIF_operating_characteristics CHAR(6) NOT NULL, "
"CIF_train_class               CHAR(1) NOT NULL, "
"CIF_sleepers                  CHAR(1) NOT NULL, "
"CIF_reservations              CHAR(1) NOT NULL, "
"CIF_connection_indicator      CHAR(1) NOT NULL, "
"CIF_catering_code             CHAR(4) NOT NULL, "
"CIF_service_branding          CHAR(4) NOT NULL, "
// Traction class Not used.
"uic_code                      CHAR(5) NOT NULL "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"cif_changes_en_route_arch\".");
   } 

   if((caller == trustdb || caller == tddb) && !table_exists("obfus_lookup"))
   {
      db_query(
"CREATE TABLE obfus_lookup                            "
"(                                                    "
"created                       INT UNSIGNED NOT NULL, "
"true_hc                       CHAR(4) NOT NULL,      "
"obfus_hc                      CHAR(4) NOT NULL       "
") ENGINE = InnoDB"
               );
      _log(GENERAL, "Created database table \"obfus_lookup\".");
   } 

   // Unlock the upgrade process
   if(!db_query("SELECT RELEASE_LOCK('openrail.database_upgrade')"))
   {
      res = db_store_result();
      mysql_free_result(res);
   }
   return result;
}