// $Id: JGTraceManager.cpp,v 1.5 2003/03/16 14:51:01 fukasawa Exp $

//=============================================================================
/**
 *  @file    JGTraceManager.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 "JGTraceManager.h"
#include "JGEquipment.h"
#include "JGInfoManager.h"
#include "JGTaskTrigger.h"
#include "BS2ACKMessage.h"
#include "BS2ErrorMessage.h"
#include "BS2ListItem.h"
#include "BS2DeclAtoms.h"

static JGTraceManager * _manager = NULL;

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
JGTraceManager::JGTraceManager() : JGManager(CATEGORY_TRACE)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGTraceManager::JGTraceManager");
}

//-----------------------------------------------------------------------------
JGTraceManager::~JGTraceManager()
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGTraceManager::~JGTraceManager");

}

//-----------------------------------------------------------------------------
// Return own.
//-----------------------------------------------------------------------------
JGTraceManager * JGTraceManager::instance()
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::instance");
    if (_manager == NULL)
    {
        _manager = new JGTraceManager;
    }
    return _manager;
}

//-----------------------------------------------------------------------------
// Save variables to database.
//-----------------------------------------------------------------------------
int JGTraceManager::init(void * parm)
{
    TRACE_FUNCTION(TRL_MIDDLE, "JGTraceManager::init");
    ACE_UNUSED_ARG(parm);

    this->initKeeping();

    m_maxtrace = 4;
    const string& max_trace = m_equipment->getConf(CONF_MAX_TRACE_COUNT);
    if (max_trace.size() > 0)
    {
        m_maxtrace = _tcstoul(max_trace.c_str(), NULL, 0);
    }

    m_maxsvid = 8;
    const string& max_svid = m_equipment->getConf(CONF_MAX_TRACE_SVID);
    if (max_svid.size() > 0)
    {
        m_maxsvid = _tcstoul(max_svid.c_str(), NULL, 0);
    }

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Load trace infomation to keeping table from database.
//-----------------------------------------------------------------------------
int JGTraceManager::initKeeping(int parm)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::initKeeping");
    ACE_UNUSED_ARG(parm);

    JGTraceTable::iterator iter = m_keepTraces.begin();
    for ( ; iter != m_keepTraces.begin(); iter++)
    {
        JGTrace * trace = &((*iter).second);
        trace->m_cursmp = 0;
        trace->convertime();            // m_dsper: string to ACE_Time_Value
    }

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Get trace information by trid.
//-----------------------------------------------------------------------------
JGTrace * JGTraceManager::find(JGid& trid)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::find");

    JGTraceTable::iterator iter = m_traces.find(trid);
    if (iter == m_traces.end())
    {
        return NULL;
    }
    return &((*iter).second);
}

//-----------------------------------------------------------------------------
// Get trace information  by trid, which red from database.
//-----------------------------------------------------------------------------
JGTrace * JGTraceManager::getKeeping(JGid& trid)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::getKeeping");

    JGTraceTable::iterator iter = m_keepTraces.find(trid);
    if (iter == m_keepTraces.end())
    {
        return NULL;
    }
    return &((*iter).second);
}

//-----------------------------------------------------------------------------
// Set trace information into table.
//-----------------------------------------------------------------------------
int JGTraceManager::entry(JGTrace * trinfo)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::entry");
    if (m_maxtrace <= (int)m_traces.size())
    {
        return BEE_ERROR;
    }
    m_traces.insert(JGTracePair(trinfo->trid(), *trinfo));
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Remove trace information from table.
//-----------------------------------------------------------------------------
int JGTraceManager::remove(JGid& trid)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::remove");

    JGTraceTable::iterator iter = m_traces.find(trid);
    if (iter == m_traces.end())
    {
        return BEE_ERROR;
    }
    m_traces.erase(iter);
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Start trace
//-----------------------------------------------------------------------------
int JGTraceManager::start(JGid& trid)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::start");
    JGTrace * trace = this->find(trid);
    if (trace == NULL)
    {
        TRACE_ERROR((_TX("Starting Trace (%s) is not found\n"),
                     trid.toString().c_str()));
        return BEE_ERROR;
    }

    // Initial trace infomation
    trace->m_cursmp = 0;    // reset current count

    // Start trace timer
    long result =
        ACE_Proactor::instance()->schedule_timer(*trace,
                                                 (void *)ACE_TEXT("TRACE"),
                                                 trace->m_dspertv,
                                                 trace->m_dspertv);
    if (result == -1)
    {
        TRACE_ERROR((_TX("%p\n"), _TX("schedule_timer")));
        return BEE_ERROR;
    }
    trace->m_tmid = result;             // save timer id

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Cancel trace
//-----------------------------------------------------------------------------
int JGTraceManager::cancel(JGid& trid)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::cancel");
    JGTrace * trace = this->find(trid);
    if (trace == NULL)
    {
        TRACE_ERROR((_TX("Canceling Trace (%s) is not found\n"),
                     trid.toString().c_str()));
        return BEE_ERROR;
    }

    // Cancel timer
    ACE_Proactor::instance()->cancel_timer(trace->m_tmid);

    // Remove trace infomation from table
    this->remove(trid);

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Trace Initialize Send : S2F23 --> S2F24 (TIAACK)
//                                     = 0 : Normal
//                                       1 : too many SVIDs
//                                       2 : No more traces allowed
//                                       3 : invalid period
//                                       4 : svid not found
//                                     > 3 : equipment-specified error
//-----------------------------------------------------------------------------
int JGTraceManager::parseS2F23(void * clientData, BS2Item * item)
{
    JGid trid;
    JGVariable * var;
    JGvalue value;
    JGTrace * trinfo = (JGTrace *)clientData;
    if (item->isList())
    {   // Ignore list item
        return 0;
    }
    BS2Atom * atom = item->atom();
    if (item->name() == _TX("SVID"))
    {
        atom->get(trid);
        JGEquipment * equip = trinfo->m_manager->m_equipment;
        JGInfoManager * infoManager =
                        (JGInfoManager *)equip->findManager(CATEGORY_INFO);
        var = infoManager->variableBySvid(trid);
        if (var != NULL)
        {
            trinfo->insert(var);
        }
        else
        {
            return -4;
        }
    }
    else if (item->name() == _TX("TRID"))
    {
        atom->get(trid);
        trinfo->trid(trid);
    }
    else if (item->name() == _TX("DSPER"))
    {
        trinfo->m_dsper = ((BS2Ascii *)atom)->value();
        trinfo->convertime();
    }
    else if (item->name() == _TX("TOTSMP"))
    {
        //JGvalue totsmp = *atom;        // When compiled by release, abort.
        JGvalue totsmp(*atom);
        trinfo->m_totsmp = totsmp.getUInt();
    }
    else if (item->name() == _TX("REPGSZ"))
    {
        // JGvalue repgsz = *atom;       // When compiled by release, abort.
        JGvalue repgsz(*atom);
        trinfo->m_repgsz = repgsz.getUInt();
    }
    return 0;
}

//-----------------------------------------------------------------------------
BS2Message * JGTraceManager::traceInit(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGTraceManager::traceInit");

    BYTE ack;
    BS2Message * replymsg;
    JGTrace trinfo;
    trinfo.manager(this);
    int result = msg->traverse(JGTraceManager::parseS2F23, &trinfo);  // Message Parse
    if (result < 0)
    {   // Illegal parameter
        ack = (BYTE)(-result);
        replymsg = BS2Message::response(msg, ack, _TX("TIAACK"));
        return replymsg;
    }

    // Check duplicate trid
    JGTrace * other = this->find(trinfo.trid());
    if (other != NULL)
    {   // Found same trid's trace information
        this->cancel(trinfo.trid());        // Cancel and Remove trace
        ACE_OS::sleep(1);                   // Delay
    }

    ack = ACK_OK;
    if (trinfo.m_totsmp == 0)
    {
        ; // Cancel trace
    }
    else if (trinfo.varSize() > m_maxsvid)
    {
        ack = 1;
    }
    else
    {
        int result = this->entry(&trinfo);
        if (result >= 0)
        {
            this->start(trinfo.trid());     // Start trace
        }
        else
        {
            ack = 2;
        }
    }

    // Create reply message.
    replymsg = BS2Message::response(msg, ack, _TX("TIAACK"));
    return replymsg;
}

//-----------------------------------------------------------------------------
// Thread of received trigger event.
//-----------------------------------------------------------------------------
BS2Message * JGTraceManager::msg_svc(JGMessageTrigger * trigger,
                                     BS2Message * msg)
{
    ACE_UNUSED_ARG(trigger);
    BS2Message * replymsg = NULL;

    if (msg->sf() == SFCODE(2,23))
    {
        replymsg = this->traceInit(msg);
    }
    else if (msg->sf() == SFCODE(6,2))
    {   // Ignore
    }
    else
    {   // Unexpected message
        replymsg = this->unrecognized(msg);
    }
    return replymsg;
}
