Introducing RML

RML (Roast Monitor Language) provides a means to add customised functionality to the CoffeeSnobs.com.au Roast Monitor.

A sequence of RML statements forms a program which is run within Roast Monitor to perform defined actions when specified events occur. The complete program is executed once per second, the effect is as if monitoring is continuous during the roast process.

RML has been designed to be extensible, to allow for additional actions and events. The downside is that the language specification will seem rather complex to someone unfamiliar with computer languages; however many requirements can be achieved fairly simply - if you find the specification too daunting, skip to the examples.


Formal specification

Language elements are shown below in bold, underlined text Like this.

Keywords (words that have special meaning in RML) are shown here in upper case to distinguish them from other text, although within programs, they can actually be in upper, lower, or mixed case; according to your preference.

Program

A program is simply a list of statements which are performed in order. It may also include a Priority directive to specify the displayed sequence of variables.

Statement

A Statement can be either a Simple statement or a Compound statement.

Compound statement

A Compound statement consists of "{" (left brace or curly bracket), one or more statements, and "}" (right brace or curly bracket). This might sound like a circular definition, but it is a way of clumping multiple statements, so they act like a single statement, particularly within the If statement and Limit statement.

Simple statement

A Simple statement is one of:

Empty statement

An Empty statement consists only of ";" (semicolon). It is the simplest of statements, because it does nothing except fulfil the requirements of a statement within the language; it is sometimes useful within an If statement.

Adjust statement

An Adjust statement actions a variable control on an optional roast controller.

   ADJUST control-name value;

This statement is effective only when RML mode is selected, either via the radio button or via an RML MODE statement. The available control-names depend on the particular roast controller, but "HEAT" and "AIR" always operate the corresponding heat slider control, with value between 0 and 100.

Alert statement

An Alert statement writes a message to the RoastMonitor alerts area.

   ALERT message;

message is an Expression that is interpreted as a string of text.

Audio statement

An Audio statement plays the contents of an audio file.

   AUDIO filename;

filename is the name of a file of audio data. The data can be formatted as WAV, AIFF, or AU, but definitely not MP3 (blame the lawyers). The data may be mono or stereo, at sample rates ranging from 8kHz to 48kHz.

If all you want is sound effects, you might find suitable sound files in \windows\media (standard Windows sounds), or you can create your own. If you need to edit audio files, the free tool audacity is highly recommended.

Beep statement

A Beep statement consists of the keyword BEEP, followed by a semicolon. Not surprisingly, it causes the computer to emit a beep.

   BEEP;

Debug statement

A Debug statement turns debugging on or off.

   DEBUG ON;
   DEBUG OFF;

Whenever debugging is turned on, a trace of statements and variable values is logged in "RoastMonitorLog.txt".

The first time it is turned on, a dump of the internal form of the RML program with line-numbers is logged to simplify matching the trace and the program exceution.

If statement

An If statement provides a way to choose whether or not to perform an action. It has two formats:

   (a) IF condition action1
   (b) IF condition action1 ELSE action2
The process begins by evaluating condition; if the result is "true", then action1 is performed. If the result is "false", case (a) does nothing, while case (b) performs action2.

condition is an Expression. Some expressions are naturally boolean (either "true" or "false"); e.g. xxx > 3. Other expressions are interpreted appropriately.

action1 and action2 are each Statements; so they can be either Simple statements or Compound statements. This means that they could themselves be If statements. When If statements are nested like this, it is important to note that an ELSE always matches the most recent IF. There is no practical limit on the depth of such nesting.

Note that there is no semicolon at the end of the If statement, as such; there may be one as the terminator of an embedded simple statement.

Label statement

A Label statement annotates the graph with a message exactly as if it had been entered in the comments field of RoastMonitor.

   LABEL message;

message is an Expression that is interpreted as a string of text.

Limit statement

A Limit statement controls the number of times that an action is performed.

    LIMIT count action
If the number of times this statement has been performed so far is less than count, then action is performed; if not, no action occurs.

