Skip to content

History: configuring

This page covers what gets recorded in the history, where it is stored, how history plots are set up, and how to maintain the data.

For the concepts and day-to-day use, see History: overview.

What gets recorded

mlogger records history data from two places in the ODB (see Data sources):

  • the Variables directory of each equipment whose Common/Log history is greater than 0, recorded when it is written but at most once every Log history seconds
  • the links in each subdirectory of /History/Links (virtual events), recorded every time one of the linked keys is written

Only numbers are recorded: strings and 64-bit integers are skipped.

Restart mlogger after changing what gets recorded

mlogger works out the history events and tags when it starts.

Restart mlogger after you:

  • add an equipment, or change its Common/Log history
  • add, remove or rename keys in an equipment's Variables directory (but see below)
  • change the names in an equipment's Settings/Names arrays
  • change /History/Links, or the PerVariableHistory settings

mlogger does notice when the size of the data it records changes (e.g. an array in Variables gets more elements, or a key is added to Variables when using per-equipment history). The next time Variables is written, it redefines all the history events, and logs a harmless "struct size mismatch" error. With per-variable history, a new key in Variables doesn't change the size of any recorded data, so it isn't recorded until mlogger restarts.

If you make an array in Variables longer, you must make its Settings/Names array (if any) longer first. Otherwise mlogger stops with an "Array size mismatch" error as soon as the array is written, and refuses to start again until the names are fixed.

mhttpd may not see new history variables straight away

Once mlogger has recorded a new or changed variable, when the web interface sees it depends on the driver it reads from:

  • FILE: straight away. mhttpd checks for new history files every time it reads the history.
  • SQL drivers and the legacy midas driver: mhttpd keeps the list of events and tags that it read when it first needed it. Until that list is reloaded, new variables aren't offered when you add variables to a panel, and plots of them show no data. To reload it, click Add active variables on a panel's settings page (once, after loading the page), or click Clear history cache in the old settings editor for the OldHistory page. Restarting mhttpd also reloads it.

Per-variable vs per-equipment

When recording history data in files (rather than an SQL database), you can choose whether to record all your equipment's variables at once, or only record the variables that are written to.

Per-variable history
If Variables/x is modified but Variables/y is not, only Variables/x will get recorded in the binary file. You get a history "event definition" for each variable separately.
Per-equipment history
If Variables/x is modified but Variables/y is not, both Variables/x and Variables/y will get recorded in the binary file. The history "event definition" includes all the variables for the equipment.

For the file-based history, you can choose which option is used using the ODB key /Logger/History/xxx/PerVariableHistory (where xxx is MIDAS or FILE).

Note

  • SQL databases always use per-variable history.
  • ODB arrays are always recorded atomically - even if only one element in an array is written to, the state of the entire array will be recorded in the history system.
  • If your equipment's Variables directory contains sub-directories, the subdirectory is treated as the "variable" - if you write to any key in the sub-directory, the entire subdirectory will be recorded.
  • "Virtual" events (from /History/Links) are always treated as a single event - the entire ODB directory in /History/Links/xxx is recorded whenever a key within that directory is written to. If you want keys to be recorded separately, put the links in different subdirectories (i.e. as xxx/x and yyy/y instead of xxx/x and xxx/y).

Recording other ODB keys (virtual events)

History linksTo record ODB keys outside /Equipment, create a subdirectory in /History/Links. Each subdirectory becomes a "virtual" history event, named after the subdirectory.

You should ensure that your subdirectory names in /History/Links do not clash with any equipment names in /Equipment!

Each subdirectory should contain symlinks to other ODB keys that you wish to log. The links can point to individual numbers or arrays, but may NOT link to other directories.

These "virtual" events are recorded whenever an ODB key is written to - there is no built-in throttling by mlogger. You should make sure that you do not update the keys too frequently, so that data storage does not grow too large.

mlogger will complain (and refuse to start) if the target of a link at /History/Links/xxx/yyy does not exist.

