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

//=============================================================================
/**
 *  @file    JGCommDevice.cpp
 *
 *  @author Fukasawa Mitsuo
 *
 *
 *    Copyright (C) 1998-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 "JGEquipment.h"
#include "JGCommDevice.h"
#include "JGTaskTrigger.h"
#include "JGManager.h"
#include "BS2DeclAtoms.h"
#include "BS2SECSReceiver.h"
#include "BS2HSMSReceiver.h"
#include "BS2Sender.h"
#include "BS2Serial.h"
#include "BS2Socket.h"
#include "BS2Driver.h"
#include "BS2TransactionManager.h"
#include "BS2MessageInfo.h"
#include "BS2ErrorMessage.h"
#include "SECSXmlParser.h"

//-----------------------------------------------------------------------------
// Constructor/Destructor
//-----------------------------------------------------------------------------
JGCommDevice::JGCommDevice() : BS2Device()
{
    TRACE_FUNCTION(TRL_CONSTRUCT, "JGCommDevice::JGCommDevice");
}

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

//-----------------------------------------------------------------------------
// Send message. (Caller mast be delete message)
//-----------------------------------------------------------------------------
int JGCommDevice::send(BS2Message * msg, JGManager * mngr)
{
    TRACE_FUNCTION(TRL_LOW, "JGCommDevice::send");

    int   result;
    msg->deviceNum(m_devid);        //  Set device-number in BS2MsgHeader

    // Check communication
    if (! m_equipment->isCommunicating())
    {
        // SPOOL
        if (m_equipment->isSpooling())
        {
            // S1F13,S1F14 and spooled messages are excepted spooling
            if (! (STREAMCODE(msg->sf()) == 1 || msg->spooled()))
            {
                m_equipment->spool(msg);
                return 0;       // 0 mean no reply
            }
        }

        // Can't send message
        if (! (msg->sf() == SFCODE(1,13) || msg->sf() == SFCODE(1,14) ||
               STREAMCODE(msg->sf()) == 9 || (msg->sf() & 0xFF) == 0))
        {   // Can't send message : [E30 3.2.4]
            TRACE_DEBUG((_TX("As not communication, can't send the SECS Message(%s).\n"),
                         msg->charName()));
            return 0;       // 0 mean no reply
        }
    }
    else if (! m_equipment->isOnline())
    {
        if (msg->sf() == SFCODE(6,11))
        {
            BS2Atom * atom = msg->getAtom(_TX("CEID"));
            if (atom != NULL && atom->isAscii())
            {
#if 1
                BS2Ascii * ceidAtom = (BS2Ascii *)atom;
                if (ceidAtom->value() == CEID_OFFLINE)
                {   // Pass message
                }
                else
                {   // Can't send message
                    TRACE_DEBUG((_TX("As not online, can't send the S6F11(%s).\n"),
                                 ceidAtom->value().c_str()));
                    return 0;
                }
#endif
            }
            else
            {   // Can't send message
                TRACE_DEBUG((_TX("As not online and bad CEID, can't send S6F11.\n")));
                return 0;
            }
        }
        else if (! (msg->sf() == SFCODE(1,1) || msg->sf() == SFCODE(1,2) ||
                    msg->sf() == SFCODE(1,15) || msg->sf() == SFCODE(1,16) ||
                    (msg->sf() & 0xFF) == 0))
        {   // Can't send message
            TRACE_DEBUG((_TX("As not online, can't send the SECS Message(%s).\n"),
                         msg->charName()));
            return 0;       // 0 mean no reply
        }
    }

    result = this->BS2Device::send(msg, (void *)mngr);
    return result;
}

//-----------------------------------------------------------------------------
// Communication has connected.
//-----------------------------------------------------------------------------
int JGCommDevice::connected()
{
    TRACE_FUNCTION(TRL_LOW, "JGCommDevice::connected");

    m_equipment->communicating();

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Communication has disconnected.
//-----------------------------------------------------------------------------
int JGCommDevice::disconnected()
{
    TRACE_FUNCTION(TRL_LOW, "JGCommDevice::disconnected");

    m_trmgr->removeAll();
    m_equipment->notCommunicating();

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Dispatch by category name
//-----------------------------------------------------------------------------
int JGCommDevice::gotoManager(BS2MessageInfo& msginfo, const BCHAR * evtName)
{
    TRACE_FUNCTION(TRL_LOW, "JGCommDevice::gotoManager");

    BS2Message * msg = msginfo.message();

    msginfo.setStatus(0);            // return status
    JGManager * mngr = (JGManager *)msginfo.getUserPtr();
    if (mngr == NULL && msg != NULL)
    {
        if (msg->isSecondary())
        {
            TRACE_ERROR((_TX("Manager in the primary(%s) is not found.\n"),
                         msg->charName()));
            return BEE_ERROR;
        }
        // else if (msg->isPrimary() && (! msg->reply()))
        // {
        //     TRACE_ERROR((_TX("This primary message(%s) is off wait-bit, maybe spooled message.\n"),
        //                 msg->charName()));
        // }
        mngr = m_equipment->findManager(msg->manager());
        if (mngr == NULL)
        {
            TRACE_ERROR((_TX("Manager(%s) not found\n"), msg->manager().c_str()));
            msginfo.setStatus(-1);    // return request s9f3
            return BEE_ERROR;
        }
    }

    // Notify Event to Manager.
    if (evtName != NULL)
    {   // Error: clear message pointer
        if ((msg = msginfo.getMessageAndClear()) != NULL)
        {
            delete msg;
        }
        if (mngr->notify(evtName) == BEE_ERROR)
        {
           TRACE_ERROR((_TX("(%t) %p\n"), _TX("illegal manager send error.")));
           return BEE_ERROR;
        }
    }
    else
    {
        if (mngr->notify(msginfo) == BEE_ERROR)
        {
           TRACE_ERROR((_TX("(%t) %p\n"), _TX("manager send error.")));
           return BEE_ERROR;
        }
    }

    msginfo.setMessage(NULL);   // clear message pointer

    return BEE_SUCCESS;
}

//-----------------------------------------------------------------------------
// Receive message thread.
//-----------------------------------------------------------------------------
int JGCommDevice::svc(void)
{
    int result = 0;
    BS2MessageInfo msginfo;
    ACE_Time_Value mb_tv(1);

    for (;;)
    {
        ACE_Message_Block *mb;

        // delay(1 sec) for task switching
        result = this->getq(mb, &mb_tv);
        if (result == -1)
        {   // time out

            //### if ((result = this->receiveWithLimit(msginfo, &msg_tv)) >= 0)
            if ((result = this->receive(msginfo)) >= 0)
            {
                BS2Message * msg = msginfo.message();
                if (msginfo.getResult() == BS2RET_NORMAL && msg != NULL)
                {
                    // msg->dump();    // for DEBUG
                    if (m_equipment->isCommunicating())
                    {
                        // Check grant of multi-block
                        if (msg->isGrant())
                        {
                            BS2Message * orgmsg = this->removeInquiry(msg);
                            if (orgmsg != NULL)
                            {
                                msginfo.remove();
                                if (msg->getGrant() == 0)
                                {
                                    this->send_s(orgmsg, msginfo.getUserPtr());
                                    delete msg;    // delete grant message
                                    continue ;     // goto wait
                                }
                                else
                                {
                                    delete msg;    // delete grant message
                                    BS2Message * fakemsg = BS2Message::response(orgmsg);
                                    //
                                    // Set nack
                                    //
                                    msginfo.setMessage(fakemsg);
                                    msg = fakemsg; // set fake response
                                }
                            }
                        }

                        if (m_equipment->isOnline() ||
                            msg->sf() == SFCODE(1,2) || msg->sf() == SFCODE(1,17))
                        {   // Notify message to the each manager
                            if (this->gotoManager(msginfo) == BEE_ERROR)
                            {   // clear message pointer
                                msg = msginfo.getMessageAndClear();
                                if (msginfo.getStatus() < 0)
                                {   // Not support message
                                    BS2Message * s9f3 = new BS2S9F3Message(msg);
                                    this->BS2Device::send(s9f3);
                                }
                                delete msg;
                            }
                        }
                        else
                        {   // E30 : 3.3 Control State model (OFFLINE)
                            ACE_DEBUG((LM_DEBUG,
                                  ACE_TEXT("JGCommDevice: Control model is OFFLINE (%s).\n"),
                                  msg->charName()));

                            msg = msginfo.getMessageAndClear();
                            int bad_sf = msg->sf();
                            UINT transaction = msg->transNum();
                            delete msg;

                            if (bad_sf & 1)
                            {   // Send abort message
                                int abort_sf = (bad_sf & ~0xFF);
                                BS2Message * abort_msg = BS2Message::factory(abort_sf);
                                abort_msg->transNum(transaction);
                                this->BS2Device::send(abort_msg);
                            }
                        }
                    }
                    else
                    {   // E30 : 3.2.4 Communication State  (Table-3.2 #8)
                        if (msg->sf() == SFCODE(1,13) || msg->sf() == SFCODE(1,14))
                        {
                            if (this->gotoManager(msginfo) == BEE_ERROR)
                            {   // clear message pointer
                                msg = msginfo.getMessageAndClear();
                                delete msg;
                            }
                        }
                        else
                        {   // ignore receipt message
                            ACE_DEBUG((LM_DEBUG,
                                  ACE_TEXT("JGCommDevice: Not communicating! (%s).\n"),
                                  msg->charName()));

                            msg = msginfo.getMessageAndClear();
                            UINT transaction = msg->transNum();
                            delete msg;

                            BS2Message * abort_msg = BS2Message::factory(SFCODE(1,0));
                            abort_msg->transNum(transaction);
                            this->BS2Device::send(abort_msg);
                        }
                    }
                }
                else
                {   // Line Error
                    ACE_DEBUG((LM_DEBUG, "Timeout ! Is communication disconnect(%d) ?\n",
                              msginfo.getResult()));
#if 0  // #### BUG ####
                    //
                    // see disconnect()
                    //
                    if (m_equipment->isCommunicating())
                    {
                        if (m_driver->canSend())
                        {   // Stop communications
                            m_equipment->notCommunicationg();
                        }
                    }
#endif

                    // Dispatch by error code
                    BCHAR * evtName;
                    switch (msginfo.getResult())
                    {
                    case BS2RET_ERROR:
                        evtName = MSGEVT_ERROR;
                        break;
                    case BS2RET_ABORT:
                        evtName = MSGEVT_ABORT;
                        break;
                    case BS2RET_TIMEOUT:
                        evtName = MSGEVT_TIMEOUT;
                        break;
                    case BS2RET_TIMEOUT_MESSAGE:
                        evtName = MSGEVT_ERROR;
                        break;
                    default:    // BS2RET_ILLEGAL_MESSAGE
                        evtName = MSGEVT_ERROR;
                        break;
                    }
                    if (this->gotoManager(msginfo, evtName) == BEE_ERROR)
                    {
                        // clear message pointer
                    }
                }
            }
            else
            {
                ACE_ERROR((LM_ERROR, "Received error.\n"));
            }
        }
        else
        {
            int length = mb->length();
            if (length > 0)
            {
                // BCHAR * top = mb->rd_ptr();
                ACE_ERROR((LM_ERROR,
                    ACE_TEXT("JGCommDevice: Received unexpected message.\n")));
            }
            mb->release ();

            if (length == 0)             // shutdown
                break;
        }
    }

    ACE_DEBUG((LM_DEBUG, "(%t) %s \"device %d\" dispatcher is deleted.\n",
                         this->getName(), m_devid));
    return 0;
}

