// $Id: JGLimit.cpp,v 1.4 2003/03/16 14:51:00 fukasawa Exp $

//=============================================================================
/**
 *  @file    JGLimit.cpp
 *
 *  @author  Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 2001-2003 BEE Co.,Ltd. All rights reserved.
 *
 * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */
//=============================================================================

#define BEE_BUILD_DLL

#include "JGLimit.h"
#include "JGEquipment.h"
#include "JGLimitManager.h"
#include "JGInfoManager.h"
#include "JGTimeModule.h"
#include "BS2Message.h"
#include "BS2ListItem.h"
#include "BS2Item.h"
#include "BS2DeclAtoms.h"

//-----------------------------------------------------------------------------
// Make limit nack list for S2F46
//-----------------------------------------------------------------------------
//      LIMITACK = 1 : LIMITID does not exist
//                 2 : UPPERDB > LIMITMAX
//                 3 : LOWERDB < LIMITMIN
//                 4 : UPPERDB < LOWERDB
//                 5 : Illegal format specified UPPERDB or LOWERDB
//                 6 : Ascii value cannot be translated to numeric
//                 7 : Duplicatre limit definition for this variable
//                >7 : Duplicatre limit definition for this variable
//-----------------------------------------------------------------------------
enum
{
    LIMITACK_LIMITID_NOT_EXIST = 1,
    LIMITACK_UPPERDB_OVER      = 2,
    LIMITACK_LOWERDB_UNDER     = 3,
    LIMITACK_ILLEGAL_DEADBAND  = 4,
    LIMITACK_ILLEGAL_FORMAT    = 5,
    LIMITACK_ILLEGAL_ASCII_VALUE = 6,
    LIMITACK_DUPLICATE_DEFINE  = 7
};

static BS2ListItem * nackLimitBorder(BYTE limid, BYTE nack)
{
    BS2ListItem * listitem = new BS2ListItem;
    BS2Item * item = BS2Item::factory("LIMITID", new BS2Binary(limid));
    listitem->add(item);
    item = BS2Item::factory("LIMITACK", new BS2Binary(nack));
    listitem->add(item);
    return listitem;
}

//-----------------------------------------------------------------------------
// Copy
//-----------------------------------------------------------------------------
JGLimit& JGLimit::operator=(const JGLimit& rhs)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::operator=");
    if (this == &rhs)
        return *this;
    this->JGNameInfo::copy(rhs);
    m_unit = rhs.m_unit;
    m_limitid = rhs.m_limitid;
    m_transition = rhs.m_transition;
    m_maxval = rhs.m_maxval;
    m_minval = rhs.m_minval;
    m_borders = rhs.m_borders;
    m_manager = rhs.m_manager;
    return *this;
}

//-----------------------------------------------------------------------------
// Sort border data.
//-----------------------------------------------------------------------------
void JGLimit::sort()
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::sort");

    std::sort(m_borders.begin(), m_borders.end(), comp_border());

    return ;
}



//-----------------------------------------------------------------------------
// Check defined limit attributes in S2F45
//-----------------------------------------------------------------------------
JGLimitBorder * JGLimit::find(int limid)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::find");

    JGLimitBorderSet::iterator iter = m_borders.begin();
    for ( ; iter != m_borders.end(); iter++)
    {
        JGLimitBorder * border = &(*iter);
        if (border->limitId() == limid)
        {
            return border;
        }
    }
    return NULL;
}

//-----------------------------------------------------------------------------
// Check defined limit attributes in S2F45
//-----------------------------------------------------------------------------
BS2ListItem * JGLimit::checkNewBorders(JGLimit& limAttrs)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::checkNewBorders");
    BS2ListItem * limit_nack = NULL;

    limAttrs.sort();
    JGvalue before_upval;
    JGLimitBorderSet::iterator iter = limAttrs.m_borders.begin();
    for ( ; iter != limAttrs.m_borders.end(); iter++)
    {
        JGLimitBorder * border = &(*iter);
        int limid = border->limitId();
        // Check limit id
        JGLimitBorder * cur_border = find(limid);
        if (cur_border == NULL)
        {
            limit_nack = nackLimitBorder(limid, LIMITACK_LIMITID_NOT_EXIST);
            break;
        }
        if (! border->enabled())
        {
            continue;                   // Not defined deadband
        }

        // Check LowerDB and UpperDB
        int format = m_var->format();
        int upval_format = border->upperdb().format();
        int lowval_format = border->lowerdb().format();
        if (upval_format == ATOM_ASCII)
        {
            const BCHAR * upval_ptr = border->upperdb().toString().c_str();
            if (! isdigit(*upval_ptr))
            {
                limit_nack = nackLimitBorder(limid, LIMITACK_ILLEGAL_ASCII_VALUE);
                break;
            }
            upval_format = format;
        }
        if (lowval_format == ATOM_ASCII)
        {
            const BCHAR * lowval_ptr = border->lowerdb().toString().c_str();
            if (! isdigit(*lowval_ptr))
            {
                limit_nack = nackLimitBorder(limid, LIMITACK_ILLEGAL_ASCII_VALUE);
                break;
            }
            lowval_format = format;
        }

        if (border->upperdb() < border->lowerdb())
        {
            limit_nack = nackLimitBorder(limid, LIMITACK_ILLEGAL_DEADBAND);
            break;
        }
        if (m_maxval < border->upperdb())
        {
            limit_nack = nackLimitBorder(limid, LIMITACK_UPPERDB_OVER);
            break;
        }
        if (border->lowerdb() < m_minval)
        {
            limit_nack = nackLimitBorder(limid, LIMITACK_UPPERDB_OVER);
            break;
        }
        if (! before_upval.isNil())
        {
            if (! (before_upval < border->lowerdb()))
            {   // overwrapped
                limit_nack = nackLimitBorder(limid, LIMITACK_DUPLICATE_DEFINE);
                break;
            }
        }

        // Check data format
        if (format == ATOM_INT1 || format == ATOM_INT2)
        {
            format = ATOM_INT4;
        }
        else if (format == ATOM_UINT1 || format == ATOM_UINT2)
        {
            format = ATOM_UINT4;
        }
        else if (format == ATOM_FLOAT4)
        {
            format = ATOM_FLOAT8;
        }
        if (upval_format != format || lowval_format != format)
        {
            limit_nack = nackLimitBorder(limid, LIMITACK_ILLEGAL_FORMAT);
            break;
        }

        before_upval = border->upperdb();         // Save upper db
    }
    return limit_nack;
}

