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 (myStatment)
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.
Folder Structure
Loupe libraries should be written in 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 typically adhere to the follow structure.
- subsystem
- in
- cmd
- par
- cfg
- io
- out
- done
- busy
- error
- errorId
- errorString
- internal
- fub optional
- cmd optional
- in
// 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.
// 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