count is an Expression. Most often it will be a very simple expression such as "1", but it can be as complex as required. If the expression is not naturally a number, it will be interpreted appropriately.

action is a Statement; so it can be either a Simple statement or a Compound statement.

A Limit statement is most useful in conjunction with an If statement. Consider:

   IF temperature > 200
      BEEP;
All will be quiet until the temperature reaches 200, when you will hear a beep - followed one second later by another beep, then another, and another, and ... Remember that the RML program runs through every statement once per second. Probably what was wanted was to beep once or twice when the temperature reached 200. This is better:
   IF temperature > 200
      LIMIT 2
         BEEP;
Note that the following wouldn't do what we wanted:
   LIMIT 1
      IF temperature > 200
         BEEP;
because the IF will be performed only once, right after pressing the "Go" button.

Note that, like the If statement, there is no semicolon at the end of the Limit statement, as such; there may be one as the terminator of an embedded simple statement.

Log statement

A Log statement writes a message to "RoastMonitorLog.txt" to provide a permanent record of some relevant information.

   LOG message;

message is an Expression that is interpreted as a string of text.

Mode statement

A Mode statement sets the mode of an optional roast controller.

   MODE mode;

mode may be:

Save statement

A Save statement performs the same function as the save button in the RoastMonitor window. It will create a CSV and/or JPG file as specified in preferences.txt

   SAVE filename;

filename is any Expression

. It is interpreted relative to the RoastMonitor directory, so typically you should prefix it with "profiles\". Don't add a csv or jpg suffix; it will be added automatically.

Set statement

A Set statement stores a value into a User-defined variable.

   SET variable_name = value;
Variables can be used to hold any values during a roast cycle. The user chooses a name for each. Subsequently, the variable name can be used to reference that value in other expressions.

value is any Expression

Switch statement

A Switch statement actions an on/off control on an optional roast controller.

   SWITCH switch-name ON;
   SWITCH switch-name OFF;

This statement is effective only when RML mode is selected, either via the radio button or via an RML MODE statement. The available switch-names depend on the particular roast controller.

Priority directive

A Priority directive can be used to specify the sequence in which variables are displayed in the RML monitor window. Any other variables used within the program will be displayed in alphabetical order following the priority variables.

   PRIORITY  variable_1  variable_2 ... ;

Note that this is a directive, because it does not form part of the running program. It is not a statement, so can't be used within another statement such as IF.

Expression

Expressions range from very simple to very complex.

Simple expressions are numbers, strings (text wrapped in quotes), User-defined variable, Pre-defined variable or Dynamic variable,.

More complex expressions are formed by combining simple expressions in various ways.

User-defined variable

Any number of User-defined variables can be used to hold any values during a roast cycle. The user chooses a name for each. The name must begin with a letter, and consist only of letters, digits 0 to 9, and underscores. The name must not conflict with any Pre-defined variable.

Until given a value by way of a Set statement, the variable has a nil value.

Pre-defined variable

Pre-defined variables provide convenient access to values on either the main graph or the template - we identify these sources with the letters "M",and "T".

Each graph can have up to 9 different plots, numbered from 1 to 9.

At each point on the graph, there are three units of data that might be of interest: time, temperature, and rate of change. Unfortunately, "time" and "temperature" start with the same letter so, to avoid confusion, we use "S" (seconds), "D" (Degrees), and "R" to identify them. "S" represents seconds since "Go", "D" represents the temperature in Celsius or Fahrenheit according to your preference, and "R" is degrees per minute.

Various significant positions are labelled "START", "END", "MIN", "MAX", and "NOW"

The four elements are combined as  source   plot number   unit    _   position , so

   M1D_NOW
means "main meter" "plot 1" "degrees" NOW, so it is the current temperature.
   T1D_NOW
is the temperature at the current point of the template.

Where the graph contains a label like "First crack" or user comment, the values at that point can be accessed in a similar way:

   T1D"First crack")
is the temperature of first crack in the template.
   T1S("First crack")
is the time of first crack in the template.
   T1R("First crack")
is the rate at first crack in the template.

Dynamic variable