If you have an equipment called Trigger, mlogger creates a System subdirectory, with links to the event rate and data rate of that equipment. You can delete it, but mlogger creates it again the next time it starts.

Data destinations (history drivers)

History data can be stored either as binary files on disk or in an SQL database. There are multiple drivers in the history system that determine where the data gets logged. You can enable multiple drivers if you wish (e.g. logging to both binary files and an SQL database).

Driver Type Notes Supported by
web interface
Supported for
analysis on main midas host
Supported for
analysis on other machines
FILE File (.dat) Default ✔ ✔ Limited
Midas File (.hst/.idx/.idf) Deprecated ✔ ✔ Limited
MYSQL SQL (MySQL) ✔ ✔ ✔
PGSQL SQL (PostgreSQL) ✔ ✔ ✔
SQLITE SQL (SQLite) ✔ ✔ ✔
ODBC SQL (any) ✔ ✔ ✔

Settings for all drivers are found in the ODB at /Logger/History/xxx. The exact keys differ (and are explained below), but all of them contain a boolean /Logger/History/xxx/Active key that enables/disables the driver.

History files

There are 2 history drivers that write to binary files - the legacy midas driver creates .hst, .idx and .idf files, while the FILE driver just creates .dat files.

Where to write files

There are three ways to specify where history files should get written. The first ODB entry that is not an empty string gets used:

  1. /Logger/History/FILE/History dir (for the FILE driver) or /Logger/History/MIDAS/History dir (for the midas driver).
  2. /Logger/History dir
  3. /Logger/Data dir

File names

