Style Guide for VTScada Code

Developers at Trihedral use this style guide to create consistent and easy-to-read VTScada code. If you obey the recommendations in this guide, it will be easier for others to read your code and for you to read others' code.

Notation:

$$$ means you must obey this rule

$$ means you should obey this rule, but there is some leeway

$ means that this is a guideline

General organization

A module is the main unit of design and each logical module shall be in its own source file named after the module with a .SRC suffix.

Prologue and Epilogue

  1. $$$ Each module shall have a prologue that consists of a multi-line, 80 characters-per-line comment describing the purpose of the module. The first line of the comment must contain the name and ancestry of the module, centered between lines of equal signs:
{====== MyService.MyModule ======}
{ Description of module }
{================================}

Note that the number of equal signs varies - whatever is required to pad out the comment to 80 characters (the closing parenthesis should be in column 79). The last line of the comment should be strictly equal signs, used to mark the end of the comment.

Note that much of the existing code uses a \ between the module names in the first line. New code shall make use of the . (local scope) separator in keeping with the intended default preference for . as the scope operator.

  1. $ With module declarations, align all “Module” keywords in the same column when possible.
  2. $ Module declarations with file names should align the file names in the same column.
  3. $ Consider moving a sub-module to its own file if it exceeds 500 lines of code. Avoid small files whenever possible. There should be a good balance between overly lengthy source files that are unwieldy to navigate through, and many small files that make it difficult to follow the code without opening dozens of files.
  4. $$$ The beginning < of a module must be followed by a comment block describing the module.
  5. $$$ The closing > of a module and the opening < of another module must be separated by a blank line.
  6. $$$ The closing angled brackets for a module must be preceded by a comment indicating the end of the module. It is acceptable to have an blank line between the closing ] and the opening {.
...
]
{ End of MyService.MyModule } >

Comments

  1. $$ Full line comments (on their own line) that are used to describe the action of statements, etc. should have a single space before and after the opening bracket, and a space before the closing bracket:
{ This is a comment }
X = 1;
  1. $ If you are editing an existing module that uses a different style of comments (e.g. the five asterisk style), continue using that style within that module for the sake of consistency. You may consider changing the existing style if there aren't many instances of it or if there is an inconsistent mixture of styles.
  2. $$ Comments generally begin with a capital letter, however, single or few word comments may or may not use capitalization, as appropriate.
  3. $$ Multi-line comments should use a single set of braces. The text should be left aligned.
{ This comment has multiple lines
and should be left aligned }
  1. $$ All variable and parameter comments should start in the same column (preferably column 31) and end at the same column (preferably column 79, with the semi-colon in column 80).
  2. $$$ Any code which is inserted for testing or for other temporary purposes must have a comment that includes two consecutive question marks { ?? }, which can be rapidly located before code is shipped.
  3. $ Multiple in-line comments appearing on multiple lines should be aligned, especially for parameter comments in a module call. Padding out the right brace is preferred as well.
    IfThen(X == Y { First two are equal       } ||
X == Z { First and last are equal } ||
Y == Z { Last two are equal },
...
);

Variables and Parameters

  1. $$$ Start parameters with an opening bracket in the leftmost column on the line immediately following the module name.
  2. $$$ Indent all variable and parameter definitions two spaces (except when appearing in a class-block, in which case extra indentation is required).
  3. $$$ All variables and parameters must have a comment.
  4. $$$ Use descriptive names for variables and parameters.
  5. $$$ Variable and parameter names must begin with a capital letter. The exception is a variable or parameter which is a pointer, such as pResult.
  6. $$$ Place the semi-colon for the variable or parameter declaration after the comment on the same line. This associates the comment with the variable declaration, which will get the correct highlighting for code coverage in the Source Debugger.
  7. $$ For default values, align all = in the same column when possible.
  8. $ Blank lines may be placed between variable groups that have different purposes. A comment identifying the purpose of the group would be useful.
  9. $ A blank line is not required between the end of the parameters and the beginning of the variables, though either is acceptable.
  ...
)
[
...
  1. $ Constants may be declared with all upper case names, as appropriate.
  2. $ Constants used for indices in arrays used as records should begin with the # character.
  3. $ The keywords CONSTANT, PROTECTED, RETAINED, SHARED, MODULE and STRUCT should be uppercase, though mixed or lowercase is acceptable. The case should be consistent amongst all variable declarations in a module. Since Module and Struct appear differently in the code, adopting a different, but consistent, style for those keywords is acceptable.
  4. $$$ Structure definitions must indent their field names two spaces and should include comments for each field.

States

  1. $$$ State declarations should have a blank line before them but not after them.
...
]
NextState [
If X Done;
]
  1. $$$ State name declarations must not be indented.
  2. $$$ State names must begin with a capital letter.
  3. $$$ The square bracket beginning the state declaration must be on the same line as the state name, separated from the name by one space.

