/** @page configuration Configuration

@tableofcontents

GMM stores all configuration settings in a simple XML-format text file.  The file is global to all users, and if it does not exist will be created the first time GMM runs.  This file will need to be edited when the application first runs, but once setup correctly shouldn't need to be edited again.

Platform	| Configuration File
------------|-------------------
Linux		| <tt>/var/.GormanMoistureMeter/GormanMoistureMeter.cfg</tt>
Windows		| <tt>C:\\ProgramData\\GormanMoistureMeter\\GormanMoistureMeter.cfg</tt>

There are two types of settings stored in the .cfg file:  global, and prefixed:

@li Global settings appear just once in configuration.  For example, @p GMMWnd.

@li Prefix settings appear multiple times in configuration, once per tab.  For example, @p a_device and @p b_device.  Every tab in GMM has a unique prefix.

@section ConfigStrings Configuration Strings

Configuration				| Type		| Default Value								| Description
----------------------------|-----------|-------------------------------------------|------------
@p *_chart_redraw_ms		| Prefix	| @p "1000"									| How long GMM waits between updates to the window and re-drawing the chart.  See @ref ConfigChartRedrawMs for details.
@p *_comm_str				| Prefix	| @p "..."									| See @ref ConfigCommStr for details.
@p *_device					| Prefix	| @p "..."									| The serial communication device to open.  See @ref ConfigDevice for details.
@p *_export_image			| Prefix	| @p "false"								| Whether to save the chart image to disk.  See @ref ConfigExportImage for details.
@p *_interval_ms			| Prefix	| @p "250"									| The length of time (in milliseconds) between attempts to read the serial port.
@p *_last_kiln				| Prefix	| @p "1"									| Internal value used to store the last known kiln number.  This value is automatically updated by GMM.
@p *_last_pkg				| Prefix	| @p "1"									| Internal value used to store the last known package number.  This value is automatically updated by GMM.
@p *_last_run				| Prefix	| @p "1"									| Internal value used to store the last known run number.  This value is automatically updated by GMM.
@p *_name					| Prefix	| @p "..."									| The name to display for this tab.  See @ref ConfigName for details.
@p *_opc_kiln_register		| Prefix	| @p "N75:3"								| The OPC register to read to obtain the kiln number.  The results are then stored in @p *_last_kiln.
@p *_opc_pkg_register		| Prefix	| @p "N75:5"								| The OPC register to read to obtain the package number.  The results are then stored in @p *_last_pkg.
@p *_opc_run_register		| Prefix	| @p "N75:4"								| The OPC register to read to obtain the run number.  The results are then stored in @p *_last_run.
@p *_opc_via_csv_filename	| Prefix	| @p "..."									| Another method by which RSLinx OPC data can be fed to GMM.  No longer supported.
@p *_opc_via_csv_regex		| Prefix	| @p "..."									| Another method by which RSLinx OPC data can be fed to GMM.  No longer supported.
@p *_read_len				| Prefix	| @p "11"									| The number of bytes to read from the serial port.
@p *_regex_pattern			| Prefix	| @p "\s*([\d.]{4})\s([\d.]{4})\s*"			| The regular expression used to parse the data read from the serial communication port.  See @ref ConfigRegexPattern for details.
@p *_run_simulation			| Prefix	| @p "FALSE"								| Whether to simulate the data, or actually read from the serial port.  Except when testing, this should be kept at @p "false".
@p allow_user_to_close		| Global	| @p "TRUE"									| Whether the GMM window can be closed by the user clicking on the titlebar "X".  When set to @p "false", GMM can only be closed via Windows Task Manager.
@p get_motd					| Global	| @p "TRUE"									| Whether to download and display a text message from the GMM web site.
@p get_version_number		| Global	| @p "TRUE"									| Whether to check to see if there is a new version from the GMM web site.
@p GMMWnd					| Global	| @p "..."									| Internal value used to store the window geometry.  This is automatically updated by GMM and shouldn't be modified by hand.
@p number_of_items_to_load	| Global	| @p "..."									| Internal value used to store the number of items to load on startup.  This is automatically updated by GMM and shouldn't be modified by hand.
@p opc_console_application	| Global	| @p "opcconsole.exe"						| The application and arguments to run to start the GMM OPC Console.
@p opc_group				| Global	| @p "INFEED"								| The OPC group name.
@p opc_output_filename		| Global	| @p "..."									| Internal value used to identify a filename to communicate between GMM and OPC Console.
@p opc_show_console_window	| Global	| @p "TRUE"									| Whether the OPC Console is shown or hidden.
@p opc_url					| Global	| @p "opcda://localhost/RSLinx OPC Server"	| The URL to use to connect to the OPC server.
@p output_directory			| Global	| @p "C:\\22m2k\\data\\%Y\\boardmc\\"		| The directory where the output files will be saved.  The @p "%Y" will be replaced with the current 4-digit year.
@p warn_board_threshold		| Global	| @p "1500"									| If a package contains this number of boards or more, a warning is shown to the user.  This requires that @p warn_if_package_contains_too_many_boards is set to @p "TRUE".
@p warn_if_package_contains_too_many_boards | Global | @p "TRUE"					| Determines if warnings are shown to the user if a package contains too many boards.  See @ref gmm_warning_boards for details.
@p warn_seconds_between_warnings | Global | @p "300"								| The number of seconds that must elapse before a 2nd warning is shown to the user.

@section ConfigDetails Configuration Details

Note that GMM itself modifies the .cfg file, such as when it is updating the @p GMMWnd value.  If you modify the .cfg file by hand while GMM is running, your modifications will be overwritten/discarded when GMM eventually exits!

@note Ensure that GMM is @b not running if you want to modify the configuration file.

@subsection ConfigChartRedrawMs *_chart_redraw_ms

The @p *_chart_redraw_ms configuration variable determines the length of time between iterations of the main GMM GUI loop.  Initially, this was used to redraw the chart on the screen -- thus the name of the configuration variable -- though now it contains much more functionality.

The loop drives a number of items, and the loop interval directly determines how often they happen.  By default, the interval is usually set to 1000 milliseconds.  The actions that occur once per loop are:

@li The graph at the bottom of the window is fully re-drawn.
@li All of the buckets are updated with the most recent numbers read from the serial port since the last interval.
@li The standard deviation is re-calculated for the package and the run.
@li Parse any updates (if any) from OPC Console.
@li Determine if warning popup messages (see @ref gmm_warning) need to be displayed.

@subsection ConfigCommStr *_comm_str

The @p *_comm_str configuration string determines how the serial port is initialized.  The string is passed as-is to the Win32 function call @p BuildCommDCBA().

Additional information on how to use this configuration string can be found by doing a Google search on functions such as @p BuildCommDCBA(), @p BuildCommDCBAndTimeoutsA(), or how to use the @p mode command to configure a serial communication port.

For example, refer to https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/mode for help with the @p mode command.

Several common strings to try:

Description 				| comm_str
----------------------------|---------
Flow control @p none		| @p "baud=9600 parity=N data=8 stop=1 to=on xon=off odsr=off octs=off dtr=on rts=on idsr=off"
Flow control @p hardware	| @p "baud=9600 parity=N data=8 stop=1 to=on xon=off odsr=off octs=on dtr=on rts=hs idsr=off"
Flow control @p xon/xoff	| @p "baud=9600 parity=N data=8 stop=1 to=on xon=on odsr=off octs=off dtr=on rts=on idsr=off"

@subsection ConfigDevice *_device

The @p *_device configuration string names the hardware device which will be opened and read.  Typically, the device name will be similar to @p com1, @p com2, etc.

By default when GMM runs for the first time and creates a new .xml file, the following devices are setup:

@li @p com4
@li @p com5
@li @p com6
@li @p com7

@see @ref ConfigName

@subsection ConfigExportImage *_export_image

The chart image is typically re-drawn every 1000 milliseconds for the active tab, as described by @p *_chart_redraw_ms.  In addition, the chart image can also be written to disk at the same time when @p *_export_image is set to @p "true".

When enabled, the images are redrawn for all tabs, not only the active tab, and they are exported to @p %%tmp% after they've been generated, which typically will look like <tt>C:\\Users\\username\\AppData\\Local\\Temp\\</tt>.

For example:

@image html a_chart.png

@subsection ConfigName *_name

The @p *_name configuration string provides a readable English-language name to each tab.  Instead of refering to a device as "com4", it can then be refered to as "Planer #1" which is more meaningful.

The original mapping between @p *_device and @p *_name is as follows:

Device	| Name
--------|-----
@p com4	| @p "Planer #1"
@p com5	| @p "Planer #2"
@p com6	| @p "Moulder"
@p com7	| @p "Spare"

@see @ref ConfigDevice

@subsection ConfigRegexPattern *_regex_pattern

The @p *_regex_pattern configuration string contains a regular expression used to parse the data read from the communication port.  Regular expressions are complex textual descriptions used to perform character matching.  Do a Google search or refer to https://en.wikipedia.org/wiki/Regular_expression for details.

The default regex used is the following:  @p "\s*([\d.]{4})\s([\d.]{4})\s*".  This is broken down as follows:

Regex			| Meaning
----------------|--------
@p "\s*"		| Skip over any number of leading whitespace characters.
@p "([\d.]{4})"	| Read exactly 4 characters and remember them.  The characters must be either numerical digits or a period.  For example:  @p 14.3. <br/>This value is interpreted as the moisture value for the current board.
@p "\s"			| Skip over a single whitespace character, such as @p space or @p tab.
@p "([\d.]{4})"	| Read exactly 4 characters and remember them.  The characters must be either numerical digits or a period.  For example:  @p 14.3. <br/>This value is interpreted as the average.
@p "\s*"		| Skip over any number of trailing whitespace characters.  For example, the @p \\r\\n (carriage return + linefeed) at the end of the string.

@section ConfigNewDevice Adding A New Device

Adding a new device (a new tab) is very simple.  There is only 1 @b required configuration string:  @p *_device.

When GMM starts, if it finds a new @p *_device configuration string, it will then add all of the other necessary configuration strings, making it relatively easy to then customize by hand.

To add a new device (tab):

@li Stop GMM.
@li Using a plain text editor such as notepad, edit the .cfg configuration file @p "C:\ProgramData\GormanMoistureMeter\GormanMoistureMeter.cfg".
@li Choose a prefix which doesn't yet exist.  For this example, we'll use the prefix @p "test".
@li Add a new @p *_device line to the .cfg file using the new prefix.  For this example:  <tt>&lt;VALUE name="test_device" val="com9"/&gt;</tt>
@li Save the .cfg file.
@li Restart GMM.

At this point, a new tab should have been created.  If the settings for this new tab need to be modified:

@li Stop GMM.
@li Using a plain text editor such as notepad, edit the .cfg configuration file @p "C:\ProgramData\GormanMoistureMeter\GormanMoistureMeter.cfg".
@li Look for and edit all the new settings that were created with the @p "test" prefix, such as @p test_name, @p test_comm_str, @p test_regex_pattern, etc.
@li Save the .cfg file.
@li Restart GMM.

*/