//-----------------------------------------------------------------------------
// Update limit attributes in S2F45
//-----------------------------------------------------------------------------
int JGLimit::updateNewBorders(JGLimit& limAttrs)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::updateNewBorders");

    limAttrs.sort();
    m_borders.clear();    // Clear all border value
    JGvalue befor_upval;
    JGLimitBorderSet::iterator iter = limAttrs.m_borders.begin();
    for ( ; iter != limAttrs.m_borders.end(); iter++)
    {
        m_borders.push_back(*iter);
        m_borders.back().enabled(true);
    }
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Create variable limit data. (for S2F48)
//-----------------------------------------------------------------------------
BS2Item * JGLimit::attributeItem()
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::attributeItem");
    BS2Item * item;

    BS2ListItem * baselist = new BS2ListItem();
    item = BS2Item::factory(_TX("VID"), m_var->vid());
    baselist->add(item);

    BS2ListItem * mbrlist = new BS2ListItem();
    item = BS2Item::factory(_TX("UNITS"), new BS2Ascii(m_unit));
    mbrlist->add(item);

    item = BS2Item::factory(_TX("LIMITMIN"), m_minval);
    mbrlist->add(item);

    item = BS2Item::factory(_TX("LIMITMAX"), m_maxval);
    mbrlist->add(item);

    BS2ListItem * bdrlist = new BS2ListItem();
    for (size_t i = 0; i < m_borders.size(); i++)
    {
        BS2Item * border = m_borders[i].attributeItem();
        bdrlist->add(border);
    }
    mbrlist->add(bdrlist);

    baselist->add(mbrlist);

    return baselist;
}

//-----------------------------------------------------------------------------
// Create variable limit data. (for S6F11)
//-----------------------------------------------------------------------------
BS2Item * JGLimit::ceidItem(int format)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::ceidItem");

    BS2Item * item;
    if (format == ATOM_ASCII)
    {
        const string& ceidName =
                    JGEquipment::instance()->getConf(CONF_LIMIT_CEID);
        if (ceidName.size() > 0)
        {   // CEID is number
            item = BS2Item::factory(_TX("CEID"), m_id);
        }
        else
        {   // CEID is "LIMIT:%s"
            BCHAR buf[128];
            _stprintf(buf, ceidName.c_str(), m_vid.toString().c_str());
            item = BS2Item::factory(_TX("CEID"), new BS2Ascii(buf));
        }
    }
    else
    {   // CEID = BASEID + VID
        u_int ceidNum = m_id.getUInt();
        ceidNum += variable()->vid().getUInt();
        BCHAR buf[128];
        _stprintf(buf, _TX("%u"), ceidNum);
        string ceidStr(buf);
        m_id.set(ceidStr, format);
        item = BS2Item::factory(_TX("CEID"), m_id);
    }

    return item;
}

//-----------------------------------------------------------------------------
// Check limit zone transition.
//-----------------------------------------------------------------------------
int JGLimit::transition(JGvalue& last, JGvalue& current)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::transition");
    int result = TRANSITION_NONE, ret;
    m_trnum = 0;
    for (size_t i = 0; i < m_borders.size() && i < (size_t)MAX_LIMIT_ZONE; i++)
    {
        ret = m_borders[i].transition(last, current);
        if (ret != TRANSITION_NONE)
        {
            m_limitid = m_borders[i].limitId();
            m_ids[m_trnum] = m_limitid;
            m_trnum++;
            m_transition = ret;
            result = ret;
        }
    }
    return result;
}

//-----------------------------------------------------------------------------
// Make value atom with variable name.
//-----------------------------------------------------------------------------
BS2Atom * JGLimit::getAtom(const string& vname)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::getAtom");
    BS2Atom * atom = NULL;
    if (vname == VAR_EVENT_LIMIT)
    {
        atom = new BS2Binary(m_ids, m_trnum);
    }
    else if (vname == VAR_LIMIT_VARIABLE)
    {
        atom = BS2Atom::factory(m_vid);
    }
    else if (vname == VAR_TRANSITION_TYPE)
    {
        atom = new BS2Binary((BYTE)m_transition);
    }
    return atom;
}

//-----------------------------------------------------------------------------
// Print items of the limit
//-----------------------------------------------------------------------------
void JGLimit::printOn(string& buf, int mode)
{
    TRACE_FUNCTION(TRL_LOW, "JGLimit::printOn");
    ACE_UNUSED_ARG(mode);

    string vidStr;

    m_vid.get(vidStr);
    m_id.get(buf);
    buf += _TX(" ");
    buf += vidStr;
    buf += _TX(" ");
    buf += (m_enabled) ? _TX("true") : _TX("false");
    // Deadband ids

    return ;
}

