leonid@255: /*
leonid@255: This file is part of CanFestival, a library implementing CanOpen Stack. 
leonid@255: 
leonid@255: CanFestival Copyright (C): Edouard TISSERANT and Francis DUPIN
leonid@255: CanFestival Win32 port Copyright (C) 2007 Leonid Tochinski, ChattenAssociates, Inc.
leonid@255: 
leonid@255: See COPYING file for copyrights details.
leonid@255: 
leonid@255: This library is free software; you can redistribute it and/or
leonid@255: modify it under the terms of the GNU Lesser General Public
leonid@255: License as published by the Free Software Foundation; either
leonid@255: version 2.1 of the License, or (at your option) any later version.
leonid@255: 
leonid@255: This library is distributed in the hope that it will be useful,
leonid@255: but WITHOUT ANY WARRANTY; without even the implied warranty of
leonid@255: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
leonid@255: Lesser General Public License for more details.
leonid@255: 
leonid@255: You should have received a copy of the GNU Lesser General Public
leonid@255: License along with this library; if not, write to the Free Software
leonid@255: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
leonid@255: */
leonid@255: 
etisserant@145: // can_uvccm_win32 adapter (http://www.gridconnect.com)
etisserant@145: // driver for CanFestival-3 Win32 port
etisserant@145: 
etisserant@145: #include <sstream>
etisserant@145: #include <iomanip>
etisserant@145: #if 0  // change to 1 if you use boost
etisserant@145: #include <boost/algorithm/string/case_conv.hpp>
etisserant@145: #else
etisserant@145: #include <algorithm>
etisserant@145: #endif
etisserant@145: 
etisserant@145: extern "C" {
etisserant@145: #include "can_driver.h"
etisserant@145: }
etisserant@145: class can_uvccm_win32
etisserant@145:    {
etisserant@145:    public:
etisserant@145:       class error
etisserant@145:         {
etisserant@145:         };
etisserant@145:       can_uvccm_win32(s_BOARD *board);
etisserant@145:       ~can_uvccm_win32();
etisserant@145:       bool send(const Message *m);
etisserant@145:       bool receive(Message *m);
etisserant@145:    private:
etisserant@145:       bool open_rs232(int port = 1, int baud_rate = 57600);
etisserant@145:       bool close_rs232();
etisserant@145:       bool get_can_data(const char* can_cmd_buf, long& bufsize, Message* m);
etisserant@145:       bool set_can_data(const Message& m, std::string& can_cmd);
etisserant@145:    private:
etisserant@145:       HANDLE m_port;
etisserant@145:       HANDLE m_read_event;
etisserant@145:       HANDLE m_write_event;
etisserant@145:       std::string m_residual_buffer;
etisserant@145:    };
etisserant@145: 
etisserant@145: can_uvccm_win32::can_uvccm_win32(s_BOARD *board) : m_port(INVALID_HANDLE_VALUE),
etisserant@145:       m_read_event(0),
etisserant@145:       m_write_event(0)
etisserant@145:    {
etisserant@145:    if (strcmp( board->baudrate, "125K") || !open_rs232(1))
etisserant@145:       throw error();
etisserant@145:    }
etisserant@145: 
etisserant@145: can_uvccm_win32::~can_uvccm_win32()
etisserant@145:    {
etisserant@145:    close_rs232();
etisserant@145:    }
etisserant@145: 
etisserant@145: bool can_uvccm_win32::send(const Message *m)
etisserant@145:    {
etisserant@145:    if (m_port == INVALID_HANDLE_VALUE)
greg@267:       return true;
etisserant@145: 
etisserant@145:    // build can_uvccm_win32 command string
etisserant@145:    std::string can_cmd;
etisserant@145:    set_can_data(*m, can_cmd);
etisserant@145: 
etisserant@145:    OVERLAPPED overlapped;
etisserant@145:    ::memset(&overlapped, 0, sizeof overlapped);
etisserant@145:    overlapped.hEvent = m_write_event;
etisserant@145:    ::ResetEvent(overlapped.hEvent);
etisserant@145: 
etisserant@145:    unsigned long bytes_written = 0;
etisserant@145:    ::WriteFile(m_port, can_cmd.c_str(), (unsigned long)can_cmd.size(), &bytes_written, &overlapped);
etisserant@145:    // wait for write operation completion
etisserant@145:    enum { WRITE_TIMEOUT = 1000 };
etisserant@145:    ::WaitForSingleObject(overlapped.hEvent, WRITE_TIMEOUT);
etisserant@145:    // get number of bytes written
etisserant@145:    ::GetOverlappedResult(m_port, &overlapped, &bytes_written, FALSE);
etisserant@145: 
etisserant@145:    bool result = (bytes_written == can_cmd.size());
greg@267:    
greg@267:    return false;
etisserant@145:    }
etisserant@145: 
etisserant@145: 
etisserant@145: bool can_uvccm_win32::receive(Message *m)
etisserant@145:    {
etisserant@145:    if (m_port == INVALID_HANDLE_VALUE)
etisserant@145:       return false;
etisserant@145: 
etisserant@145:    long res_buffer_size = (long)m_residual_buffer.size();
etisserant@145:    bool result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
etisserant@145:    if (result)
etisserant@145:       {
etisserant@145:       m_residual_buffer.erase(0, res_buffer_size);
etisserant@145:       return true;
etisserant@145:       }
etisserant@145: 
etisserant@145:    enum { READ_TIMEOUT = 500 };
etisserant@145: 
etisserant@145:    OVERLAPPED overlapped;
etisserant@145:    ::memset(&overlapped, 0, sizeof overlapped);
etisserant@145:    overlapped.hEvent = m_read_event;
etisserant@145:    ::ResetEvent(overlapped.hEvent);
etisserant@145:    unsigned long event_mask = 0;
etisserant@145: 
etisserant@145:    if (FALSE == ::WaitCommEvent(m_port, &event_mask, &overlapped) && ERROR_IO_PENDING == ::GetLastError())
etisserant@145:       {
etisserant@145:       if (WAIT_TIMEOUT == ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT))
etisserant@145:          return false;
etisserant@145:       }
etisserant@145: 
etisserant@145:    // get number of bytes in the input que
etisserant@145:    COMSTAT stat;
etisserant@145:    ::memset(&stat, 0, sizeof stat);
etisserant@145:    unsigned long errors = 0;
etisserant@145:    ::ClearCommError(m_port, &errors, &stat);
etisserant@145:    if (stat.cbInQue == 0)
etisserant@145:       return false;
etisserant@145:    char buffer[3000];
etisserant@145: 
etisserant@145:    unsigned long bytes_to_read = min(stat.cbInQue, sizeof (buffer));
etisserant@145: 
etisserant@145:    unsigned long bytes_read = 0;
etisserant@145:    ::ReadFile(m_port, buffer, bytes_to_read, &bytes_read, &overlapped);
etisserant@145:    // wait for read operation completion
etisserant@145:    ::WaitForSingleObject(overlapped.hEvent, READ_TIMEOUT);
etisserant@145:    // get number of bytes read
etisserant@145:    ::GetOverlappedResult(m_port, &overlapped, &bytes_read, FALSE);
etisserant@145:    result = false;
etisserant@145:    if (bytes_read > 0)
etisserant@145:       {
etisserant@145:       m_residual_buffer.append(buffer, bytes_read);
etisserant@145:       res_buffer_size = (long)m_residual_buffer.size();
etisserant@145:       result = get_can_data(m_residual_buffer.c_str(), res_buffer_size, m);
etisserant@145:       if (result)
etisserant@145:          m_residual_buffer.erase(0, res_buffer_size);
etisserant@145:       }
etisserant@145:    return result;
etisserant@145:    }
etisserant@145: 
etisserant@145: bool can_uvccm_win32::open_rs232(int port, int baud_rate)
etisserant@145:    {
etisserant@145:    if (m_port != INVALID_HANDLE_VALUE)
etisserant@145:       return true;
etisserant@145: 
etisserant@145:    std::ostringstream device_name;
etisserant@145:    device_name << "COM" << port;
etisserant@145: 
etisserant@145:    m_port = ::CreateFile(device_name.str().c_str(),
etisserant@145:                          GENERIC_READ | GENERIC_WRITE,
etisserant@145:                          0,   // exclusive access
etisserant@145:                          NULL,   // no security
etisserant@145:                          OPEN_EXISTING,
etisserant@145:                          FILE_FLAG_OVERLAPPED,   // overlapped I/O
etisserant@145:                          NULL); // null template
etisserant@145: 
etisserant@145:    // Check the returned handle for INVALID_HANDLE_VALUE and then set the buffer sizes.
etisserant@145:    if (m_port == INVALID_HANDLE_VALUE)
etisserant@145:       return false;
etisserant@145: 
etisserant@145:    //  SetCommMask(m_hCom,EV_RXCHAR|EV_TXEMPTY|EV_CTS|EV_DSR|EV_RLSD|EV_BREAK|EV_ERR|EV_RING); //
etisserant@145:    ::SetCommMask(m_port, EV_RXFLAG);
etisserant@145: 
etisserant@145:    COMMTIMEOUTS timeouts;
etisserant@145:    ::memset(&timeouts, 0, sizeof (timeouts));
etisserant@145:    timeouts.ReadIntervalTimeout = -1;
etisserant@145:    timeouts.ReadTotalTimeoutConstant = 0;
etisserant@145:    timeouts.ReadTotalTimeoutMultiplier = 0;
etisserant@145:    timeouts.WriteTotalTimeoutConstant = 5000;
etisserant@145:    timeouts.WriteTotalTimeoutMultiplier = 0;
etisserant@145:    SetCommTimeouts(m_port, &timeouts); //
etisserant@145: 
etisserant@145:    ::SetupComm(m_port, 1024, 512); // set buffer sizes
etisserant@145: 
etisserant@145:    // Port settings are specified in a Data Communication Block (DCB). The easiest way to initialize a DCB is to call GetCommState to fill in its default values, override the values that you want to change and then call SetCommState to set the values.
etisserant@145:    DCB dcb;
etisserant@145:    ::memset(&dcb, 0, sizeof (dcb));
etisserant@145:    ::GetCommState(m_port, &dcb);
etisserant@145:    dcb.BaudRate = baud_rate;
etisserant@145:    dcb.ByteSize = 8;
etisserant@145:    dcb.Parity = NOPARITY;
etisserant@145:    dcb.StopBits = ONESTOPBIT;
etisserant@145:    dcb.fAbortOnError = TRUE;
etisserant@145:    dcb.EvtChar = 0x0A; // '\n' character
etisserant@145:    ::SetCommState(m_port, &dcb);
etisserant@145: 
etisserant@145:    ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
etisserant@145: 
etisserant@145:    m_read_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
etisserant@145:    m_write_event = ::CreateEvent(NULL, TRUE, FALSE, NULL);
etisserant@145: 
etisserant@145:    return true;
etisserant@145:    }
etisserant@145: 
etisserant@145: bool can_uvccm_win32::close_rs232()
etisserant@145:    {
etisserant@145:    if (m_port != INVALID_HANDLE_VALUE)
etisserant@145:       {
etisserant@145:       ::PurgeComm(m_port, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR);
etisserant@145:       ::CloseHandle(m_port);
etisserant@145:       m_port = INVALID_HANDLE_VALUE;
etisserant@145:       ::CloseHandle(m_read_event);
etisserant@145:       m_read_event = 0;
etisserant@145:       ::CloseHandle(m_write_event);
etisserant@145:       m_write_event = 0;
etisserant@145:       m_residual_buffer.clear();
etisserant@145:       }
etisserant@145:    return true;
etisserant@145:    }
etisserant@145: 
etisserant@145: bool can_uvccm_win32::get_can_data(const char* can_cmd_buf, long& bufsize, Message* m)
etisserant@145:    {
etisserant@145:    if (bufsize < 5)
etisserant@145:       {
etisserant@145:       bufsize = 0;
etisserant@145:       return false;
etisserant@145:       }
etisserant@145: 
etisserant@145:    Message msg;
etisserant@145:    ::memset(&msg, 0 , sizeof (msg));
etisserant@145:    char colon = 0, type = 0, request = 0;
etisserant@145:    std::istringstream buf(std::string(can_cmd_buf, bufsize));
etisserant@365:    buf >> colon >> type >> std::hex >> msg.cob_id >> request;
etisserant@145:    if (colon != ':' || (type != 'S' && type != 'X'))
etisserant@145:       {
etisserant@145:       bufsize = 0;
etisserant@145:       return false;
etisserant@145:       }
etisserant@145:    if (request == 'N')
etisserant@145:       {
etisserant@145:       msg.rtr = 0;
etisserant@145:       for (msg.len = 0; msg.len < 8; ++msg.len)
etisserant@145:          {
etisserant@145:          std::string data_byte_str;
etisserant@145:          buf >> std::setw(2) >> data_byte_str;
etisserant@145:          if (data_byte_str[0] == ';')
etisserant@145:             break;
etisserant@145:          long byte_val = -1;
etisserant@145:          std::istringstream(data_byte_str) >> std::hex >> byte_val;
etisserant@145:          if (byte_val == -1)
etisserant@145:             {
etisserant@145:             bufsize = 0;
etisserant@145:             return false;
etisserant@145:             }
etisserant@145:          msg.data[msg.len] = (UNS8)byte_val;
etisserant@145:          }
etisserant@145:       if (msg.len == 8)
etisserant@145:          {
etisserant@145:          char semicolon = 0;
etisserant@145:          buf >> semicolon;
etisserant@145:          if (semicolon != ';')
etisserant@145:             {
etisserant@145:             bufsize = 0;
etisserant@145:             return false;
etisserant@145:             }
etisserant@145:          }
etisserant@145: 
etisserant@145:       }
etisserant@145:    else if (request == 'R')
etisserant@145:       {
etisserant@145:       msg.rtr = 1;
etisserant@145:       buf >> msg.len;
etisserant@145:       }
etisserant@145:    else
etisserant@145:       {
etisserant@145:       bufsize = 0;
etisserant@145:       return false;
etisserant@145:       }
etisserant@145: 
etisserant@145:    bufsize = buf.tellg();
etisserant@145: 
etisserant@145:    *m = msg;
etisserant@145:    return true;
etisserant@145:    }
etisserant@145: 
etisserant@145: bool can_uvccm_win32::set_can_data(const Message& m, std::string& can_cmd)
etisserant@145:    {
etisserant@145:    // build can_uvccm_win32 command string
etisserant@145:    std::ostringstream can_cmd_str;
etisserant@365:    can_cmd_str << ":S" << std::hex << m.cob_id;
etisserant@145:    if (m.rtr == 1)
etisserant@145:       {
etisserant@145:       can_cmd_str << 'R' << (long)m.len;
etisserant@145:       }
etisserant@145:    else
etisserant@145:       {
etisserant@145:       can_cmd_str << 'N';
etisserant@145:       for (int i = 0; i < m.len; ++i)
etisserant@145:          can_cmd_str << std::hex << std::setfill('0') << std::setw(2) << (long)m.data[i];
etisserant@145:       }
etisserant@145:    can_cmd_str << ';';
etisserant@145:    can_cmd = can_cmd_str.str();
etisserant@145: #ifdef BOOST_VERSION
etisserant@145:    boost::to_upper(can_cmd);
etisserant@145: #else
etisserant@145:    std::transform(can_cmd.begin(),can_cmd.end(),can_cmd.begin(),::toupper);
etisserant@145: #endif
etisserant@145:    return true;
etisserant@145:    }
etisserant@145: 
etisserant@145: 
etisserant@145: //------------------------------------------------------------------------
etisserant@145: extern "C"
etisserant@145:    UNS8 canReceive_driver(CAN_HANDLE fd0, Message *m)
etisserant@145:    {
etisserant@145:    return (UNS8)(!(reinterpret_cast<can_uvccm_win32*>(fd0)->receive(m)));
etisserant@145:    }
etisserant@145: 
etisserant@145: extern "C"
etisserant@145:    UNS8 canSend_driver(CAN_HANDLE fd0, Message *m)
etisserant@145:    {
etisserant@145:    return (UNS8)reinterpret_cast<can_uvccm_win32*>(fd0)->send(m);
etisserant@145:    }
etisserant@145: 
etisserant@145: extern "C"
etisserant@145:    CAN_HANDLE canOpen_driver(s_BOARD *board)
etisserant@145:    {
etisserant@145:    try
etisserant@145:       {
etisserant@145:       return (CAN_HANDLE) new can_uvccm_win32(board);
etisserant@145:       }
etisserant@145:    catch (can_uvccm_win32::error&)
etisserant@145:       {
etisserant@145:       return NULL;
etisserant@145:       }
etisserant@145:    }
etisserant@145: 
etisserant@145: extern "C"
etisserant@145:    int canClose_driver(CAN_HANDLE inst)
etisserant@145:    {
etisserant@145:    delete reinterpret_cast<can_uvccm_win32*>(inst);
etisserant@145:    return 1;
etisserant@145:    }
etisserant@145: 
groke6@384: extern "C"
groke6@384: 	UNS8 canChangeBaudRate_driver( CAN_HANDLE fd, char* baud)
groke6@384: 	{
groke6@384: 	return 0;
groke6@384: 	}