/******************************************************************************
 *
 * Copyright (C) 2015 Alexander Rössler
 *
 *
 * Safety latch for error signals
 *
 ******************************************************************************
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * THE AUTHORS OF THIS PROGRAM ACCEPT ABSOLUTELY NO LIABILITY FOR
 * ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
 * TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
 * harming persons must have provisions for completely removing power
 * from all motors, etc, before persons enter any danger area.  All
 * machinery must be designed to comply with local and national safety
 * codes, and the authors of this software can not, and do not, take
 * any responsibility for such compliance.
 *
 * This code was written as part of the LinuxCNC project.  For more
 * information, go to www.linuxcnc.org.
 *
 ******************************************************************************/
component safety_latchv2 "latch for error signals";
description """
HAL component that implements a safety latch for error singnals
with customizable harm, healing and latching features.

When the component is not enabled the error input value is
forwarded to output without further modififactions.

If error-in is true the error count is increased by harm.
If error-in is false the error count is decreased by heal.
When the error count exceeds the threscold value error-out is
set to true. If latching is false the error-out pin will only
return to false when reset is set to true.

The inputs pin min and max clamp the error count value to a
specified range.
""";
pin_ptr in bit error_in = false "Error Input";
pin_ptr in s32 heal = 1 "Heal when ok per tick";
pin_ptr in s32 harm = 1 "Harm when error per tick";
pin_ptr in bit latching = true "If a reset is necessary to heal an error";
pin_ptr in bit reset = false "Reset input";
pin_ptr in s32 threshold = 100 "Error output threshold";
pin_ptr in s32 min = 0 "Minimum count";
pin_ptr in s32 max = 1000 "Maximum count";
pin_ptr in bit enable = true "If not enabled the error count is passed to the output";
pin_ptr out s32 count = 0 "Current count";
pin_ptr out bit error_out = false "Error output";
pin_ptr out bit ok_out = true "Ok output";

variable hal_bit_t last_reset = false;

function _ nofp;
license "GPL";
author "Alexander Roessler";
;;
#define MIN(x,y) (x < y ? x : y)

FUNCTION(_)
{
    if (!gb(enable))
    {
        sb(error_out, gb(error_in));
        sb(ok_out, !gb(error_in));
        ss(count, 0);
    }
    else
    {
        if ((gb(reset) ^ last_reset) && gb(reset))
        {
            ss(count, 0);
            sb(error_out, false);
            sb(ok_out, true);
        }

        if (gb(error_in))
        {
            ss(count, gs(count) + gs(harm));
        }
        else
        {
            ss(count, gs(count) - gs(heal));
        }
        ss(count, MIN(MAX(gs(count), gs(min)), gs(max)));

        if (gs(count) >= gs(threshold))
        {
            sb(error_out, true);
            sb(ok_out, false);
        }
        else if (!gb(latching))
        {
            sb(error_out, false);
            sb(ok_out, true);        
        }
    }

    last_reset = gb(reset);

    return 0;
}
