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

//=============================================================================
/**
 *  @file    JGProcProgManager.cpp
 *
 *  @author  Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 2001-2002 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 "JGProcProgManager.h"
#include "JGProcProgStorage.h"
#include "JGEquipment.h"
#include "JGVariable.h"
#include "JGTrigger.h"
#include "JGProcessManager.h"
#include "BS2ErrorMessage.h"
#include "BS2Message.h"
#include "BS2ListItem.h"
#include "BS2DeclAtoms.h"

enum
{
    PPGNT_OK = 0,
    PPGNT_ALREADY = 1, PPGNT_NO_SPACE = 2, PPGNT_INVALID_PPID = 3,
    PPGNT_BUSY = 4,    PPGNT_NOT_ACCEPT = 5
};


static JGProcProgManager * _manager = NULL;

//-----------------------------------------------------------------------------
//
// Watchdog Response
//
//-----------------------------------------------------------------------------
class ProcProgTimerHandler : public TaskTimerHandler
{
    friend class JGProcProgManager;

public:
    ProcProgTimerHandler(JGid& ppid) : TaskTimerHandler(),
                                       m_ppid(ppid), m_result(0) {}
    virtual ~ProcProgTimerHandler(void) {}
    virtual void handle_time_out(const ACE_Time_Value& tv, const void * arg)
    {
        TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::handle_timeout");
        ACE_UNUSED_ARG(tv);
        ACE_UNUSED_ARG(arg);

        JGProcProgManager * mngr = (JGProcProgManager *)m_task;

        // Signal
        mngr->m_event.signal();   // signal to wait in pp manager.

        // Message
        TRACE_ERROR((_TX("Timeout response message from host.\n")));

        m_result = BEE_ERROR;
        return ;
    }

    void init(size_t length, int maxRetry) {
        m_length = length;
        m_maxRetry = maxRetry;
        m_curRetry = 0;
    }

    void dump() const {
            string ppidStr = m_ppid.toString();
            ACE_DEBUG((LM_DEBUG,
                ACE_TEXT("ppid = %s, result = %d, length = %d, max retry = %d, current = %d.\n"),
                         m_ppid.toString().c_str(), m_result, m_length, m_maxRetry, m_curRetry));
        }
//
protected:
    JGid m_ppid;
    int  m_result;  // return value;
    int  m_length;
    int  m_maxRetry;
    int  m_curRetry;
};

//-----------------------------------------------------------------------------
//
// Process Program Manager
//
//-----------------------------------------------------------------------------
//
// S7Fx Message Paser
//
class S7FxParam : public BS2Traverser
{
public:
    S7FxParam() : BS2Traverser() {}
    virtual ~S7FxParam() {}

    JGid   m_ppid;

    // Parse atom to ppid
    int parsePPIDAtom(BS2Atom * atom)
    {
        if (atom->isAscii())
        {
            m_ppid = ((BS2Ascii *)atom)->value();
        }
        else if (atom->isBinary())
        {
            m_ppid.set(((BS2Binary *)atom)->value(), ((BS2Binary *)atom)->size());
        }
        else
        {
            ACE_ERROR((LM_ERROR, ACE_TEXT("Received PPID illegal format(%s).\n"),
                                 atom->format()));
            return -(PPGNT_INVALID_PPID);
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
JGProcProgManager::JGProcProgManager() : JGManager(CATEGORY_PROCPROG),
                                         m_programs(NULL)
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGProcProgManager::JGProcProgManager");
}

//-----------------------------------------------------------------------------
JGProcProgManager::~JGProcProgManager()
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGProcProgManager::~JGProcProgManager");
    if (m_programs != NULL)
    {
        delete m_programs;
    }
}

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

//-----------------------------------------------------------------------------
// Initialize
//-----------------------------------------------------------------------------
int JGProcProgManager::init(void * parm)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::init");
    ACE_UNUSED_ARG(parm);

    string dirName = m_equipment->rootDirectory();
    dirName += DIR_SEPARATOR_STRING;
    dirName += _TX("recipe");

    size_t maxppq = MAX_PROCPROG_COUNT;
    string rcpq = m_equipment->getConf(CONF_MAX_RECIPE_COUNT);
    if (rcpq.size() > 0)
    {
        maxppq = _tcstoul(rcpq.c_str(), NULL, 10);
        if (maxppq == 0)
        {
            maxppq = MAX_PROCPROG_COUNT;
        }
    }

    m_retryTime = 0;
    string rtm = m_equipment->getConf(CONF_PPGNT_RETRY_TIME);
    if (rtm.size() > 0)
    {
        m_retryTime = _tcstoul(rtm.c_str(), NULL, 10);
    }

    // Create Process Program Controller
    m_programs = new JGProcProgStorage(dirName, _TX("prg"));
    m_programs->init(maxppq);

    m_changeList = m_equipment->variable(VAR_PP_CHANGE_LIST);
    m_changeName = m_equipment->variable(VAR_PP_CHANGE_NAME);
    m_changeStatus = m_equipment->variable(VAR_PP_CHANGE_STATUS);
    m_ppFormat = m_equipment->variable(VAR_PP_FORMAT);

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Find the process program
//-----------------------------------------------------------------------------
int JGProcProgManager::find(JGid& ppid)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::find");

    ProcProgBlock * ppblock = m_programs->find(ppid);
    if (ppblock == NULL)
    {
        return -1;
    }
    return ppblock->m_size;
}

//-----------------------------------------------------------------------------
int JGProcProgManager::find(const string& ppid)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::find");

    ProcProgBlock * ppblock = m_programs->find(ppid);
    if (ppblock == NULL)
    {
        return -1;
    }
    return ppblock->m_size;
}

//-----------------------------------------------------------------------------
// Read the process program
//-----------------------------------------------------------------------------
int JGProcProgManager::read(JGid& ppid, BYTE*& datap, size_t& size)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::read");

    return m_programs->get(ppid, datap, size);
}

//-----------------------------------------------------------------------------
// Write the process program
//-----------------------------------------------------------------------------
int JGProcProgManager::write(JGid& ppid, BYTE * datap, size_t size)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::write");

    ProcProgBlock * ppblock = m_programs->find(ppid);
    BYTE chgstate = (ppblock != NULL) ? UPDATED : CREATED;
    JGvalue stval(chgstate);    // state is UInt1

    int result = m_programs->put(ppid, datap, size);
    if (result >= 0)
    {
        // PP Change (send S6F11)
        m_changeName->setv(ppid);
        m_changeStatus->setv(stval);
    }
    return result;
}

//-----------------------------------------------------------------------------
// Delete the process program
//-----------------------------------------------------------------------------
int JGProcProgManager::del(JGid& ppid)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::del");

    int result = m_programs->del(ppid);
    if (result >= 0)
    {
        // PP Change (send S6F11)
        m_changeName->setv(ppid);
        JGvalue stval((BYTE)DELETED);
        m_changeStatus->setv(stval);
    }
    return result;

}

//-----------------------------------------------------------------------------
// Purge all process programs
//  * m_changeName must be vector type
//-----------------------------------------------------------------------------
int JGProcProgManager::purge()
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::purge");

    m_ppids.clear();
    m_programs->all(m_ppids);    // Save All PPIDs

    int result = m_programs->purge();
    if (result < 0)
    {
        return result;
    }

    // PP Change (send S6F11)
    // m_changeName->setv(ppid);
    JGvalue stval((BYTE)PURGED);
    m_changeStatus->setv(stval);
    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Upload by Equipment
//-----------------------------------------------------------------------------
int JGProcProgManager::upload(JGid& ppid, int watchTime, int maxRetry)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::upload");

    ProcProgBlock * ppblock = m_programs->find(ppid);
    if (ppblock == NULL)
    {
        TRACE_ERROR((_TX("Not found process program(%s).\n"), ppid.toString().c_str()));
        return BEE_ERROR;
    }
    if (m_watchdog.value() != NULL)
    {
        TRACE_ERROR((_TX("Waiting other request to host.\n")));
        return BEE_ERROR;
    }
    ProcProgTimerHandler * tmh = new ProcProgTimerHandler(ppid);
    m_watchdog = (void *)tmh;
    tmh->init(ppblock->m_size, maxRetry);

    //
    // Process Program Inquire
    //
    BS2Message * msg = BS2Message::factory(SFCODE(7,1));
    BS2ListItem * rootlist = new BS2ListItem;
    msg->add(rootlist);
    rootlist->add(BS2Item::factory(_TX("PPID"), BS2Atom::factory((b_value&)ppid)));
    rootlist->add(BS2Item::factory(_TX("LENGTH"), new BS2Int4((int)ppblock->m_size)));
                                                  //  ^^^^^^^
                                                  //  modify format, if necessary
    int result = this->send(msg);
    if (result <= 0)
    {
        TRACE_ERROR((_TX("Can't send to inquire process program.\n")));
        delete tmh;
        m_watchdog = NULL;
        return BEE_ERROR;
    }

    // Start watchdog timer
    ACE_Time_Value tv(watchTime);
    this->addTimer(tv, tmh);

    // Wait for response
    if (m_event.wait() == -1)
    {
        TRACE_ERROR((_TX("Wait error return.\n")));
        tmh->m_result = BEE_ERROR;
        this->cancelTimer(tmh->m_tmid);
    }
    tmh = (ProcProgTimerHandler *)m_watchdog.value();
    result = tmh->m_result;
    delete tmh;
    m_watchdog = NULL;     // Unlock
    return result;
}

//-----------------------------------------------------------------------------
// Download by Equipment
//-----------------------------------------------------------------------------
int JGProcProgManager::download(JGid& ppid, int watchTime)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::download");

    if (m_watchdog.value() != NULL)
    {
        TRACE_ERROR((_TX("Waiting other request to host.\n")));
        return BEE_ERROR;
    }
    ProcProgTimerHandler * tmh = new ProcProgTimerHandler(ppid);
    m_watchdog = (void *)tmh;

    //
    // Process Program Request
    //
    BS2Message * msg = BS2Message::factory(SFCODE(7,5));
    msg->add(BS2Item::factory(_TX("PPID"), BS2Atom::factory((b_value&)ppid)));
    int result = this->send(msg);
    if (result <= 0)
    {
        TRACE_ERROR((_TX("Can't send to request process program.\n")));
        delete tmh;
        m_watchdog = NULL;
        return BEE_ERROR;
    }

    // Start watchdog timer
    ACE_Time_Value tv(watchTime);
    this->addTimer(tv, tmh);

    //
    // Wait for response
    //
    if (m_event.wait() == -1)
    {
        TRACE_ERROR((_TX("Wait error return.\n")));
        tmh = (ProcProgTimerHandler *)m_watchdog.value();
        tmh->m_result = BEE_ERROR;
        this->cancelTimer(tmh->m_tmid);
    }
    tmh = (ProcProgTimerHandler *)m_watchdog.value();
    result = tmh->m_result;
    delete tmh;
    m_watchdog = NULL;   // Unlock
    return result;
}

//-----------------------------------------------------------------------------
// Send Verification by Equipment
//-----------------------------------------------------------------------------
int JGProcProgManager::verified(JGid& ppid, vector<ProcProgVerification>& ppv)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::verified");

    if (m_watchdog.value() != NULL)
    {
        TRACE_ERROR((_TX("Waiting other request to host.\n")));
        return BEE_ERROR;
    }
    ProcProgBlock * ppblock = m_programs->find(ppid);
    if (ppblock == NULL)
    {
        TRACE_ERROR((_TX("Not found process program(%s).\n"), ppid.toString().c_str()));
        return BEE_ERROR;
    }

    // Make process program verification
    BS2Message * msg = BS2Message::factory(SFCODE(7,27));
    BS2ListItem * rootlist = new BS2ListItem;
    msg->add(rootlist);
    rootlist->add(BS2Item::factory(_TX("PPID"), BS2Atom::factory((b_value&)ppid)));
    BS2ListItem * bodylist = new BS2ListItem;
    rootlist->add(bodylist);
    for (size_t i = 0; i < ppv.size(); i++)
    {
        BS2ListItem * mbrlist = new BS2ListItem;
        bodylist->add(mbrlist);
        mbrlist->add(BS2Item::factory(_TX("ACKC7A"), new BS2UInt1(ppv[i].m_ack)));
        mbrlist->add(BS2Item::factory(_TX("SEQNUM"), new BS2Int4(ppv[i].m_seqnum)));
                                                     //  ^^^^^^^
                                                     //  modify format, if necessary
        mbrlist->add(BS2Item::factory(_TX("ERRW7"), new BS2Ascii(ppv[i].m_error)));
    }

    //
    // Process Program Verification Send (Multi-Block is unsupported, yet)
    //
    int result = this->send(msg);
    if (result <= 0)
    {
        TRACE_ERROR((_TX("Can't send to inquire process program.\n")));
        return BEE_ERROR;
    }

    return result;
}

//-----------------------------------------------------------------------------
// Accept trigger from the variable.
//-----------------------------------------------------------------------------
int JGProcProgManager::notify(const string& category, JGVariable * var,
                              void * arg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::notify");
    ACE_UNUSED_ARG(arg);

    TRACE_ERROR((_TX("%s : %s = %s\n"), category.c_str(), var->charName(),
                                        var->getv().toString().c_str()));

    int  result = BEE_SUCCESS;
    JGid ppid;
    if (category == TRID_RECIPE_EXEC)
    {
        // Send S6F11
        m_equipment->sendEvent(this, EVT_PP_SELECTED);
    }
    else if (category == TRG_CREATE)
    {
        JGid ppid = m_changeName->getv().toString();
        //
        // if ppid is number, must convert to integer
        //
        m_programs->create(ppid); // As request from operator , create control block
        // Send S6F11
        m_equipment->sendEvent(this, EVT_PP_CHANGE);
    }
    else if (category == TRG_EDIT || category == TRG_DELETE)
    {
        // Send S6F11
        m_equipment->sendEvent(this, EVT_PP_CHANGE);
    }
    else if (category == TRG_PURGE)
    {
        // Send S6F11
        m_equipment->sendEvent(this, EVT_PP_CHANGE, m_changeList);
    }
    else
    {
        result = BEE_ERROR;
    }
    JGvalue initval((BYTE)0);
    m_changeStatus->setv(initval);

    return result;
}

//-----------------------------------------------------------------------------
// Delete Process Program Send (S7F17 -> S7F18)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::remove(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::remove");

    BS2Message * replymsg;
    BYTE ack = ACKC7_OK;
    vector<JGid> ppids;
    int result = msg->getId(_TX("PPID"), ppids);
    if (result < 0)
    {
        TRACE_ERROR((_TX("Receive illegal message format (%s).\n"),
                     msg->name().c_str()));
        ack = ACKC7_NOT_PERMIT;
        replymsg = BS2Message::response(msg, ack, "ACKC7");
        return replymsg;
    }

    if (ppids.size() == 0)
    {   // all process program
        m_programs->purge();
    }
    else
    {   // Check PPID
        for (size_t i = 0; i < ppids.size(); i++)
        {
            ProcProgBlock * ppblock = m_programs->find(ppids[i]);
            if (ppblock == NULL)
            {   // not found process program id
                ack = ACKC7_INVALID_PPID;
                break;
            }
        }
        if (ack == 0)
        {   // PPIDs valid
            for (size_t i = 0; i < ppids.size(); i++)
            {
                m_programs->del(ppids[i]);
            }
        }
    }

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

//-----------------------------------------------------------------------------
// Current EPPD Request (S7F19 -> S7F20)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::eppd(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::eppd");

    BS2Message * replymsg = BS2Message::response(msg); // Create reply message.
    BS2ListItem * rootlist = new BS2ListItem;
    replymsg->add(rootlist);

    vector<JGid> ppids;
    m_programs->all(ppids);    // Get All PPIDs
    for (size_t i = 0; i < ppids.size(); i++)
    {
        BS2value val((b_value&)ppids[i]);
        rootlist->add(BS2Item::factory(_TX("PPID"), val.getAtom()));
    }
    return replymsg;
}


//-----------------------------------------------------------------------------
// Formatted Process Program Request (S7F25 -> S7F26)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::upFormatted(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::upFormatted");

    BS2Message * replymsg = BS2Message::response(msg);
    BS2ListItem * rootlist = new BS2ListItem;
    replymsg->add(rootlist);

    BS2Atom * atom = msg->getAtom("PPID");
    if (atom == NULL)
    {
        TRACE_ERROR((_TX("Receive illegal message format (%s).\n"),
                     msg->charName()));
        return replymsg;
    }

    JGid ppid = reinterpret_cast<b_id&>(*atom);
    JGVariable * variable;
    ProcProgBlock * ppblock = m_programs->find(ppid);
    if (ppblock != NULL)
    {   // Found process program
        rootlist->add(BS2Item::factory(_TX("PPID"), BS2Atom::factory(atom)));

        variable = m_equipment->variable(VAR_EQUOP_MODEL);
        rootlist->add(BS2Item::factory(_TX("MDLN"), variable->getAtom()));

        variable = m_equipment->variable(VAR_EQUIP_SOFTREV);
        rootlist->add(BS2Item::factory(_TX("SOFTREV"), variable->getAtom()));

        BS2ListItem * pplist = new BS2ListItem;
        //
        // BS2ListItem * cmdlist = new BS2ListItem;
        // pplist->add(cmdlist);
        // cmdlist->add(BS2Item::factory(_TX("CCODE"), new BS2UInt2((USHORT)0));
        // BS2ListItem * parmlist = new BS2ListItem;
        // cmdlist->add(parmlist);
        // parmlist->add(BS2Item::factory(_TX("PPARM"), new BS2Ascii(""));
        //
        rootlist->add(pplist);

    }
    else
    {   // Invalid PPID
        // Send null list
    }

    // Create reply message.
    return replymsg;
}

//-----------------------------------------------------------------------------
// Process Program Data (S7F5 -> S7F6)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::upUnformatted(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::upUnformatted");

    BS2Message * replymsg = BS2Message::response(msg);  // Create reply message.
    BS2ListItem * rootlist = new BS2ListItem;
    replymsg->add(rootlist);

    BS2Atom * atom = msg->getAtom(_TX("PPID"));
    if (atom == NULL)
    {
        return replymsg;
    }

    JGid ppid = reinterpret_cast<b_id&>(*atom);
    ProcProgBlock * ppblock = m_programs->find(ppid);
    if (ppblock == NULL)
    {   // Invalid PPID : Send null list
        return replymsg;
    }
    else
    {   // Found process program
        BYTE * bufptr;
        size_t size;
        int result = m_programs->get(ppid, bufptr, size);
        if (result < 0)
        {
            return replymsg;
        }
        rootlist->add(BS2Item::factory(_TX("PPID"), BS2Atom::factory(atom)));
        rootlist->add(BS2Item::factory(_TX("PPBODY"), new BS2Binary(bufptr, size)));
        delete[] bufptr;
    }

    return replymsg;
}

//-----------------------------------------------------------------------------
// Formatted Process Program Send (S7F23 -> S7F24)
//-----------------------------------------------------------------------------
class FormattedSendParam : public S7FxParam
{
public:
    FormattedSendParam() : S7FxParam() {}
    virtual ~FormattedSendParam() {}

    string m_mdln;
    string m_softrev;

    // Parse Formatted Process Program Send
    virtual int parseItem(BS2Item * item)
    {
        TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::FormattedSendParam::parseItem");

        BS2Atom * atom = item->atom();
        if (item->name() == _TX("PPID"))
        {
            return parsePPIDAtom(atom);
        }
        else if (item->name() == _TX("CCODE"))
        {
            // Not support
        }
        else if (item->name() == _TX("PPARAM"))
        {
            // Not support
        }
        else if (item->name() == _TX("MDLN"))
        {
            if (! atom->isAscii())
            {
                TRACE_ERROR((_TX("%s is illegal format(%d).\n"),
                            item->charName(), atom->format()));
                return -(ACKC7_INVALID_PPID);
            }
            m_mdln = ((BS2Ascii *)atom)->value();
        }
        else if (item->name() == _TX("SOFTREV"))
        {
            if (! atom->isAscii())
            {
                TRACE_ERROR((_TX("%s is illegal format(%d).\n"),
                            item->charName(), atom->format()));
                return -(ACKC7_INVALID_PPID);
            }
            m_softrev = ((BS2Ascii *)atom)->value();
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::downFormatted(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::downFormatted");

    BS2Message * replymsg;
    BYTE ack = ACKC7_OK;
    FormattedSendParam dwinfo;
    int result = msg->traverse(&dwinfo);
    if (result < 0)
    {
        TRACE_ERROR((_TX("Received illegal message (%s).\n"),
                     msg->charName()));
        ack = -(result);
        replymsg = BS2Message::response(msg, ack, _TX("ACKC7"));
        return replymsg;
    }

    // Put process program
    //
    // Convert PPData to Bianry program
    //
    // m_programs->put(dwinfo.m_ppid, body, size);

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

//-----------------------------------------------------------------------------
// Unformatted Process Program Send (S7F3 -> S7F4)
//-----------------------------------------------------------------------------
class PPSendParam : public S7FxParam
{
public:
    PPSendParam() : S7FxParam() {}
    virtual ~PPSendParam() {}

    BYTE * m_body;
    size_t m_size;

    // Parse Process Program Send
    virtual int parseItem(BS2Item * item)
    {
        TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::PPSendParam::parseItem");

        BS2Atom * atom = item->atom();
        if (item->name() == _TX("PPID"))
        {
            return parsePPIDAtom(atom);
        }
        else if (item->name() == _TX("PPBODY"))
        {
            if (atom->isBinary())
            {
                m_body = ((BS2Binary *)atom)->value();
                m_size = ((BS2Binary *)atom)->size();
            }
            // else if (atom->isAscii())
            // {
            // }
            else
            {
                TRACE_DEBUG((_TX("PPBODY is illegal format.\n"), atom->format()));
                return -1;
            }
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::downUnformatted(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::downUnformatted");

    BS2Message * replymsg;
    BYTE ack = ACKC7_OK;
    PPSendParam dwinfo;
    int result = msg->traverse(&dwinfo);
    if (result < 0)
    {
        TRACE_ERROR((_TX("Received illegal message (%s).\n"),
                    msg->charName()));
        ack = ACKC7_NOT_PERMIT;
        replymsg = BS2Message::response(msg, ack, _TX("ACKC7"));
        return replymsg;
    }

    // Put process program
    m_programs->put(dwinfo.m_ppid, dwinfo.m_body, dwinfo.m_size);

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

//-----------------------------------------------------------------------------
// Formatted Process Program Send (S7F1 -> S7F2)
//-----------------------------------------------------------------------------
class PPInqLoadParam : public S7FxParam
{
public:
    PPInqLoadParam() : S7FxParam() {}
    virtual ~PPInqLoadParam() {}

    size_t m_length;

    // Parse Process Probram Load Inquire
    virtual int parseItem(BS2Item * item)
    {
        TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::PPInqLoadParam::parseItem");

        BS2Atom * atom = item->atom();
        if (item->name() == _TX("PPID"))
        {
            return parsePPIDAtom(atom);
        }
        else if (item->name() == _TX("LENGTH"))
        {
            if (atom->isUInt() || atom->isInt())
            {
                BS2value val(*atom);
                m_length = val.getUInt();
            }
            else
            {
                TRACE_ERROR((_TX("Received illegal format (%s:%s).\n"),
                            item->charName(), atom->format()));
                return -5;
            }
        }
        return 0;
    }
};

//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::inqPPLoad(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::inqPPLoad");

    BS2Message * replymsg;
    BYTE ack = PPGNT_OK;
    PPInqLoadParam inqinfo;
    int result = msg->traverse(&inqinfo);
    if (result < 0)
    {
        ack = (-result & 0xFF);
        replymsg = BS2Message::response(msg, ack, _TX("PPGNT"));
        return replymsg;
    }

    // Check Process Program or Process
    ProcProgBlock * ppblock = m_programs->find(inqinfo.m_ppid);
    if (ppblock != NULL)
    {
        ack = PPGNT_ALREADY;
    }
    else if (m_programs->full())
    {
        ack = PPGNT_NO_SPACE;
    }
    else
    {
        JGProcessManager * proc_mngr = m_equipment->getProcessManager();
        if (proc_mngr->isActive())
        {
            ack = PPGNT_BUSY;
        }
    }

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

//-----------------------------------------------------------------------------
// Get grant code in PPGNT
//-----------------------------------------------------------------------------
int JGProcProgManager::ppgnt(BS2Atom * atom)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::ppgnt");
    int result = -1;
    if (atom != NULL)
    {
        if (atom->isBinary() && atom->size() > 0)
        {
            BYTE * gntptr = ((BS2Binary *)atom)->value();
            result = *gntptr;
        }
    }
    return result;
}

//-----------------------------------------------------------------------------
// Process Program Load Grant (S7F2 -> S7F3)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::loadGrant(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::loadGrant");

    ProcProgTimerHandler * tmh = (ProcProgTimerHandler *)m_watchdog.value();
    if (tmh == NULL)
    {
        TRACE_ERROR((_TX("Already timeout(Process Program Load Grant).\n")));
        return NULL;
    }
    ProcProgBlock * ppblock = m_programs->find(tmh->m_ppid);

    BS2Atom * atom = msg->getAtom(_TX("PPGNT"));
    int grant = this->ppgnt(atom);
    if (grant < 0 || (! (grant == PPGNT_OK || grant == PPGNT_BUSY)) ||
        ppblock == NULL)
    {
        TRACE_ERROR((_TX("Receive illegal message(%s) or lost ppid (%s).\n"),
                     (grant != 0) ? _TX("Yes") : _TX("No"),
                     (ppblock == NULL) ? _TX("Yes") : _TX("No")));
        this->cancelTimer(tmh->m_tmid);
        tmh->m_result = BEE_ERROR;
        m_event.signal();
        return NULL;
    }

    if (grant == PPGNT_BUSY)
    {
        if (m_retryTime == 0)
        {   // Return to application
            this->cancelTimer(tmh->m_tmid);
            tmh->m_result = 1;             // Set retry code (=1)
            m_event.signal();
            return NULL;
        }
        else
        {   // Retry send S7F1
            ACE_OS::sleep(m_retryTime);    // Sleep, not good method.

            tmh->m_curRetry++;
            if (tmh->m_maxRetry == 0 || (tmh->m_maxRetry > tmh->m_curRetry))
            {
                BS2Message * replymsg = BS2Message::factory(SFCODE(7,1));
                BS2ListItem * rootlist = new BS2ListItem;
                replymsg->add(rootlist);
                rootlist->add(BS2Item::factory(_TX("PPID"),
                              BS2Atom::factory((b_value&)tmh->m_ppid)));
                rootlist->add(BS2Item::factory(_TX("LENGTH"),
                              new BS2Int4((int)tmh->m_length)));
                return replymsg;
            }
            else
            {
                TRACE_ERROR((_TX("As host has been busy, can't upload(%s).\n"),
                            tmh->m_ppid.toString().c_str()));
                this->cancelTimer(tmh->m_tmid);
                tmh->m_result = BEE_ERROR;
                m_event.signal();
                return NULL;
            }
        }
    }

    // grant == PPGNT_OK

    BYTE * bufptr;
    size_t size;
    int result = m_programs->get(tmh->m_ppid, bufptr, size);
    if (result < 0)
    {
        TRACE_ERROR((_TX("Read process program error(%s).\n"),
                     tmh->m_ppid.toString().c_str()));
        this->cancelTimer(tmh->m_tmid);
        tmh->m_result = BEE_ERROR;
        m_event.signal();
        return NULL;
    }

    BS2Message * replymsg = BS2Message::factory(SFCODE(7,3));
    BS2ListItem * rootlist = new BS2ListItem;
    replymsg->add(rootlist);
    rootlist->add(BS2Item::factory(_TX("PPID"),
                  BS2Atom::factory((b_value&)tmh->m_ppid)));
    rootlist->add(BS2Item::factory(_TX("PPBODY"), new BS2Binary(bufptr, size)));
    delete[] bufptr;

    return replymsg;
}


//-----------------------------------------------------------------------------
// Process Program Acknowledge (S7F4)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::ppAck(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::loadGrant");

    ProcProgTimerHandler * tmh = (ProcProgTimerHandler *)m_watchdog.value();
    if (tmh == NULL)
    {
        TRACE_ERROR((_TX("Already timeout(Process Program Acknowledge).\n")));
        return NULL;
    }

    BS2Atom * atom = msg->getAtom(_TX("ACKC7"));
    if (atom == NULL)
    {
        TRACE_ERROR((_TX("Receive illegal message(S7F4)\n")));
        tmh->m_result = BEE_ERROR;

    }
    else
    {   // Check ack code, and set return value
        if (atom->isBinary() && (atom->size() > 0))
        {
            BYTE * ackptr = ((BS2Binary *)atom)->value();
            tmh->m_result = (*ackptr == 0) ? BEE_SUCCESS : BEE_ERROR;
        }
        else
        {
            tmh->m_result = BEE_ERROR;
        }
    }

    // Notify caller
    this->cancelTimer(tmh->m_tmid);
    m_event.signal();
    return NULL;
}

//-----------------------------------------------------------------------------
// Process Program Data (S7F6)
//-----------------------------------------------------------------------------
BS2Message * JGProcProgManager::ppData(BS2Message * msg)
{
    TRACE_FUNCTION(TRL_LOW, "JGProcProgManager::ppData");

    ProcProgTimerHandler * tmh = (ProcProgTimerHandler *)m_watchdog.value();
    if (tmh == NULL)
    {
        TRACE_ERROR((_TX("Already timeout(Process Program Data).\n")));
        return NULL;
    }

    PPSendParam dwinfo;      // See Unformatted Process Program Send (S7F3)
    int result = msg->traverse(&dwinfo);
    if (result < 0 || (tmh->m_ppid != dwinfo.m_ppid))
    {
        TRACE_ERROR((_TX("Receive illegal message(%s) or differ ppid.\n"),
                     (result < 0) ? _TX("Yes") : _TX("No"),
                     (tmh->m_ppid != dwinfo.m_ppid) ? _TX("Yes") : _TX("No")));

        // PP Change (send S6F11)
        JGvalue errval((BYTE)DELETED);
        m_changeName->setv(dwinfo.m_ppid);
        m_changeStatus->setv(errval);

        this->cancelTimer(tmh->m_tmid);
        tmh->m_result = BEE_ERROR;
        m_event.signal();
        return NULL;
    }

    ProcProgBlock * ppblock = m_programs->find(tmh->m_ppid);
    JGvalue stval((BYTE)((ppblock != NULL) ? UPDATED : CREATED));  // type is UInt1

    // Put process program
    m_programs->put(tmh->m_ppid, dwinfo.m_body, dwinfo.m_size);

    // PP Change (send S6F11)
    m_changeName->setv(tmh->m_ppid);
    m_changeStatus->setv(stval);

    this->cancelTimer(tmh->m_tmid);
    tmh->m_result = BEE_SUCCESS;
    m_event.signal();
    return NULL;
}

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

    if (msg->sf() == SFCODE(7,1))
    {   // Process Program Inquire Load
        replymsg = this->inqPPLoad(msg);
    }
    else if (msg->sf() == SFCODE(7,2))
    {   // Process Program Load Grant
        replymsg = this->loadGrant(msg);
    }
    else if (msg->sf() == SFCODE(7,3))
    {   // Process Program Send
        replymsg = this->downUnformatted(msg);
    }
    else if (msg->sf() == SFCODE(7,4))
    {   // Process Program Acknowledge
        replymsg = this->ppAck(msg);
    }
    else if (msg->sf() == SFCODE(7,5))
    {   // Process Program Data
        replymsg = this->upUnformatted(msg);
    }
    else if (msg->sf() == SFCODE(7,6))
    {   // Process Program Data
        replymsg = this->ppData(msg);
    }
    else if (msg->sf() == SFCODE(7,17))
    {   // Delete Process Program Send
        replymsg = this->remove(msg);
    }
    else if (msg->sf() == SFCODE(7,19))
    {   // Current EPPD Request
        replymsg = this->eppd(msg);
    }
    else if (msg->sf() == SFCODE(7,25))
    {   // Formatted Process Program Request
        replymsg = this->upFormatted(msg);
    }
    else if (msg->sf() == SFCODE(7,23))
    {   // Formatted Process Program Send
        replymsg = this->downFormatted(msg);
    }
    else if (msg->sf() == SFCODE(7,28))
    {   // Process Program Verification Acknowledge
        replymsg = NULL;                          // Ignore
    }
    else
    {
        replymsg = this->unrecognized(msg);
    }

    return replymsg;
}


//-----------------------------------------------------------------------------
// Dump
//-----------------------------------------------------------------------------
void JGProcProgManager::dump() const
{
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("===== PROCESS PROGRAM =====\n")));
    string ppidStr = m_changeName->getv().toString();
    int    ppStatus = m_changeStatus->getv().getInt();

    ACE_DEBUG((LM_DEBUG,
        ACE_TEXT("ppid = %s, change status = %d\n"), ppidStr.c_str(), ppStatus));

    ProcProgTimerHandler * tmh = (ProcProgTimerHandler *)m_watchdog.value();
    if (tmh != NULL)
    {
        tmh->dump();
    }
    else
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("Nothig request to host.\n")));
    }

    m_programs->dump();
}

