// $Id: PLCDeviceManager.cpp,v 1.5 2003/03/22 16:41:44 fukasawa Exp $

//=============================================================================
/**
 *  @file    PLCDeviceManager.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

#ifdef _MSC_VER
#pragma warning(disable: 4786)  // too long identifier
#endif

#include "PLCDeviceManager.h"
#include "PLCDevice.h"
#include "ace/Log_Msg.h"

struct int_less
{
    bool operator() (int x, int y) const { return (x < y); }
};

struct str_less
{
   bool operator() (const string& x, const string& y) const { return (x < y); }
};

typedef map<int, PLCDevice *, int_less, allocator<PLCDevice *> >    PLCCodeMap;
typedef map<string, PLCDevice *, str_less, allocator<PLCDevice *> > PLCStringMap;
typedef PLCCodeMap::value_type    PLCCodePair;
typedef PLCStringMap::value_type  PLCNamePair;

//------------------------------------------------------------------------------
//
// Implementate
//
//------------------------------------------------------------------------------
static PLCCodeMap    _mapByCode;
static PLCStringMap  _mapByName;
static PLCDeviceManager * _deviceManager = NULL;

//------------------------------------------------------------------------------
// constructor/destoructor
//------------------------------------------------------------------------------
PLCDeviceManager::PLCDeviceManager(ACE_TCHAR * filename)
{
    if (filename == NULL)
    {
        m_filename = "";
    }
    else
    {
        m_filename = filename;
    }

    // Initialize Devices
    for (int i = 0; i < MAX_IODEVICE; i++)
    {
        m_iodev.push_back(NULL);
    }
    m_usage = 0;
}

//-----------------------------------------------------------------------------
PLCDeviceManager::~PLCDeviceManager()
{
    PLCDevice * memptr;
    for (int i = 0; i < MAX_IODEVICE; i++)
    {
        memptr = m_iodev.at(i);
        if (memptr != NULL)
        {
            delete memptr;
        }
    }
}

//------------------------------------------------------------------------------
// Instance Device Manager Object.
//------------------------------------------------------------------------------
PLCDeviceManager * PLCDeviceManager::instance()
{
    if (_deviceManager == NULL)
    {   // Create Device Manager
        _deviceManager = new PLCDeviceManager();
    }
    return _deviceManager;
}

//------------------------------------------------------------------------------
PLCDeviceManager * PLCDeviceManager::instance(PLCDeviceManager * child)
{
    if (_deviceManager != NULL) {
        ACE_ERROR((LM_ERROR,
                   ACE_TEXT("PLCDeviceManager::instance: manager is exist.\n")));
        delete _deviceManager;
    }

    // Initialize Device Manager
    _deviceManager = child;

    return _deviceManager;
}

//-----------------------------------------------------------------------------
// Initialize Device Manager Object.
//-----------------------------------------------------------------------------
int PLCDeviceManager::init(const ACE_TCHAR * pname, const ACE_TCHAR * xmlname,
                           int chan, int stnum, int unit)
{
    ACE_UNUSED_ARG(xmlname);
    m_channel = chan;                  // Channel: 12 = Q-bus
    m_stationNum = stnum;
    m_unit = unit;                     // Unit: 1 = PC Unit

    m_basedir = (pname) ? pname : ACE_TEXT(".");
    m_filename = m_basedir;            // mmap file in path
#ifdef WIN32
    m_filename += "\\vmplc.mel";
#else
    m_filename += "/vmplc.mel";
#endif

    return 0;
}

//------------------------------------------------------------------------------
// Add Memory Device Object.
//------------------------------------------------------------------------------
int PLCDeviceManager::add(PLCDevice * mem)
{
    if (m_usage >= MAX_IODEVICE)
    {
        return -1;
    }
    m_iodev[m_usage] = mem;
    m_usage++;

    _mapByCode.insert(PLCCodePair(mem->devCode(), mem));
    _mapByName.insert(PLCNamePair(mem->name(), mem));

    return 0;
}

//------------------------------------------------------------------------------
// Search Memory Device Object by name.
//------------------------------------------------------------------------------
PLCDevice * PLCDeviceManager::get(const ACE_TCHAR * devname) const
{
    string name(devname);
    PLCStringMap::const_iterator iter = _mapByName.find(name);
    if (iter == _mapByName.end())
    {
        return NULL;
    }
    PLCDevice * mem = (*iter).second;
    return mem;
}

//------------------------------------------------------------------------------
// Search Memory Device Object by code.
//------------------------------------------------------------------------------
PLCDevice * PLCDeviceManager::get(int devcd) const
{
    PLCCodeMap::const_iterator iter = _mapByCode.find(devcd);
    if (iter == _mapByCode.end())
    {
        return NULL;
    }
    PLCDevice * mem = (*iter).second;
    return mem;
}

//------------------------------------------------------------------------------
// Read data.
//------------------------------------------------------------------------------
int PLCDeviceManager::read(PLCAddress& address, size_t size,
                           u_short * data) const
{
    PLCCodeMap::const_iterator iter = _mapByCode.find(address.type());
    if (iter == _mapByCode.end())
    {
        return -1;
    }
    PLCDevice * mem = (*iter).second;
    return mem->read(address.addr(), size, data);
}

//------------------------------------------------------------------------------
// Write data.
//------------------------------------------------------------------------------
int PLCDeviceManager::write(PLCAddress& address, size_t size,
                            u_short * data)
{
    PLCCodeMap::const_iterator iter = _mapByCode.find(address.type());
    if (iter == _mapByCode.end())
    {
        return -1;
    }

    PLCDevice * mem = (*iter).second;
    return mem->write(address.addr(), size, data);
}

//------------------------------------------------------------------------------
// Get data.
//------------------------------------------------------------------------------
u_short PLCDeviceManager::get(PLCAddress& address) const
{
    PLCCodeMap::const_iterator iter = _mapByCode.find(address.type());
    if (iter == _mapByCode.end())
    {
        return 0;
    }

    PLCDevice * mem = (*iter).second;
    return mem->get(address.addr());
}

//------------------------------------------------------------------------------
// Put data.
//------------------------------------------------------------------------------
int PLCDeviceManager::put(PLCAddress& address, u_short data)
{
    PLCCodeMap::const_iterator iter = _mapByCode.find(address.type());
    if (iter == _mapByCode.end())
    {
        return -1;
    }
    PLCDevice * mem = (*iter).second;
    return mem->put(address.addr(), data);
}

//------------------------------------------------------------------------------
// Parse device address to convert string to device code and offset.
//------------------------------------------------------------------------------
int PLCDeviceManager::parseAddress(const string& , PLCAccess& ) const
{
    ACE_ERROR((LM_ERROR,
            ACE_TEXT("PLCDeviceManager::parseAddress: Not implement.\n")));
    return -1;
}

//-----------------------------------------------------------------------------
// Set clock to plc (Dummy Method)
//-----------------------------------------------------------------------------
int PLCDeviceManager::setClock(struct tm& )
{
    return 0;
}

//-----------------------------------------------------------------------------
// Get clock from plc (Dummy Method)
//-----------------------------------------------------------------------------
int PLCDeviceManager::getClock(struct tm& )
{
    return 0;
}

//------------------------------------------------------------------------------
// Get PLC error message test.
//------------------------------------------------------------------------------
const ACE_TCHAR * PLCDeviceManager::errmsg(int ) const
{
    return ACE_TEXT("PLCDeviceManager::errmsg : NO MESSAGE.");
}

//------------------------------------------------------------------------------
// Dump this object.
//------------------------------------------------------------------------------
void PLCDeviceManager::dump() const
{
    size_t max_size = m_iodev.size();
    ACE_DEBUG((LM_DEBUG, ACE_BEGIN_DUMP, this));
    ACE_DEBUG((LM_DEBUG, ACE_TEXT("\n*** PLC Device Manager [%d/%d] ***\n"),
                         m_usage, max_size));
    vector<PLCDevice *>::const_iterator iter = m_iodev.begin();
    for (size_t pos = 0; pos < max_size; pos++)
    {
        PLCDevice * mem = *iter;
        if (mem != NULL)
        {
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("    %s\n"), mem->name().c_str()));
        }
    }
    ACE_DEBUG((LM_DEBUG, ACE_END_DUMP));
    return ;
}

