// $Id: MELMemory.cpp,v 1.4 2003/03/19 16:49:24 fukasawa Exp $

//=============================================================================
/**
 *  @file    MELMemory.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 "MELDeviceManager.h"
#include "MELMemory.h"
#if !defined(PLC_SHARED_MEMORY)
#include "Mdfunc.h"
#endif
#include "ace/Mem_Map.h"
#include "ace/Log_Msg.h"

// #define DEBUG_ZR

//------------------------------------------------------------------------------
//
// Word Memory of MELSEC
//
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Read data from virtual memory
//------------------------------------------------------------------------------
int MELWordMemory::read(long address, size_t size, u_short * data) const
{
    // Check end address
    size_t getsize = (size == (size_t)ACTION_END) ? m_areaSize : size ;
    if ((address + getsize) > m_areaSize)
    {
        getsize = m_areaSize - address;
    }

    // Copy virtual memory to user's buffer
#if defined(PLC_SHARED_MEMORY)
    u_short * top = (u_short *)m_mmap.addr();
    for (size_t pos = 0; pos < getsize; pos++)
    {
        *(data + pos) = *(top + address + pos);
    }
    return getsize;
#else
    // read from melsec
    short retsize = getsize * sizeof(short);
    int result = mdReceive(m_path, m_stnum, m_devCode, (short)address,
                           &retsize, data);
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC WORD READ ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
   
    return retsize;
#endif
}

//------------------------------------------------------------------------------
// Write data to PLC
//------------------------------------------------------------------------------
int MELWordMemory::write(long address, size_t size, u_short * data)
{
    // Check end address
    size_t setsize = (size == (size_t)ACTION_END) ? m_areaSize : size;
    if ((address + setsize) >= m_areaSize)
    {
        setsize = m_areaSize - address;
    }

    // Copy user's buffer to virtual memory
#if defined(PLC_SHARED_MEMORY)
    u_short * top = (u_short *)m_mmap.addr();
    for (size_t pos = 0; pos < setsize; pos++)
    {
         *(top + address + pos) = *(data + pos);
    }
    m_mmap.sync();
#else
    // write to melsec
    short outsize = setsize * sizeof(short);
    int result = mdSend(m_path, m_stnum, m_devCode, (short)address, &outsize,
                        data);
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC WORD WRITE ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
#endif
    return setsize;
}

//------------------------------------------------------------------------------
// Get a datum from virtual memory
//------------------------------------------------------------------------------
u_short MELWordMemory::get(long address) const
{
    if ((u_long)address >= m_areaSize)
    {
        throw runtime_error("MELWordMemory::get : bad device address");
    }

#if defined(PLC_SHARED_MEMORY)
    u_short * top = (u_short *)m_mmap.addr();
    return *(top + address);
#else
    // read from melsec
    short dev[4];
    short buff[8];
    dev[0] = 1;
    dev[1] = m_devCode;
    dev[2] = (short)address;
    dev[3] = 1;
    int result = mdRandR(m_path, m_stnum, dev, buff, sizeof(buff));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC WORD GET ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
    }
    return buff[0];
#endif
}

//------------------------------------------------------------------------------
// Put a datum to virtual memory
//------------------------------------------------------------------------------
int MELWordMemory::put(long address, u_short datum)
{
    if ((unsigned long)address >= m_areaSize)
    {
        throw runtime_error("MELWordMemory::put : bad device address");
    }

#if defined(PLC_SHARED_MEMORY)
    u_short * top = (u_short *)m_mmap.addr();
    *(top + address) = datum;
    m_mmap.sync();
#else
    // write to melsec
    short dev[4];
    short buff[8];
    dev[0] = 1;
    dev[1] = m_devCode;
    dev[2] = (short)address;
    dev[3] = 1;
    buff[0] = datum;
    int result = mdRandW(m_path, m_stnum, dev, buff, sizeof(buff));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC WORD PUT ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
#endif
    return 0;
}

//------------------------------------------------------------------------------
//
// Bit Memory
//
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Read data from virtual memory
//------------------------------------------------------------------------------
int MELBitMemory::read(long address, size_t size, u_short * data) const
{
    // Check end address
    size_t getsize = (size == (size_t)ACTION_END) ? m_areaSize : size ;
    if (((unsigned long)address + getsize) > m_areaSize)
    {
        getsize = m_areaSize - address;
    }

    // Copy virtual memory to user's buffer
#if defined(PLC_SHARED_MEMORY)
    unsigned char * top = (unsigned char *)m_mmap.addr();
    bool   is_bound = true;
    size_t offset = 0;
    u_short bits = 0;
    for (unsigned int pos = 0; pos < getsize; pos++)
    {
        if ((pos != 0) && (pos % BITS_OF_SHORT == 0))
        {
            *(data + offset) = bits;
            offset++;
            bits = 0;
            is_bound = true;
        }
        else
        {
            is_bound = false;
        }
        unsigned long reladr = ((address + pos) > 0) ? (address + pos) / 8 : 0;
        unsigned long bnum = ((address + pos) > 0) ? (address + pos) % 8 : 0;
        unsigned char bt = *(top + reladr);
        bool bit = ((bt & PLCBitMemory::BitPattern[bnum]) != 0);
        int bitnum = pos & MASK_OF_SHORT;
        if (bit)
        {
            bits |= PLCBitMemory::BitPattern[bitnum];
        }
    }
    if (! is_bound)
    {   // set data to memory
        *(data + offset) = bits;
    }
#else
    // read from melsec 
    short dev[4];
    dev[0] = 1;
    dev[1] = m_devCode;
    dev[2] = (short)address;
    dev[3] = getsize;
    int result = mdRandR(m_path, m_stnum, dev, data,
                         ((getsize + 15) / 16) * sizeof(short));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC BIT READ ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
#endif

    return getsize;
}

//------------------------------------------------------------------------------
// Write data to PLC
//------------------------------------------------------------------------------
int MELBitMemory::write(long address, size_t size, u_short * data)
{
    // Check end address
    size_t setsize = (size == (size_t)ACTION_END) ? m_areaSize : size;
    if ((address + setsize) >= m_areaSize)
    {
        setsize = m_areaSize - address;
    }

#if defined(PLC_SHARED_MEMORY)
    size_t wordSize = (setsize + BITS_OF_SHORT) % BITS_OF_SHORT;
    unsigned char * top = (unsigned char *)m_mmap.addr();
    // Copy user's buffer to virtual memory
    for (u_int pos = 0; pos < setsize; pos++)
    {
        size_t offset = (pos == 0) ? 0 : (pos / BITS_OF_SHORT);
        size_t bitnum = pos & MASK_OF_SHORT;
        u_short bits = *(data + offset);
        unsigned long boffset = ((address + pos) > 0) ? (address + pos) / 8 : 0;
        unsigned long bnum = ((address + pos) > 0) ? (address + pos) % 8 : 0;
        unsigned char bt = *(top + boffset);
        bool bit = (bits & PLCBitMemory::BitPattern[bitnum]) ? true : false;
        if (bit)
        {
            bt |= PLCBitMemory::BitPattern[bnum];
        }
        else
        {
            bt &= ~PLCBitMemory::BitPattern[bnum];
        }
        *(top + boffset) = bt;
    }
    m_mmap.sync();
#else
    // write to melsec
    short dev[4];
    dev[0] = 1;
    dev[1] = m_devCode;
    dev[2] = (short)address;
    dev[3] = setsize;
    int result = mdRandW(m_path, m_stnum, dev, data,
                         ((setsize + 15) / 16) * sizeof(short));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC BIT WRITE ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
#endif
    return setsize;
}

//------------------------------------------------------------------------------
// Get a datum from virtual memory
//------------------------------------------------------------------------------
u_short MELBitMemory::get(long address) const
{
    if ((u_long)address >= m_areaSize)
    {
        throw runtime_error("MELBitMemory::get : bad device address");
    }

#if defined(PLC_SHARED_MEMORY)
    unsigned char * top = (unsigned char *)m_mmap.addr();
    unsigned long offset = (address > 0) ? address / 8 : 0;
    unsigned long bnum = (address > 0) ? address % 8 : 0;
    unsigned char bt = *(top + offset);
    return ((bt & PLCBitMemory::BitPattern[bnum]) != 0) ? 1 : 0;
#else
    // read from melsec
    short data[4];
    short buff[8];
    data[0] = 1;
    data[1] = m_devCode;
    data[2] = (short)address;
    data[3] = 1;
    int result = mdRandR(m_path, m_stnum, data, buff, sizeof(buff));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC BIT GET ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
    }
    return buff[0];  
#endif
}

//------------------------------------------------------------------------------
// Put a datum to virtual memory
//------------------------------------------------------------------------------
int MELBitMemory::put(long address, u_short datum)
{
    if ((unsigned long)address >= m_areaSize)
    {
        throw runtime_error("MELBitMemory::put : bad device address");
    }

#if defined(PLC_SHARED_MEMORY)
    unsigned char * top = (unsigned char *)m_mmap.addr();
    unsigned long offset = (address > 0) ? address / 8 : 0;
    unsigned long bnum = (address > 0) ? address % 8 : 0;
    unsigned char bt = *(top + offset);
    if (datum)
    {
        bt |= PLCBitMemory::BitPattern[bnum];
    }
    else
    {
        bt &= ~PLCBitMemory::BitPattern[bnum];
    }
    *(top + offset) = bt;
    m_mmap.sync();
#else
    // write to melsec
    int result;
    if (datum)
    {
        result = mdDevSet(m_path, m_stnum, m_devCode, (short)address);
    }
    else
    {
        result = mdDevRst(m_path, m_stnum, m_devCode, (short)address);
    }

    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC BIT PUT ERROR (%s:%d): %s.\n"),
                             name().c_str(), address,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
#endif
    return 0;
}

//------------------------------------------------------------------------------
//
// ZR Memory
//
//------------------------------------------------------------------------------
#ifndef MIN
#define MIN(a, b)              ((a) <= (b) ? (a) : (b))
#endif
const int ZR_BLOCK_SIZE = 0x8000;
const int ZR_BLOCK_MASK = 0x7FFF;
const int ZR_RW_REC = 3;

static struct {
    long   m_a;
    size_t m_l;
} _rwrec[ZR_RW_REC] = { {0, 0x3000}, {0x3000, 0x3000}, {0x6000, 0x2000} };

struct BlockRWInfo                    // Current read/write block
{
    int m_devtype;
    size_t m_size;
    size_t m_total;
    size_t m_iosz;
    int m_inblk;
    unsigned int m_offset;

    BlockRWInfo(long address, size_t size) : m_size(size), m_total(0) {
            m_offset = address % ZR_BLOCK_SIZE;
            m_devtype = ((address / ZR_BLOCK_SIZE) == 0) ? (int)MELDEV_R :
                                ((int)MELDEV_ER - 1) + (address / ZR_BLOCK_SIZE);
            // int sz_inblk = MIN((ZR_BLOCK_SIZE - m_offset), size);
            if (_rwrec[1].m_a > (long)m_offset) {
                m_inblk = 0;
            } else if (_rwrec[2].m_a > (long)m_offset) {
                m_inblk = 1;
            } else {
                m_inblk = 2;
            }
            u_int sz1 = _rwrec[m_inblk].m_l - ((long)m_offset - _rwrec[m_inblk].m_a);
            m_iosz = MIN(sz1, size);
        }
    void next() {
            m_inblk++;
            if (m_inblk >= ZR_RW_REC) {
                m_inblk = 0;             // Increment block
                m_devtype = (m_devtype == MELDEV_R) ? MELDEV_ER : m_devtype + 1;
            }
            m_offset = _rwrec[m_inblk].m_a;
            m_total += m_iosz;
            m_iosz = MIN(_rwrec[m_inblk].m_l, (m_size - m_total));
        }
    bool end() { return (m_size <= m_total); }
};

//------------------------------------------------------------------------------
// Open data from virtual memory
//------------------------------------------------------------------------------
int MEL_ZR_Memory::open(long bus, long stnum, long address, long offset)
{
    m_path = bus;
    m_stnum = stnum;

#if defined(PLC_SHARED_MEMORY)
    size_t size = ZR_BLOCK_SIZE;
    string dname = "R";
    int result = mapOpen(m_mmap, dname, size, address, offset);
    if (result < 0)
    {
        ACE_ERROR((LM_ERROR,
            ACE_TEXT("MEL_ZR_Memory::open: %s.\n"), dname.c_str()));
        return result;
    }

    ACE_Mem_Map * mmap_er;
    int maxBlock = (m_areaSize + ZR_BLOCK_SIZE) / ZR_BLOCK_SIZE;
    for (int i = 1; i < maxBlock; i++)
    {
        char buf[128];
        sprintf(buf, "ER%d", i);    // Block-0 is DevR
        dname = buf;
        mmap_er = new ACE_Mem_Map;
        result = mapOpen(*mmap_er, dname, size, 0, 0);
        if (result < 0)
        {
            ACE_ERROR((LM_ERROR,
                ACE_TEXT("MEL_ZR_Memory::open: %s.\n"), dname.c_str()));
            this->close();
            delete mmap_er;
            return result;
        }
        m_mmaps.push_back(mmap_er);
    }

#endif
    return 0;
}

//------------------------------------------------------------------------------
// Close data map memory
//------------------------------------------------------------------------------
int MEL_ZR_Memory::close() 
{
    this->PLCMemory::close();
#if defined(PLC_SHARED_MEMORY)
    for (u_int i = 0; i < m_mmaps.size(); i++)
    {
        ACE_Mem_Map * mmap = m_mmaps[i];
        if (mmap != NULL)
        {
            mmap->sync();
            mmap->close();
            delete mmap;
        }
    }
#endif
    return 0;
}

//------------------------------------------------------------------------------
// Read data from virtual memory
//------------------------------------------------------------------------------
int MEL_ZR_Memory::read(long address, size_t size, u_short * data) const
{
    // Check end address
    size_t getsize = (size == (size_t)ACTION_END) ? m_areaSize : size ;
    if ((address + getsize) > m_areaSize)
    {
        getsize = m_areaSize - address;
    }

    BlockRWInfo curblk(address, getsize);     // Initial block parameters

    // Read from melsec
#ifdef DEBUG_ZR    // Print read argument
    ACE_DEBUG((LM_DEBUG,
        ACE_TEXT("mdReceive(0x%x, %d, %d, 0x%x, %d, 0x%x);\n"), 
        m_path, m_stnum, curblk.m_devtype, (u_short)curblk.m_offset,
        curblk.m_iosz * sizeof(short), (data + curblk.m_total)));
#endif
    // Read first block
    u_short retsize = curblk.m_iosz * sizeof(short);
    int result = this->receive(curblk.m_devtype, (u_short)curblk.m_offset,
                         &retsize, (data + curblk.m_total));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC ZR READ ERROR (%d:%d): %s.\n"), 
                             curblk.m_devtype, curblk.m_offset,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
    // Read next block
    curblk.next();
    while (! curblk.end())
    {
#ifdef DEBUG_ZR    // Print read argument
        ACE_DEBUG((LM_DEBUG,
            ACE_TEXT("mdReceive(0x%x, %d, %d, 0x%x, %d, 0x%x);\n"), 
            m_path, m_stnum, curblk.m_devtype, (u_short)curblk.m_offset,
            curblk.m_iosz * sizeof(short), (data + curblk.m_total)));
#endif
        retsize = curblk.m_iosz * sizeof(short);
        result = this->receive(curblk.m_devtype, (u_short)curblk.m_offset,
                         &retsize, (data + curblk.m_total));
        if (result != MEL_NORMAL) 
        {
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC ZR READ ERROR (%d:%d): %s.\n"), 
                                 curblk.m_devtype, curblk.m_offset,
                                 MELDeviceManager::instance()->errmsg(result)));
            return -1;
        }
        curblk.next();
    }

    return getsize;
}

//------------------------------------------------------------------------------
// Write data to PLC
//------------------------------------------------------------------------------
int MEL_ZR_Memory::write(long address, size_t size, u_short * data)
{
    // Check end address
    size_t setsize = (size == (size_t)ACTION_END) ? m_areaSize : size;
    if ((address + setsize) >= m_areaSize)
    {
        setsize = m_areaSize - address;
    }

    BlockRWInfo curblk(address, setsize);     // Initial block parameters

    // Write to melsec
#ifdef DEBUG_ZR    // Print write argument
    ACE_DEBUG((LM_DEBUG,
        ACE_TEXT("mdSend(0x%x, %d, %d, 0x%x, %d, 0x%x);\n"), 
        m_path, m_stnum, curblk.m_devtype, (u_short)curblk.m_offset,
        curblk.m_iosz * sizeof(short), (data + curblk.m_total)));
#endif
    u_short outsize = curblk.m_iosz * sizeof(short);
    int result = send(curblk.m_devtype, (u_short)curblk.m_offset,
                      &outsize, (data + curblk.m_total));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC ZR WRITE ERROR (%d:%d): %s.\n"),
                             curblk.m_devtype, curblk.m_offset,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
    // Write next block
    curblk.next();
    while (! curblk.end())
    {
#ifdef DEBUG_ZR    // Print write argument
        ACE_DEBUG((LM_DEBUG,
            ACE_TEXT("mdSend(0x%x, %d, %d, 0x%x, %d, 0x%x);\n"), 
            m_path, m_stnum, curblk.m_devtype, (u_short)curblk.m_offset,
            curblk.m_iosz * sizeof(short), (data + curblk.m_total)));
#endif
        outsize = curblk.m_iosz * sizeof(short);
        result = send(curblk.m_devtype, (u_short)curblk.m_offset,
                      &outsize, (data + curblk.m_total));
        if (result != MEL_NORMAL) 
        {
            ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC ZR WRITE ERROR (%d:%d): %s.\n"),
                                 curblk.m_devtype, curblk.m_offset,
                                 MELDeviceManager::instance()->errmsg(result)));
            return -1;
        }
        curblk.next();
    }

    return setsize;
}

//------------------------------------------------------------------------------
// Get a datum from virtual memory
//------------------------------------------------------------------------------
u_short MEL_ZR_Memory::get(long address) const
{
    if ((u_long)address >= m_areaSize)
    {
        throw runtime_error("MELWordMemory::get : bad device address");
    }

    BlockRWInfo curblk(address, 1);     // Initial block parameters

#ifdef DEBUG_ZR    // Print get argument
    ACE_DEBUG((LM_DEBUG,
        ACE_TEXT("mdRandR(0x%x, %d, (%d, 0x%x));\n"), 
        m_path, m_stnum, curblk.m_devtype, (u_short)curblk.m_offset));
#endif
    // Read from melsec
    u_short dev[4];
    u_short buff[8];
    dev[0] = 1;
    dev[1] = curblk.m_devtype;
    dev[2] = (u_short)curblk.m_offset;
    dev[3] = 1;
    int result = this->randRead((short *)dev, buff, sizeof(buff));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC ZR GET ERROR (%d:%d): %s.\n"),
                             curblk.m_devtype, curblk.m_offset,
                             MELDeviceManager::instance()->errmsg(result)));
    }
    return buff[0];
}

//------------------------------------------------------------------------------
// Put a datum to virtual memory
//------------------------------------------------------------------------------
int MEL_ZR_Memory::put(long address, u_short datum)
{
    if ((unsigned long)address >= m_areaSize)
    {
        throw runtime_error("MELWordMemory::put : bad device address");
    }

    BlockRWInfo curblk(address, 1);     // Initial block parameters

#ifdef DEBUG_ZR    // Print put argument
    ACE_DEBUG((LM_DEBUG,
              ACE_TEXT("mdRandW(0x%x, %d, (%d, 0x%x), 0x%x);\n"), 
              m_path, m_stnum, curblk.m_devtype, (u_short)curblk.m_offset,
              datum));
#endif
    // Write to melsec
    u_short dev[4];
    u_short buff[8];
    dev[0] = 1;
    dev[1] = curblk.m_devtype;
    dev[2] = (u_short)curblk.m_offset;
    dev[3] = 1;
    buff[0] = datum;
    int result = randWrite((short *)dev, buff, sizeof(buff));
    if (result != MEL_NORMAL) 
    {
        ACE_DEBUG((LM_DEBUG, ACE_TEXT("MELSEC ZR PUT ERROR (%d:%d): %s.\n"),
                             curblk.m_devtype, curblk.m_offset,
                             MELDeviceManager::instance()->errmsg(result)));
        return -1;
    }
    return 0;
}

//------------------------------------------------------------------------------
// Send data to bus driver
//------------------------------------------------------------------------------
int MEL_ZR_Memory::send(short devtype, u_short addr,
                        u_short * retsize, u_short * data)
{
    int result = MEL_NORMAL;
#if defined(PLC_SHARED_MEMORY)
    ACE_Mem_Map * mmptr;
    if (devtype == MELDEV_R)
         mmptr = &m_mmap;
    else 
    {
         u_int pos = devtype - MELDEV_ER;
         if (pos >= m_mmaps.size())
         {
            ACE_ERROR((LM_ERROR, ACE_TEXT("MEL_ZR_Memory::send: %d.\n"),
                                 devtype));
            return -3;
         }
         mmptr = m_mmaps[pos];
    }

    size_t setsize = *retsize / sizeof(short);
    if (((u_short)addr + setsize) > (size_t)ZR_BLOCK_SIZE)
    {
        setsize = ZR_BLOCK_SIZE - addr;
    }

    u_short * top = (u_short *)mmptr->addr();
    for (size_t pos = 0; pos < setsize; pos++)
    {
         *(top + addr + pos) = *(data + pos);
    }
    mmptr->sync();
    *retsize = setsize * sizeof(short);
#else
    result = mdSend(m_path, m_stnum, devtype, addr, (short *)retsize, data);
#endif
    return result;
}

//------------------------------------------------------------------------------
// Receive data from bus driver
//------------------------------------------------------------------------------
int MEL_ZR_Memory::receive(short devtype, u_short addr,
                           u_short * retsize,
                           u_short * data) const
{
    int result = MEL_NORMAL;
#if defined(PLC_SHARED_MEMORY)
    ACE_Mem_Map * mmptr;
    if (devtype == MELDEV_R)
         mmptr = const_cast<ACE_Mem_Map *>(&m_mmap);
    else 
    {
         u_int pos = devtype - MELDEV_ER;
         if (pos >= m_mmaps.size())
         {
            ACE_ERROR((LM_ERROR, ACE_TEXT("MEL_ZR_Memory::send: %d.\n"),
                                 devtype));
            return -3;
         }
         mmptr = m_mmaps[pos];
    }

    size_t setsize = *retsize / sizeof(short);
    if ((addr + setsize) > (size_t)ZR_BLOCK_SIZE)
    {
        setsize = ZR_BLOCK_SIZE - addr;
    }

    u_short * top = (u_short *)mmptr->addr();
    for (size_t pos = 0; pos < setsize; pos++)
    {
         *(data + pos) = *(top + addr + pos);
    }
    *retsize = setsize * sizeof(short);
#else
    result = mdReceive(m_path, m_stnum, devtype, addr, (short *)retsize, data);
#endif
    return result;
}

//------------------------------------------------------------------------------
// Random read data from bus driver
//------------------------------------------------------------------------------
int MEL_ZR_Memory::randRead(short * devp, u_short * bufp,
                            u_short bufsize) const
{
    ACE_UNUSED_ARG(bufsize);

    int result = MEL_NORMAL;
#if defined(PLC_SHARED_MEMORY)
    int devtype = *(devp + 1);
    u_short addr = *(devp + 2);
    u_short size = *(devp + 3);
    ACE_Mem_Map * mmptr;
    if (devtype == MELDEV_R)
         mmptr = const_cast<ACE_Mem_Map *>(&m_mmap);
    else 
    {
         u_int pos = devtype - MELDEV_ER;
         if (pos >= m_mmaps.size())
         {
            ACE_ERROR((LM_ERROR, ACE_TEXT("MEL_ZR_Memory::send: %d.\n"),
                                 devtype));
            return -3;
         }
         mmptr = m_mmaps[pos];
    }

    if ((addr + size) > ZR_BLOCK_SIZE)
    {
        return 131;
    }

    u_short * top = (u_short *)mmptr->addr();
    *bufp = *(top + addr);

#else
    result = mdRandR(m_path, m_stnum, devp, bufp, bufsize);
#endif
    return result;
}

//------------------------------------------------------------------------------
// Random write data to bus driver
//------------------------------------------------------------------------------
int MEL_ZR_Memory::randWrite(short * devp, u_short * bufp,
                             u_short bufsize)
{
    ACE_UNUSED_ARG(bufsize);

    int result = MEL_NORMAL;
#if defined(PLC_SHARED_MEMORY)
    int devtype = *(devp + 1);
    u_short addr = *(devp + 2);
    u_short size = *(devp + 3);
    ACE_Mem_Map * mmptr;
    if (devtype == MELDEV_R)
         mmptr = &m_mmap;
    else 
    {
         u_int pos = devtype - MELDEV_ER;
         if (pos >= m_mmaps.size())
         {
            ACE_ERROR((LM_ERROR, ACE_TEXT("MEL_ZR_Memory::send: %d.\n"),
                                 devtype));
            return -3;
         }
         mmptr = m_mmaps[pos];
    }

    if ((addr + size) > ZR_BLOCK_SIZE)
    {
        return 131;
    }

    u_short * top = (u_short *)mmptr->addr();
    *(top + addr) = *bufp;
    mmptr->sync();

#else
    result = mdRandW(m_path, m_stnum, devp, bufp, bufsize);
#endif
    return result;
}
