Formatting

Source Code

Comments

Function descriptions in .fun files should be filled out to ensure tool tips work properly.

Whenever possible, inline comments (//) should be preferred to block comments (/* */). Comment delimiters should be separated from the body of the comment by spaces. If a comment is intended to separate one section of code from another, use a commented line of dashes or equals signs, as in markdown.

// Prefer inline comments

/* Not block comments */

// Make sure there is a space after your delimiter

// If you want to make a section heading, do it like this
//--------------------------------------------------------

// Or like this
//=============================

// You can use your judgement to decide when to wrap
// comments and how long to make your heading lines

Braces

When programming in C, braces should be used in if, else, for, do and while statements. Simple single line if statements are allowed. If statements should be formatted as below with respect to spacing and new lines.

if (myStatement) {
    int temp = 0;
    return temp;
}
else {
    somethingElse;
}

// Simple if statements can be on a single line
if (myStatement) return 0;

// Ternary conditional operators are also fine
myStatement ? return 0 : somethingElse;

// This is not okay
if (myStatement)
  something...;
somethingElse...;

Ignores

Libraries can include diagnostic pragmas to hide specific warnings during compile time. Automation Studio often throws compile time warnings for conversions between c and IEC types. Though the warnings are usually harmless, they obfuscate the build output. Loupe supports the following options for ignoring warning(s).

// Do not warn about incompatible integer to pointer and pointer to integer conversions. This pragma is about implicit conversions.
// For explicit conversions, the pragmas -Wno-int-to-pointer-cast and -Wno-pointer-to-int-cast may be used.

#pragma GCC diagnostic ignored "-Wint-conversion"

The following pragmas should be used cautiously. Consider fixing warnings instead of adding these pragmas.

// Do not warn for pointer argument passing or assignment with different signedness.
#pragma GCC diagnostic ignored "Wpointer-sign"

// Do not warn when there is a conversion between pointers that have incompatible types.
// This warning is for cases not covered by -Wpointer-sign.

#pragma GCC diagnostic ignored "-Wincompatible-pointer-types"

// Do not warn whenever a function is defined with a return type that defaults to int.
// Also do not warn about any return statement with no return value in a function whose return type is not void (falling off the end of the function body is considered returning without a value).
// Also do not warn about a return statement with an expression in a function whose return type is void, unless the expression type is also void.
#pragma GCC diagnostic ignored "-Wreturn-type"

Note: Ignores are only supported in GCC 6 and newer

Libraries

Libraries should be distributed as binaries with possible exceptions based on circumstances. This helps with tracking changes, avoids updates to central library overwriting application changes inadvertently, and help improve the shared libraries over time. If library source is distrubuted, it is recommended only to be done as reference, and should not be used directly in applications.

Folder Structure

Loupe libraries should be written in C or C++ and should contain the following files.

LICENSE.md

This should contain meta data for the library.

Copyright 2019 Loupe

This software is licensed under the Loupe Software License Agreement.
Please contact Loupe for information at info@loupe.team or 1-800-240-7042.

README.md

Contains a info section, a list of changes with each release version, and a link to documentation.

# Info
Library is provided by Loupe
https://loupe.team
info@loupe.team
1-800-240-7042

# Change log

 - 1.00.0 - Increment version release
 - 0.00.4 - Add initial documentation
 - 0.00.3 - Add Replace variable functions
 - 0.00.2 - Remove unused dependencies
 - 0.00.1 - Initial version

# Documentation

Documentation for this and other Loupe libraries can be found at https://loupeteam.github.io/Sandbox/libraries.html

Variables

Declarations & Prefixes

All variable names should be in lower camel case. Variable names may contain an informative prefix (see below for some examples). Variables declared in a .var file should only have initial values specified if they are non-zero.

myLocalVariable		// variables are lower camel case
gMyGlobalVariable   // Global variables start with g
pMyPointer          // Pointers start with p
dMyDynamicVar		// Dynamic variables start with d
diMyDigitalInput    // Digital inputs start with di (similarly, do, ai, ao)

Type declarations should use upper camel case and end in _typ. These declarations are within .typ files in Automation Studio.

// General type name
MySubsystemIn_typ

// Due to name length restrictions of 32 characters,
// these structure names are both acceptable for
mySubsystem.in.par.myParameters

MySubsystemInPar_typ
or
MySubsystemPar_typ

Subsystem Structures

Each subsystem or task should be able to function on its own. It should contain the commands required to request actions and give proper responses. To that end, most subsystems have structures for accepting commands/parameters, internally processing, and outputting statuses.

Members of a structure should be lower camel case and descriptive. Only denote the variable type for IO (see below for examples).

Subsystems should not nest other subsystems in their structures, include axes.

Subsystems typically adhere to the follow structure. Some projects prefer to use full words, which is also acceptable

  • subsystem
    • in
      • cmd
      • par
      • cfg
    • io
      • diMyInput
      • doMyOutput
    • out
      • done
      • busy
      • error
      • errorId
      • errorString
    • internal
      • fub optional
      • cmd optional
// Commands that this subsystem can respond to
subsystem.in.cmd.myCommands..
subsystem.in.cmd.acknowledgeError   // Acknowledge Error Command to clear persistent errors

// Configuration variables may or may not be user changeable.
// They can be saved through power cycle and backed up to file.
subsystem.in.cfg.staticBootValues..

// Runtime parameters are not typically saved through a power cycle
// These are parameters that change with use of the subsystem
subsystem.in.par.dynamicApplicationValues..

// Any IO that is specific to a subsystem can be included under IO
// The prefix should indicate the direction and type of IO.
subsystem.io.diMyDigitalInput
subsystem.io.doMyDigitalOutput
subsystem.io.aiMyAnalogInput
subsystem.io.aoMyAnalogOutput

// Output statuses from this system
subsystem.out.statusOutputs..
subsystem.out.busy
subsystem.out.done

// Error Information integrates with ErrorCollector
subsystem.out.error         // Error boolean indicates an active error that needs to be cleared
subsystem.out.errorID       // Error ID can be used to look up language specific text
subsystem.out.errorString   // Error text to display

// Variables that should only be used by this subsystem
// Function blocks should be in FUB
subsystem.internal.fub.internalFunctionBlocks..

// Other internal variables/structures can be included directly in .internal
subsystem.internal.internalVariables..

Pointers

Variables that reference a location (pointers in C and dynamic variables in structured text) can be used as seen below.

// Structured text dynamic variables
dMyDynamicVariable ACCESS gMyGlobalStructure.IN.PAR.pMyPointer
dMyDynamicVariable.myMembers..

// C pointers
MyPoint_type* pMyVariable = gMyGlobalStructure.IN.PAR.pMyPointer;
pMyVariable->myMembers..
MyPoint_type* pMyArray = &MyArray

Functions & Function Blocks

Function Calls

Function calls should contain spaces between arguments.

// Function Calls should contain spaces after commas
myFunction( &myStruct, &myString, strlen(myString) );

Function Block States

For FUBs that have done, busy, error bits the following standard is used to check status. Avoid using multiple states to handle a single function block.

// States with function blocks
// With Error, Busy, Done bits
IF myFub.error THEN
   // Handle error
ELSIF myFub.done THEN
   // Do next thing
ELSIF NOT myFub.busy THEN
   // Execute FUB
   myFub.enable := TRUE;
END_IF

Or if FUB has a status instead

// States with function blocks
// With Error, Busy, Done bits
IF myFub.status = 0 THEN // ERR_OK
   // Done, Do next thing
ELSIF myFub.status < 65534 THEN // ERR_FUB_ENABLE_FALSE
   // Handle error
ELSIF myFub.status = 65534 THEN // ERR_FUB_ENABLE_FALSE
   // Execute FUB
   myFub.enable := TRUE;
END_IF