Dynamic variables are similar to Pre-defined variables, except that they do not relate to values on graphs. The dynamic variables are:

Conversion of expressions

There are three types of expression in RML:

The various places where expressions are used each expect to see expressions of an appropriate type; for example, an If statement expects its condition expression to be boolean. Unlike many computer languages, RML is lenient; so an expression of the wrong type is automatically converted to the correct type.

The conversion rules are:
Actual type
Required type Boolean Number String
Boolean None If zero, false, otherwise true If empty, false, otherwise true
Number If false, 0, otherwise 1 None Count of characters
String The word "true", or "false" Representation of the number None

Comments

Comments can be placed anywhere within an RML program. A comment begins with the hash ("#") character, and continues to the end of the line; it can contain any characters.


Examples

The request that triggered this whole project

To provide alerts when the bean-drop door is left open, and when first crack is overdue.

# Detect that the bean-drop door was left open.
if M1D_NOW < 80 AND M1R_NOW < -15
   {
   BEEP;
   ALERT "Close Bean Drop";
   }

# First crack should have happened by now.
if M1D_NOW > 200 AND M1R_NOW < 3
   {
   BEEP; 
   ALERT "Stalled roast";
   }

The gas bottle has run out

Detect no-gas by a fall in temperature

if M1S_NOW > 60   # Avoid false alarm on initial temperature fall with bean drop.
   if M1D_NOW < PREVIOUS
      LIMIT 3
         {
         BEEP;
         LOG "Temp was " : PREVIOUS : ", now " : M1D_NOW;
         }
PREVIOUS = M1D_NOW;

First crack should happen soon

Provide a warning that first crack is about to happen.

if M1D_NOW > 195
   LIMIT 3
      BEEP;

Each of the above examples is a complete RML program in itself. However, all of the above could be combined into a single program to perform all of the functions.

The kitchen sink - a test of IF statements

The following nonsense was used during development with tweaks here and there to verify, amongst other things, that nested IF statements performed correctly.

LIMIT 5
   BEEP;

LIMIT 1
   AUDIO "\windows\media\tada.wav";

LOG 2+3*4+5;
SET Alpha = 5;

# comment line ... 
LIMIT 3 {
   IF alpha = 3        # comments on the same line.
      {
      BEEP;
      SET Beta=6;
      }
   ELSE
      IF alpha=5
         {
         BEEP;
         SET gamma=9;
         }
      ELSE 
         {
         BEEP;
         SET delta=11;
         }
IF 3
   {
   BEEP;
   SET Epsilon=14;
   }
LOG "A=" : alpha : " B=" : beta : " C=" : gamma :
    " D=" : delta : " E=" : epsilon;
}

Creating RML programs

An RML program can be created using any text editor, such as notepad, wordpad, emacs, or vim. Use simple text format, and save the file with an extension of ".rml". Don't use a word processor, such as MS Word, it will cause you grief.

Loading and running RML programs

An RML program is loaded via the "RML" button.

If an error in the program is discovered, the error and its location will be displayed in a pop-up window and program loading stops.

Upon successful load, an RML monitor window pops up. This window displays the current value of all User-defined variables. Any of these variables can be changed on-the-fly by clicking on the variable, typing a new value, and pressing Enter. Buttons allow you to suspend, then resume the program.

The RML program begins executing as soon as the "Go" button is pressed (or immediately if "Go" had already been pressed). The complete program is executed once per second (more correctly, it runs at the update interval specified in Preferences.txt, which defaults to 1000 miliseconds. The exact interval is typically a little longer because of overheads and interruptions from other processes).

The program runs until the "Reset" button is pressed.

If "default.rml" exists in the RoastMonitor folder, it will be loaded automatically on startup.

Style

Programming style is a personal choice, and RML doesn't impose any rules on how you lay out your program. However, the examples show a recommended style, with subordinate statements indented, and matching IF/ELSE pairs aligned. This makes the program easier to understand and simpler to modify.


Revision 2.58
© 2007-2015 Coffee Snobs Pty.Ltd.
CoffeeSnobs.com.au