Statements

  1. $$$ Statements in a state must be indented by two spaces. Your editor should be configured to use two spaces instead of a tab when you hit the TAB key.
  2. $$$ “If” statements (predicate conditions) with attached script blocks must have the beginning [ and ending ] each on their own lines at the same indentation as the “If” statement.
  3. $$$ “If” must be written with the mixed capitalization.
  4. $$$ Indent statements within script blocks an additional two spaces. Statements nested within other statements carry on in this fashion, increasing indentation by two spaces at a time.
  5. $$$ Do not use spaces on either side of the opening parenthesis for a function or module call. The exception, if desired, is IfElse (, IfThen (, WhileLoop (, and DoLoop (.
  6. $$$ All commas must be followed by a space except when appearing at the end of a line (as in the condition for an IfThen/IfElse statement).
  7. $$ Where multiple = statements are listed together, align the = in the same column when possible. This is especially valuable where several related assignments are referencing the same array or structure. It helps to highlight the similarity and differences between such lines of code.
  8. $$$ Semicolons terminating a statement must follow immediately at the end of the line without any preceding or trailing spaces.
  9. $$ Your editor should be configured to automatically trim trailing spaces when the file is saved.
  10. $$$ Parameters to functions, such as IfThen and IfElse, must be considered as if they are independent statements, following the same rules such as those for indentation and semicolon use.
  11. $$$ No more than one statement may appear on a line.
  12. $ Long assignment statements can wrap to the next line at approximately column 80. Each line should start in the same column as the initial assignment. Non-terminated lines should end with an operator. Alignment of statements that span several lines should not be random, but aid in the readability of the code, as illustrated in this example.
X = Parm1 + Parm2 + Parm3 +
Parm4 + Parm5;
  1. $$ When working with UI controls that require coordinates, use the global constants EditHt, DropHt, BtnWd, etc, rather than hardcoded coordinates.

Operators

  1. $$$ Place a space before and after the operators = == != += -= *= /= %= && || ^ @ < > <= >= ? : >> and <<.
  2. $$$ Do not place a space before or after the [ ] ++ and –- operators.
  3. $$$ When ++ or –- are being used, and there is no functional difference, they should come after the variable, not before, e.g.
I++;
  1. $$ Spaces around the + - * / and % operators are preferred, but can be omitted where clarity is not sacrificed. They can be helpful in complex statements, but take up unnecessary space in simple ones.
  2. $$$ The prefix operators ! & and * must not have a space after them.
  3. $$$ The scope operators \ and . must never have a space between themselves and their variable.
  4. $$ Always use the local scope operator (.) unless you intentionally mean for the scope to be global (\).
MyTag.Name
  1. $$ The operators <> and >< are discouraged. Use != instead.
  2. $$ The operator ~ is discouraged. Use ! instead.
  3. $$ The operators => and =< are discouraged. Use >= and <= instead.
  4. $$ The operators | and & are discouraged. Use || and && instead.

Function calls

  1. $$$ Start all function calls with an upper case letter.
  2. $$$ Functions with long lists of parameters or conditions can wrap to the next line at approximately column 80. The parameters should start in the same column. There is a balance as to which column the line break should occur. Statements that span multiple lines are sometimes harder to read, while statements that extend beyond the visible area of a text editor are also a problem. Try to keep a statement on a single line if only a small portion would get split out to the following line.
MyModule(Parm1, Parm2, Parm3,
Parm4, Parm5);
  1. $$$ Except for the condition (if they have one), WhileLoop, DoLoop, IfThen, IfElse, CriticalSection, Execute and Case must terminate all internal statements with semi-colons, not commas, even though they are technically parameters.
  2. $$ The functions WhileLoop, DoLoop, IfThen, IfElse, CriticalSection, Execute and Case do not need an ending comment such as { End WhileLoop }, except in cases of extreme complexity or length. Modern editors and proper indentation easily identify the end of such statements for you.

WhileLoop & IfThen

  1. $$$ Place the condition on the same line as the WhileLoop/IfThen.
  2. $$$ Indent all internal statements two spaces from the WhileLoop/IfThen.
  3. $$$ Terminate the statement with the ); on a separate line aligning it in the same column as the WhileLoop/IfThen.
    WhileLoop(I < N,
...
I++;
);

DoLoop

  1. $$$ Start the DoLoop statement with no parameters on the first line.
  2. $$$ Indent the internal statements of the loop two spaces from the DoLoop.
  3. $$ Terminate the loop with the comment { While } at the same column position as the DoLoop, followed by the condition on the next line (indented by two spaces), followed by the ); on the next line aligning it in the same column as the DoLoop. It is acceptable, however, to put the condition on the same line as the { While }.
    DoLoop(
...
{ While }
!Valid(X);
);

IfElse

  1. $$$ Place the condition on the same line as the IfElse.
  2. $$$ If an Execute is required for the TRUE case, it is placed on the first line as well, with its opening bracket immediately following it (no space) and its closing bracket on its own line in the same column as the IfElse. This avoids gratuitous indentation of the body of the condition and makes IfElse and IfThen statements look very similar in construction with all of their statements indented by two spaces from the condition and the closing of the block always aligned with the start of the block.
  3. $$$ The FALSE case should be started with a comment { Else } in the same column as the IfElse. If it requires an Execute, the same guidelines are followed as described previously, except that the closing brackets for the Execute and the IfElse are kept together on the last line with no space in between.
  4. $ An IfThen can be used in place of an Execute where applicable.
  5. e. $$ Multiple nested IfElse statements should all start in the same column if they represent multiple mutually exclusive choices. Avoiding extra indentation will help with the readability.
IfElse(X,
  Y = 1;
{ Else }
  Y = 2;
);

IfElse(X == 0, Execute(
  ...
);
IfElse(X < 10, Execute(
  ...
);
{ Else } Execute(
   ...
)));

Case

  1. $$$ Place the condition on the same line as the Case.
  2. $$ Identify the integer value of each internal statement with a comment. The comment should be indented two spaces from the Case.
  3. $$ Simple Case statements should appear on the same line as their comment, whereas complex Case statements that use Execute should appear on a separate line indented two spaces from the beginning of the comment.
  4. $$$ The closing bracket should be on its own line in the same column as the Case
X = Case(FirstVar,
      { 0 } A;
      { 1 } B + C;
      { 2 } 2*A + B/2 + Min(C, D%5);
    );

Case(SecondVar,
  { 0 - This}
    DoThis();
  { 1 - This & That } Execute(
    DoThis();
    DoThat();
  );
  { 2 - The other thing }
    Other();
);
  1. $$ All function calls should have a set of parentheses, regardless of whether they require parameters. This makes it easy to differentiate between a function and a variable. The exceptions are Invalid, Self, TRUE and FALSE, which do not need parentheses.
  2. $ If a module does not need or have a valid return value, Return() is preferred over Return(Invalid).
  3. $$ When True(X) and False(X) are used with parameters, they require parentheses and should have only the first letter capitalized. If they are being used as constants, they should be all uppercase without parentheses: TRUE and FALSE.