// $Id: JGBitSetSensor.cpp,v 1.8 2003/03/02 04:32:45 fukasawa Exp $

//=============================================================================
/**
 *  @file    JGBitSetSensor.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 "sac/JGBitSetSensor.h"
#include "JGVariable.h"
#include "JGTriggerInfo.h"

#define GET_BITDATA(b,p) \
        (*(b + (p / 16)) & BitTable[(p % 16)])

static unsigned int BitTable[32] =
{
    0x00000001, 0x00000002, 0x00000004, 0x00000008,
    0x00000010, 0x00000020, 0x00000040, 0x00000080,
    0x00000100, 0x00000200, 0x00000400, 0x00000800,
    0x00001000, 0x00002000, 0x00004000, 0x00008000,
    0x00010000, 0x00020000, 0x00040000, 0x00080000,
    0x00100000, 0x00200000, 0x00400000, 0x00800000,
    0x01000000, 0x02000000, 0x04000000, 0x08000000,
    0x10000000, 0x20000000, 0x40000000, 0x80000000,
};

//-----------------------------------------------------------------------------
// Constructor/Destractor
//-----------------------------------------------------------------------------
JGBitSetSensor::JGBitSetSensor() : JGIODevice(), m_first(true)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGBitSetSensor::JGBitSetSensor");
    m_words = ((m_dimension + 15) / 16) + 1;
    m_saved = (u_short *)calloc(m_words, sizeof(short));
    m_readbuf = (u_short *)calloc(m_words, sizeof(short));
    for (int i = 0; i < m_dimension; i++)
    {   // Clear elements
        m_vars.push_back(NULL);
    }
}

//-----------------------------------------------------------------------------
JGBitSetSensor::JGBitSetSensor(const JGIODevice& rhs) : JGIODevice(rhs)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGBitSetSensor::JGBitSetSensor");
    m_words = ((m_dimension + 15) / 16) + 1;
    m_first = true;
    m_saved = (u_short *)calloc(m_words, sizeof(short));
    m_readbuf = (u_short *)calloc(m_words, sizeof(short));
    for (int i = 0; i < m_dimension; i++)
    {   // Clear elements
        m_vars.push_back(NULL);
    }
}

//-----------------------------------------------------------------------------
JGBitSetSensor::~JGBitSetSensor()
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGBitSetSensor::~JGBitSetSensor");
    if (m_saved != NULL)
    {
        free(m_saved);
    }
    if (m_readbuf != NULL)
    {
        free(m_readbuf);
    }
}

//-----------------------------------------------------------------------------
// Copy
//-----------------------------------------------------------------------------
JGBitSetSensor& JGBitSetSensor::operator=(const JGBitSetSensor& rhs)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGBitSetSensor::operator=");
    if (this == &rhs)
        return *this;

    (JGIODevice&)(*this) = rhs;

    if (m_saved != NULL)
    {
        free(m_saved);
    }
    if (m_readbuf != NULL)
    {
        free(m_readbuf);
    }
    m_words = rhs.m_words;
    int byte_size = m_words * sizeof(u_short);
    m_first = rhs.m_first;
    m_saved = (u_short *)malloc(byte_size);
    memmove(m_saved, rhs.m_saved, byte_size);
    m_readbuf = (u_short *)malloc(byte_size);
    memmove(m_readbuf, rhs.m_readbuf, byte_size);
    m_vars = rhs.m_vars;
    return *this;
}

//-----------------------------------------------------------------------------
// Append variable info.
//-----------------------------------------------------------------------------
int JGBitSetSensor::add(JGVariable * var)
{
    TRACE_FUNCTION(TRL_LOW, "JGBitSetSensor::add");

    // Read memory block
    if (var->offset() >= m_dimension)
    {
        TRACE_ERROR((_TX("Illegal offset (0x%x)\n"), var->offset()));
        return BEE_ERROR;
    }

    if (m_vars[var->offset()] != NULL)
    {
        TRACE_ERROR((_TX("Duplicate variable data (0x%x)\n"), var->offset()));
        return BEE_ERROR;
    }

    m_vars[var->offset()] = var;
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Initial current value.
//-----------------------------------------------------------------------------
int JGBitSetSensor::init()
{
    TRACE_FUNCTION(TRL_LOW, "JGBitSetSensor::init");
    if (! m_first)
    {
        return BEE_SUCCESS;
    }

    // Read memory block
    int result = m_plc.memory()->read(m_plc.addr(), m_dimension, m_saved);
    if (result < 0)
    {
        TRACE_ERROR((_TX("PLC read error\n")));
        return BEE_ERROR;
    }

    for (int i = 0; i < m_dimension; i++)
    {
        if (m_vars[i] == NULL)
        {
            continue;
        }
        JGvalue val = (GET_BITDATA(m_saved, i) != 0) ? true : false;
        m_vars[i]->curval(val);
    }

    m_first = false;
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Check to update bit registers, and notify event.
//-----------------------------------------------------------------------------
int JGBitSetSensor::sense()
{
    TRACE_FUNCTION(TRL_LOW, "JGBitSetSensor::sense");

    vector<JGVariable *> trg_vars;
    int byte_size = m_words * sizeof(short);

    // Read memory block
    int result = m_plc.memory()->read(m_plc.addr(), m_dimension, m_readbuf);
    if (result < 0)
    {
        TRACE_ERROR((_TX("PLC read error\n")));
        return BEE_ERROR;
    }

    // Compare latest and current
    for (int i = 0; i < m_dimension; i++)
    {
        if (m_vars[i] == NULL)
        {   // Ignore reserve bit
            continue;
        }
        int saved_bit = GET_BITDATA(m_saved, i);
        int cur_bit   = GET_BITDATA(m_readbuf, i);
        int diff = saved_bit ^ cur_bit;
        if (diff != 0)
        {
            JGvalue val = (cur_bit != 0) ? true : false;
            m_vars[i]->curval(val);
            trg_vars.push_back(m_vars[i]);    // Entry trigger info
        }
    }

    // Update latest data
    memmove(m_saved, m_readbuf, byte_size);

    // Issue trigger
    if (trg_vars.size() > 0 && m_subject != NULL)
    {
        m_subject->changed(trg_vars);
    }

    return trg_vars.size();
}