The FILE driver writes data for each history event in a separate file. A new file is created whenever the event definition changes (e.g. because more entries got added to an equipment's Variables directory), when the current file is more than 30 days old, or when it is larger than 100 MiB. A UNIX timestamp is included in the filename to indicate when the file was created.

The event name is included in the filename, and gets manipulated to be all lower case, with any punctuation being replaced by underscores.

The full filename format is mhf_<timestamp>_<date>_<event>.dat.

Example filename for the FILE driver

mhf_1780521232_20260603_powersupply_current.dat contains data for the PowerSupply/Current event, starting from UNIX timestamp 1780521232 (approx 2:15pm Pacific on 3rd June 2026).

Low-level details of the file format are found in History file formats and database schema.

The midas driver creates 3 files per day, and stores data for all events in the same file. If an event definition changes (e.g. because more entries got added to an equipment's Variables directory), this is recorded in the files.

  • .hst files contain definitions of the events and the actual history data
  • .idf files contain just the definitions of the events
  • .idx files contain just the timestamps of the history data and "pointers" to where that data is in the .hst file

So, all the data is available in the .hst files, but the .idx and .idf files allow you to more rapidly find data in the .hst file without having to wade through an entire day's history data.

Filenames simply contain the date, in yymmdd format, e.g. 260603.hst for 3rd June 2026.

Low-level details of the file format are found in History file formats and database schema.

SQL databases

There are 4 history drivers available for writing to SQL databases:

  • MYSQL writes to a MySQL or MariaDB database. MariaDB is an open-source fork of MySQL.
  • PGSQL writes to a PostgreSQL database.
  • SQLITE writes to an SQLite database. SQLite is very low overhead but does not support remote connections - it is not very suitable if you want to run SQL queries from a separate analysis machine. It is fine if you only plan to query from the main midas machine or via the web interface.
  • ODBC can (in principle) write to any database engine that supports Open Database Connectivity. But see the important warning below.
ODBC compatibility - only MySQL-flavoured SQL syntax

Most database systems have an ODBC driver available, including Oracle, MySQL, MariaDB, Microsoft SQL, PostgreSQL, MongoDB and more. See here for links to many ODBC drivers.

Note the confusing terminology - there is both the "low-level ODBC driver" (which handles ODBC->database) and the "midas history ODBC driver" (which handles midas->ODBC)!

Although midas has the ability to connect to any database that offers a low-level ODBC driver, the different databases use slightly different SQL syntax for creating tables and inserting data, and even refer to data types differently!

Currently the midas ODBC driver only supports the MySQL flavour of SQL commands.

If you try to use ODBC with another database system, you may end up with many warnings about syntax issues. Please use the midas forum to let us know if you have a different database system in mind, and we can look at extending the compatibility.

Installing the database server

How you install the database software depends on your operating system. For PostgreSQL, SQLite and ODBC, see their own documentation. For MySQL, these are the steps on two common systems:

Run these commands as root.

  1. Install the MySQL server:

    apt install mysql-server
    
  2. Check that the server has started. The output should include Active: active (running):

    service mysql status
    
  3. Check that the server only accepts connections from the same computer (the default):

    $ grep bind-add /etc/mysql/mysql.conf.d/mysqld.cnf
    bind-address        = 127.0.0.1
    mysqlx-bind-address = 127.0.0.1
    
  4. Find the socket that programs on the same computer connect through. You can use it in the connection files:

    $ grep sock /etc/mysql/my.cnf
    socket = /run/mysqld/mysqld.sock
    
Moving the database to another disk

The database is in /var/lib/mysql. To move it (e.g. to a larger disk), as root:

  1. Stop the server and copy the data:

    systemctl stop mysql
    cp -a /var/lib/mysql /new/data/directory
    
  2. Set the new location in /etc/mysql/my.cnf:

    [mysqld]
    datadir=/new/data/directory
    
  3. Allow the server to use the new directory, by either adjusting your AppArmor settings or disabling AppArmor.

  4. Start the server with systemctl start mysql.

  5. Once you've checked that it works, delete the old directory with rm -rf /var/lib/mysql.

  1. Install MySQL, and start it as a service that also starts when you log in:

    brew install mysql
    brew services start mysql
    

    Homebrew sets up the database when it installs MySQL, with no password for the database's root user. To set one, run mysql_secure_installation.

  2. Check that the service has started. mysql should be listed as started:

    brew services list
    
  3. Homebrew's MySQL only accepts connections from the same computer, set by bind-address in /opt/homebrew/etc/my.cnf (/usr/local/etc/my.cnf on Intel Macs).

  4. Find the socket that programs on the same computer connect through. You can use it in the connection files:

    $ mysql_config --socket
    /tmp/mysql.sock
    

Creating the database and users

Start the mysql program as the database's root user. On Ubuntu, run sudo mysql. On macOS with Homebrew, run mysql -u root (add -p if you've set a root password).

Then create an empty database, a read-only user and a read-write user. You can change the database name, account names and passwords, but don't use the CHANGE_THIS_PASSWORD! passwords from this example:

CREATE DATABASE IF NOT EXISTS history;
CREATE USER history_reader@'localhost' IDENTIFIED BY 'CHANGE_THIS_PASSWORD!';
GRANT SELECT ON history.* TO history_reader@'localhost';
CREATE USER 'history_writer'@'localhost' IDENTIFIED BY 'CHANGE_THIS_PASSWORD!';
GRANT SELECT,INSERT,CREATE,ALTER,INDEX ON history.* TO 'history_writer'@'localhost';
FLUSH PRIVILEGES;

Check that you can log in as each user. Each command asks for that user's password:

mysql -u history_reader -p
mysql -u history_writer -p

I assume that you have installed PostgreSQL and have started the database server.

You will need to create an empty database, and create users with read-write and read-only permissions.

The example commands below should be run at the postgresql prompt as the superuser (e.g. by running psql -d postgres). You can change the database name, account names and passwords.

CREATE DATABASE history;
\connect history
CREATE USER history_reader WITH PASSWORD 'CHANGE_THIS_PASSWORD!';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO history_reader;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO history_reader;
CREATE USER history_writer WITH PASSWORD  'CHANGE_THIS_PASSWORD!';
GRANT SELECT,INSERT ON ALL TABLES IN SCHEMA public TO history_writer;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT,INSERT ON TABLES TO history_writer;
GRANT CREATE ON SCHEMA public TO history_writer;

I assume that you have installed SQLite3.

You do not need to create any databases or users in SQLite - you just need to create a directory where the .sqlite3 files will live.

I assume that you have installed your desired database and its ODBC driver.

You must create a database, and ideally a read-only user and a read-write user (if your database supports users/roles). The read-write user must be able to create tables and alter the structure of existing tables.

Re-compiling midas if needed

If you installed your database software after compiling midas, you may need to re-compile midas so that it includes support for your database.

CMake prints a line for each database it finds, e.g.:

-- MIDAS: Found MySQL version 8.0.41

If it prints e.g. MySQL not found instead, midas is built without support for that database.

Defining the connection

Create two files in your experiment directory (the directory specified in your exptab file):

  • mysql_reader.txt, for the read-only user
  • mysql_writer.txt, for the read-write user

If you use different names, set them in the ODB keys /Logger/History/MYSQL/MYSQL Reader and /Logger/History/MYSQL/MYSQL Writer.

The files should have the following structure:

Example mysql_reader.txt file

server=127.0.0.1
port=3306
database=history
user=history_reader
password=CHANGE_THIS_PASSWORD!

To connect through the server's socket instead of over the network, replace server= and port= with e.g. socket=/run/mysqld/mysqld.sock (see Installing the database server for how to find the socket).

To check that midas can connect, run mh2sql with the writer file. It prints nothing if the connection works, and an error if it doesn't:

mh2sql --mysql mysql_writer.txt

Create two files in your experiment directory (the directory specified in your exptab file):

  • pgsql_reader.txt, for the read-only user
  • pgsql_writer.txt, for the read-write user

If you use different names, set them in the ODB keys /Logger/History/PGSQL/PGSQL Reader and /Logger/History/PGSQL/PGSQL Writer.

The files should have the following structure:

Example pgsql_reader.txt file

database=history
server=localhost
port=5432
user=history_reader
password=CHANGE_THIS_PASSWORD!
Creating /Logger/History/PGSQL if it doesn't exist

The ODB directory /Logger/History/PGSQL may not exist if you initially installed midas before PostgreSQL support was added. To fix this, you can create the following entries in the ODB, then restart mlogger:

  • /Logger/History/PGSQL as a subdirectory
  • /Logger/History/PGSQL/Active as a boolean, and set it to true
  • /Logger/History/PGSQL/Type as a string, and set it to PGSQL

When you restart mlogger, it will create all the other keys that are needed in the /Logger/History/PGSQL directory.

SQLite does not have any concept of users or permissions.

Simply specify a directory where the .sqlite files should be written. The first of these ODB keys that isn't empty will be used:

  1. /Logger/History/SQLITE/History dir
  2. /Logger/History dir
  3. /Logger/Data dir

The files will appear in the directory as *.sqlite3 (e.g. mh_powersupply_current.sqlite3).

Create two DSNs (Data Source Names, i.e. ODBC connections) in the ~/.odbc.ini file (and optionally the ~/.odbcinst.ini file):

  • history_reader, for the read-only user
  • history_writer, for the read-write user

Set their names in the ODB keys /Logger/History/ODBC/Reader_ODBC_DSN and /Logger/History/ODBC/Writer_ODBC_DSN.

The file should have the following structure:

Example ~/.odbc.ini file

[ODBC Data Sources]
history_writer = Midas History Writer DSN
history_reader = Midas History Reader DSN

[history_writer]
Driver       = /usr/local/lib/libmyodbc9w.so
SERVER       = localhost
USER         = history_writer
Password     = CHANGE_THIS_PASSWORD!
Database     = history
OPTION       = 3

[history_reader]
Driver       = /usr/local/lib/libmyodbc9w.so
SERVER       = localhost
USER         = history_reader
Password     = CHANGE_THIS_PASSWORD!
Database     = history
OPTION       = 3

You can try testing your connection with the isql command (e.g. isql -v history_writer). If isql can connect, then midas should be able to connect!

Enabling the driver

Set the ODB key /Logger/History/MYSQL/Active to true.

Set the ODB key /Logger/History/PGSQL/Active to true.

Set the ODB key /Logger/History/SQLITE/Active to true.

Set the ODB key /Logger/History/ODBC/Active to true.

If you no longer want to write the history with the driver you used before (e.g. FILE), set its Active key to false. Then restart mlogger.

Check the message log for any errors. If mlogger can't connect to the database, it will print a message but will continue logging data to midas files and other active history drivers.

You must check the message log to ensure that your SQL connection is working!

The first time mlogger connects to a new database, it creates the tables and columns it needs, and prints a message for each one. With MySQL, it also prints an error because the history index table doesn't exist yet, which you can ignore:

[Logger,ERROR] [history_schema.cxx:1285:Mysql::Prepare,ERROR] mysql_query(SELECT event_name, table_name, itimestamp FROM _history_index WHERE table_name!='' AND column_name IS NULL;) error 1146 (Table 'history._history_index' doesn't exist)
[Logger,INFO] Adding SQL table "myperiodicequipment_pydt"
[Logger,INFO] Adding SQL table "_history_index"
[Logger,INFO] Adding column "event_name" to SQL table "_history_index", status 1
...

To check that data is being written, log in to the database and select some recent values. Each table has an _i_time column with the time as a Unix timestamp, and a column for each variable (see SQL databases for the full schema):

mysql> SELECT _i_time, pydt_0 FROM myperiodicequipment_pydt ORDER BY _i_time DESC LIMIT 3;
+------------+---------+
| _i_time    | pydt_0  |
+------------+---------+
| 1793319815 | 5.89511 |
| 1793319814 | 6.63168 |
| 1793319813 | 5.96647 |
+------------+---------+

To show the history plots from the database, see Default history driver to read from.

If you change your connection settings, you must restart mlogger for it to pick up the new settings.

Database schema

Details of the database schema that midas creates is found in History file formats and database schema.

Default history driver to read from

There are several ways to read data from the history database, including a graphical web interface for displaying plots, embedding plots in your own custom webpages, and command-line tools for dumping data. These all seamlessly handle reading data from any of the history drivers.

If you are writing to multiple destinations, you can choose which one is read from using the /History/LoggerHistoryChannel ODB key.

Example

If both the FILE and MYSQL drivers are active, you can tell the web interface to read from the SQL database when drawing plots by setting /History/LoggerHistoryChannel to MYSQL.

If this ODB key is empty, the first entry in /Logger/History that has the Active key set to true will be used.

Web display history panels

Panels are usually created and edited on the settings page of the web interface (see Defining plot groups and panels), and are stored in the ODB in /History/Display/<group>/<panel>. The same settings page is used for the History and OldHistory pages, but some settings only affect one of them.

Panel-wide settings

Setting ODB key History page OldHistory page
Time scale Timescale Time range shown when the panel opens, e.g. 30m, 1h, 7d (m for minutes, h for hours, d for days) Same
Minimum, Maximum Minimum, Maximum Range of the Y axis. Leave both at 0 to scale the axis to the data Same
Zero Y axis Zero ylow Start the Y axis at 0 Same
Logarithmic Y axis Log axis Use a logarithmic Y axis Same
Show run markers Show run markers Show the starts and ends of runs as vertical lines Same
Show values of variables Show values Show the legend when the panel opens (you can show or hide it with ) Show the latest value of each variable in the legend
Show graph fill Show fill Fill the area under each line Not used
Show factor Show factor Not used Not used
Buttons Time ranges offered by the button. Can only be set in the ODB Time ranges offered as buttons above the plot, when a single panel is shown

The Show factor setting has no effect on either plot. It is only used by the old settings page (the Edit in old editor button), to show the factor and offset columns.

Per-variable settings

Setting ODB key History page OldHistory page
Formula Formula Formula applied to the values before plotting Not used
Factor, Offset, VOffset Factor, Offset, VOffset Not used Scaling applied to the values before plotting. The settings page hides these columns unless a variable uses them, or you click Show factor and offset
Colour Colour CSS hex color of the line (e.g. #ff0000 for red) Same
Label Label Label in the legend. If empty, the variable name is shown Same, but if empty, the factor and offsets are also shown
Show raw value Show the value before the formula, rather than after it, in the legend and when the cursor is over the plot. Can only be set in the ODB or on the old settings page Not used, even though the old settings page has a Raw checkbox for it

Display formula

You will often want to display multiple related variables that have very different numerical ranges. For example, you may have a voltage that ranges from 0-1200V and a temperature that ranges from 20-30C. Both the "old history" and "new history" plots allow you to manipulate the displayed values, but in different ways:

In the new history system you specify an arbitrary formula, using the placeholder variable x. The formula is not displayed to the user anywhere, but you can write arbitrary text in the Label entry for the variable.

For example, to convert from volts to kilovolts, you could specify a Formula of x*0.001, and set a label of Voltage (kV).

In the old history you specify: a multiplicative factor, an overall offset, and a value offset. The plotted value uses the formula:

plotted_value = offset + factor * (history_value - voffset)

If the variable has no Label, the legend shows the variable name with the factor and offsets (e.g. Voltage * +0.001). Otherwise it shows just the label.

For example, to convert from volts to kilovolts, you could specify a Factor of 0.001, leave the offsets at 0, and set a label of Voltage (kV).

When you open a panel on the settings page, any variable that has a factor or offset but no formula is given the equivalent formula (offset+factor*(x-voffset)), so the History page shows it the same way once you save the panel.

Other ways to define a panel

As well as using the settings page, you can also create panels by:

  • copying/pasting an existing panel using the ODB tools (e.g. the odbedit command-line tool or the ODB web interface)
  • writing code, as described in History: programming

See /History/Display for the full list of ODB keys in a panel.

Converting legacy history files to another format (mh2sql)

mh2sql allows you to convert .hst files written by the MIDAS driver into another format - either .dat files from the FILE driver, or an SQL database. See mh2sql in the reference for the full list of options.

The --odbc, --sqlite, --mysql --pgsql and --file arguments determine where the data gets written to. If you do not specify any of these, the default history driver as specified in the ODB is used.

Note that you must manually specify the .hst files to process. If converting a large number of files, you should ensure you process them in the order that they were created.

Example conversions

  • Convert a file from old .hst format to newer .dat format, in the specified directory:
    mh2sql --file /path/to/somewhere 260812.hst
    
  • Convert 3 files to a MySQL database, with connection settings specified in mysql_writer.txt. They will be processed in the order listed:
    mh2sql --mysql /path/to/mysql_writer.txt 260813.hst 260814.hst 260815.hst
    
  • Convert a file to an SQLite database in the current directory:
    mh2sql --sqlite . 260816.hst
    
  • Convert a file to the default driver specified in the ODB:
    mh2sql 260817.hst
    

SQL history database surgery

The MySQL/Postgres history databases may sometimes contain extra columns or data that you don't want.

The databases contain tables for each equipment, with a column for each variable. It also contains an extra table called _history_index that tracks which tables/columns were active at given times (e.g. tracking when variables were added/removed from the history system).

The _history_index table is the sole source of truth, not the MySQL/Postgres table list!

Background on how mlogger parses the history database

The relevant code lives in SqlHistory::hs_define_event() in history_odbc.cxx.

When mlogger connects to the database (hs_connect()), it does this:

status = fSql->ListTables(&tables);
for (unsigned i=0; i<tables.size(); i++) {
    if (tables[i] == "_history_index") {
        fHaveIndex = true;
        break;
    }
}

It scans the list of tables from MySQL only to check whether _history_index itself exists — it does not read the contents of _history_index into the in-memory global index (gHistoryIndex) at this point. That global index starts completely empty after hs_connect().

When mlogger encounters a new equipment, it calls hs_define_event(). The function uses a lazy-read strategy:

  1. Cleans up any stale in-memory Event* objects for that event name.
  2. Creates a new Event* and, if needed, issues CREATE TABLE _history_index (...) — handling DB_KEY_EXIST gracefully if it's already there.
  3. Then it looks for an IndexEntry for this event name in gHistoryIndex. Since the global index was not pre-populated at connect time, this lookup returns nothing for a new equipment.
  4. It then calls ReadIndex(fSql, event_name) to query _history_index by event name. For a genuinely new equipment that has never been logged before, this query also returns nothing.
  5. Since no index entry is found, the code concludes that this equipment has no table and proceeds to issue CREATE TABLE <table_name> — without IF NOT EXISTS.

Detecting duplicate timestamps

Display all duplicate timestamps and the number of occurrences in the table

SELECT _i_time, COUNT(_i_time) FROM table_name GROUP BY _i_time HAVING COUNT(_i_time) > 1;

Deleting a table from the database

If we just delete the table, mlogger will complain that the table does not exist, because the table is still present in the _history_index.

  1. View the entries related to a table called "mytable", useful so we don't delete anything unwarranted by accident

    SELECT * FROM _history_index WHERE table_name="mytable";
    
  2. Delete those entries, now that we've verified which rows will be deleted:

    DELETE FROM _history_index WHERE table_name="mytable";
    
  3. Drop the table from the database

    DROP TABLE mytable;
    

Rename a table in the database

In this section we cover how to rename a table in the database. This is equivalent to changing the name of the equipment (say, if you make a typo or decide on a better naming scheme).

  1. Stop the logger and the epics frontend.
  2. Rename the equipment in the ODB

    odbedit -c 'rename "/Equipment/old_name" "new_name"'
    
  3. Rename the equipment in the frontend and recompile (if needed)

  4. Rename the table in the database

    RENAME TABLE old_name TO new_name;
    
  5. Change entries in the history index

    UPDATE _history_index SET table_name = "new_name" WHERE table_name = "old_name";
    

    Check that values changed:

    SELECT * FROM _history_index WHERE table_name="new_name";
    
  6. Restart equipment frontend and mlogger. You might get some errors from mlogger, something like Cannot define event "EpicsTest/TEST", hs_define_event() status 702. If so, restart it again and the errors should clear.

  7. Check that values in the table are updating (most recent value printed at the top)

    SELECT * FROM new_name ORDER BY _i_time DESC LIMIT 10;
    
  8. Check for odd column or table names (suffix with a timestamp) - there should be messages from mlogger warning you about the create of these when the program is started.

    SHOW COLUMNS FROM new_name;
    SHOW TABLES;
    

If these exist, stop the logger, delete the columns. Delete or rename the corresponding entries in _history_index (as needed), then restart the logger.

Recreating the history index file

If you really screw up the history index file you can recreate it from scratch:

  1. Stop mlogger

  2. Delete the history index file

    DROP TABLE _history_index;
    
  3. Start then stop mlogger. This will recreate the _history_index file properly, but all the table names will have timestamps appended to them, since the tables already exist.

  4. Change the table names in the index

    UPDATE _history_index SET table_name = "new_name" WHERE table_name = "old_name";
    
  5. Drop the bad tables

    DROP TABLE old_name;
    
  6. You might have to do something similar for the column names, if it's a problem. In addition to the above commands, the following may be useful:

    ALTER TABLE table_name DROP COLUMN column_name;
    
  7. You need to ensure that itimestamp in _database_index predates the earliest timestamp in each column

    Get earliest timestamp in the table

    SELECT MIN(_i_time) FROM table_name;
    

    Set that time (or ideally a slightly earlier one) in the schema table:

    UPDATE _history_index set itimestamp = timestamp_value where table_name = "table_name";
    
  8. When everything looks clean, restart the logger and pay attention to the messages. If everything goes smoothly, there should be no errors. Check the database again for oddities.

Merging columns from different tables

In this scenario we want to reorganize our tables: we will move columns from multiple tables into one, then assign that new table to an equipment. We assume that the table we are copying into is not yet in the _history_info schema - i.e. mlogger has yet to try to write to the table. We can summarize the steps as follows:

  1. Create the new equipment, run it and mlogger to set up the _history_info schema and the table.
  2. Delete all the data out of the table (optional).
  3. Copy the new data into the table.
  4. Clean up the timestamps (optional)
  5. Drop duplicate rows
  6. Clean up the schema info in _history_info.
  7. Start up the equipment and logger again.

Step 1: Setup

  1. Create new equipment
  2. Run that equipment
  3. Restart mlogger to prompt the creation of new tables and populate it with columns
  4. Stop mlogger

The end result is that the new table is setup in the database with the new schema as well.

Step 2: Clear (optional)

Delete all rows in the table

DELETE FROM new_table;

Step 3: Copy data

When we copy we need to worry about pre-existing duplicate timestamps, creating duplicate timestamps, and mismatched timestamps. A naive INSERT command will create duplicate timestamps instead of filling NULL column values. Having mismatched timestamps will cause columns to have lots of NULL values, resulting in very ugly history plots.

The strategy is thus to

  1. Copy the columns we're interested in into the new table
  2. Fill NULL values with real values from duplicate timestamps
  3. Delete the duplicates

Repeat the below for every table and column you want to copy data from.

INSERT INTO 
    new_table
        (_t_time, 
        _i_time, 
        column1, 
        --  ...
        columnN) 
SELECT
    _t_time,
    _i_time, 
    column1,
    -- ...
    columnN
FROM 
    old_table;

While this preserves all timestamps, if there is misalignment of the timestamps then there will be a bunch of NULL values in your database at the end of this procedure. When you plot the history graph in the browser, the NULL values will show as zero. This results in very jagged graphs.

The result at the end would be something like this, columns with mismatched timestamps:

+------------+-----------------------------+-----------------------------+
| _i_time    | ucn2_he3_ts109_rdtempk_test | ucn2_he4_pt202_rdpress_test |
+------------+-----------------------------+-----------------------------+
| 1778002480 |                        NULL |                     875.671 |
| 1778002481 |                     291.293 |                        NULL |
| 1778002490 |                        NULL |                     853.388 |
| 1778002491 |                     291.293 |                        NULL |
| 1778002500 |                        NULL |                     1011.77 |
| 1778002501 |                     291.293 |                        NULL |
+------------+-----------------------------+-----------------------------+

Step 4: Round timestamps (optional)

One way to clean this up is to try to align the timestamps. One option is to round to the nearest 10 seconds (assuming this is the logging period):

UPDATE new_table 
SET 
    _t_time = FLOOR(_t_time/10)*10,
    _i_time = FLOOR(_i_time/10)*10; 

Step 5: Drop duplicate timestamps

We now need to drop duplicate timestamps and merge the data from the corresponding columns, overwriting NULL values.

Make a row for unique IDs since we have duplicate times

ALTER TABLE new_table ADD COLUMN id INT NOT NULL AUTO_INCREMENT KEY;

Fill null values of duplicate rows

UPDATE new_table v1
INNER JOIN new_table v2 ON v1._i_time = v2._i_time AND v1.id != v2.id
SET 
    v1.column1 = COALESCE(v1.column1, v2.column1), 
    -- ...
    v1.columnN = COALESCE(v1.columnN, v2.columnN);

Drop duplicate rows, keeping the first id

DELETE v1 FROM new_table v1
INNER JOIN new_table v2
WHERE
    v1.id > v2.id AND 
    v1._i_time = v2._i_time;

Drop the id column and index

ALTER TABLE new_table DROP COLUMN id;

Step 6: Update the schema

Get the earliest timestamp

SELECT MIN(_i_time) FROM table_name;

Set itimestamp to something that predates the earliest timestamp:

UPDATE _history_index set itimestamp = timestamp_value where table_name = "table_name";

Step 7: Restart

  1. Start equipment and logger
  2. May need to restart mhttpd