Skip to content

Commit

Permalink
NE500 now supports connection via a USB-to-serial adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
cstawarz committed Mar 30, 2016
1 parent ca88745 commit 28c5bfa
Show file tree
Hide file tree
Showing 7 changed files with 222 additions and 11 deletions.
152 changes: 152 additions & 0 deletions plugins/core/NE500/Connections/NE500SerialConnection.cpp
@@ -0,0 +1,152 @@
//
// NE500SerialConnection.cpp
// NE500Plugin
//
// Created by Christopher Stawarz on 3/29/16.
//
//

#include "NE500SerialConnection.hpp"

#include <sys/ioctl.h>


BEGIN_NAMESPACE_MW


bool NE500SerialConnection::connect() {
if (connected) {
return true;
}

// Open the serial port read/write, with no controlling terminal, and don't wait for a connection.
// The O_NONBLOCK flag also causes subsequent I/O on the device to be non-blocking.
if (-1 == (fd = ::open(path.c_str(), O_RDWR | O_NOCTTY | O_NONBLOCK))) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot open serial port (%s): %s", path.c_str(), strerror(errno));
return false;
}

// open() follows POSIX semantics: multiple open() calls to the same file will succeed
// unless the TIOCEXCL ioctl is issued. This will prevent additional opens except by root-owned
// processes.
if (-1 == ioctl(fd, TIOCEXCL)) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot obtain exclusive use of serial port: %s", strerror(errno));
close(fd);
return false;
}

// Get the current options and save them, so we can restore the default settings later
if (-1 == tcgetattr(fd, &origAttrs)) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot obtain current serial port attributes: %s", strerror(errno));
close(fd);
return false;
}

struct termios attrs = origAttrs;
cfmakeraw(&attrs); // Set raw input (non-canonical) mode
cfsetspeed(&attrs, B19200); // Set speed to 19200 baud
attrs.c_cflag |= CS8; // Use 8-bit words
attrs.c_cflag &= ~PARENB; // No parity
attrs.c_cflag &= ~CSTOPB; // 1 stop bit

// Cause the new options to take effect immediately
if (-1 == tcsetattr(fd, TCSANOW, &attrs)) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot set serial port attributes: %s", strerror(errno));
close(fd);
return false;
}

connected = true;
return true;
}


bool NE500SerialConnection::disconnect() {
if (!connected) {
return true;
}

// Block until all written output has been sent to the device
if (-1 == tcdrain(fd)) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Serial port drain failed: %s", strerror(errno));
}

// Restore original options
if (-1 == tcsetattr(fd, TCSANOW, &origAttrs)) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot restore previous serial port attributes: %s", strerror(errno));
}

if (-1 == ::close(fd)) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot close serial port: %s", strerror(errno));
}

connected = false;
return true;
}


bool NE500SerialConnection::send(const std::string &command) {
if (!connected) {
return false;
}

if (-1 == ::write(fd, command.data(), command.size())) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot send data to NE500 device: %s", strerror(errno));
return false;
}

return true;
}


bool NE500SerialConnection::receive(std::string &response) {
if (!connected) {
return false;
}

std::array<char, 512> buffer;
auto rc = ::read(fd, buffer.data(), buffer.size());

if (rc >= 0) {

response.append(buffer.data(), rc);

} else if (errno != EWOULDBLOCK) {

merror(M_IODEVICE_MESSAGE_DOMAIN, "Cannot receive data from NE500 device: %s", strerror(errno));
return false;

}

return true;
}


END_NAMESPACE_MW



























45 changes: 45 additions & 0 deletions plugins/core/NE500/Connections/NE500SerialConnection.hpp
@@ -0,0 +1,45 @@
//
// NE500SerialConnection.hpp
// NE500Plugin
//
// Created by Christopher Stawarz on 3/29/16.
//
//

#ifndef NE500SerialConnection_hpp
#define NE500SerialConnection_hpp

#include "NE500Connection.hpp"

#include <termios.h>


BEGIN_NAMESPACE_MW


class NE500SerialConnection : public NE500Connection {

public:
explicit NE500SerialConnection(const std::string &path) :
path(path),
fd(-1)
{ }

bool connect() override;
bool disconnect() override;

bool send(const std::string &command) override;
bool receive(std::string &response) override;

private:
const std::string path;
int fd;
struct termios origAttrs;

};


END_NAMESPACE_MW


#endif /* NE500SerialConnection_hpp */
1 change: 0 additions & 1 deletion plugins/core/NE500/Connections/NE500SocketConnection.hpp
Expand Up @@ -33,7 +33,6 @@ class NE500SocketConnection : public NE500Connection {
private:
const std::string address;
const int port;

int s;

};
Expand Down
13 changes: 10 additions & 3 deletions plugins/core/NE500/MWComponents.yaml
Expand Up @@ -8,15 +8,22 @@ icon: smallIOFolder
description: >
Used for interfacing to a network of `NE-500 syringe pumps
<http://www.syringepump.com/oem.php#aa>`_, accessed via a serial-to-ethernet
bridge
bridge or a USB-to-serial adapter
parameters:
-
name: address
required: yes
example: "10.0.254.254"
example:
- '"10.0.254.254"'
- '"/dev/cu.usbserial-FTH1RRH5"'
description: >
If `port`_ is provided, the address is taken to be a host name,
and MWorks attempts to connect to the pump network via a
`TCP <https://en.wikipedia.org/wiki/Transmission_Control_Protocol>`_
socket. Otherwise, the address is assumed to be a filesystem path that
represents a serial port.
-
name: port
required: yes
example: 100
-
name: response_timeout
Expand Down
6 changes: 6 additions & 0 deletions plugins/core/NE500/NE500Plugin.xcodeproj/project.pbxproj
Expand Up @@ -42,6 +42,7 @@
E126F26E1BFBAF5F00CFC9FF /* NE500DeviceChannel.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E126F26C1BFBAF5F00CFC9FF /* NE500DeviceChannel.cpp */; };
E12D98B716BC2217004FEF79 /* NE500Plugin.bundle in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8D5B49B6048680CD000E48DA /* NE500Plugin.bundle */; };
E15D0B0016C2B34100F331B1 /* libboost_system.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E15D0AFF16C2B34100F331B1 /* libboost_system.a */; };
E16361131CAB04FF009752BE /* NE500SerialConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E16361111CAB04FF009752BE /* NE500SerialConnection.cpp */; };
E1810E101CAACB62003001C0 /* NE500Connection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1810E0E1CAACB62003001C0 /* NE500Connection.cpp */; };
E1810E131CAACDCE003001C0 /* NE500SocketConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = E1810E111CAACDCE003001C0 /* NE500SocketConnection.cpp */; };
E1E07E9F1C04F2D8008DD97E /* MWComponents.yaml in Resources */ = {isa = PBXBuildFile; fileRef = E1E07E9E1C04F2D8008DD97E /* MWComponents.yaml */; };
Expand Down Expand Up @@ -103,6 +104,8 @@
E126F26C1BFBAF5F00CFC9FF /* NE500DeviceChannel.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NE500DeviceChannel.cpp; sourceTree = "<group>"; };
E126F26D1BFBAF5F00CFC9FF /* NE500DeviceChannel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NE500DeviceChannel.h; sourceTree = "<group>"; };
E15D0AFF16C2B34100F331B1 /* libboost_system.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libboost_system.a; path = "/Library/Application Support/MWorks/Developer/lib/libboost_system.a"; sourceTree = "<absolute>"; };
E16361111CAB04FF009752BE /* NE500SerialConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NE500SerialConnection.cpp; sourceTree = "<group>"; };
E16361121CAB04FF009752BE /* NE500SerialConnection.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NE500SerialConnection.hpp; sourceTree = "<group>"; };
E1810E0E1CAACB62003001C0 /* NE500Connection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NE500Connection.cpp; sourceTree = "<group>"; };
E1810E0F1CAACB62003001C0 /* NE500Connection.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = NE500Connection.hpp; sourceTree = "<group>"; };
E1810E111CAACDCE003001C0 /* NE500SocketConnection.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NE500SocketConnection.cpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -201,6 +204,8 @@
children = (
E1810E0F1CAACB62003001C0 /* NE500Connection.hpp */,
E1810E0E1CAACB62003001C0 /* NE500Connection.cpp */,
E16361121CAB04FF009752BE /* NE500SerialConnection.hpp */,
E16361111CAB04FF009752BE /* NE500SerialConnection.cpp */,
E1810E121CAACDCE003001C0 /* NE500SocketConnection.hpp */,
E1810E111CAACDCE003001C0 /* NE500SocketConnection.cpp */,
);
Expand Down Expand Up @@ -287,6 +292,7 @@
5C4B0A600DC791D4001BC518 /* NE500PumpNetworkDevice.cpp in Sources */,
5C4B0ADE0DC793E2001BC518 /* NE500Plugin.cpp in Sources */,
E126F26E1BFBAF5F00CFC9FF /* NE500DeviceChannel.cpp in Sources */,
E16361131CAB04FF009752BE /* NE500SerialConnection.cpp in Sources */,
E1810E131CAACDCE003001C0 /* NE500SocketConnection.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
14 changes: 9 additions & 5 deletions plugins/core/NE500/NE500PumpNetworkDevice.cpp
Expand Up @@ -9,6 +9,7 @@

#include "NE500PumpNetworkDevice.h"

#include "NE500SerialConnection.hpp"
#include "NE500SocketConnection.hpp"


Expand All @@ -27,21 +28,24 @@ void NE500PumpNetworkDevice::describeComponent(ComponentInfo &info) {
info.setSignature("iodevice/ne500");

info.addParameter(ADDRESS);
info.addParameter(PORT);
info.addParameter(PORT, false);
info.addParameter(RESPONSE_TIMEOUT, "100ms");
info.addParameter(LOG_PUMP_COMMANDS, "YES");
}


NE500PumpNetworkDevice::NE500PumpNetworkDevice(const ParameterValueMap &parameters) :
IODevice(parameters),
address(VariablePtr(parameters[ADDRESS])->getValue().getString()),
port(parameters[PORT]),
response_timeout(parameters[RESPONSE_TIMEOUT]),
logPumpCommands(parameters[LOG_PUMP_COMMANDS]),
active(false)
{
connection.reset(new NE500SocketConnection(address, port));
const std::string address(VariablePtr(parameters[ADDRESS])->getValue().getString());
if (parameters[PORT].empty()) {
connection.reset(new NE500SerialConnection(address));
} else {
connection.reset(new NE500SocketConnection(address, int(parameters[PORT])));
}
}


Expand Down Expand Up @@ -109,7 +113,7 @@ static inline std::string removeControlChars(std::string str) {

bool NE500PumpNetworkDevice::sendMessage(const std::string &pump_id, string message) {
if (!connection->isConnected()) {
merror(M_IODEVICE_MESSAGE_DOMAIN, "No connection to NE500 device (%s)", address.c_str());
merror(M_IODEVICE_MESSAGE_DOMAIN, "No connection to NE500 device");
return false;
}

Expand Down
2 changes: 0 additions & 2 deletions plugins/core/NE500/NE500PumpNetworkDevice.h
Expand Up @@ -20,8 +20,6 @@ BEGIN_NAMESPACE_MW
class NE500PumpNetworkDevice : public IODevice, boost::noncopyable {

private:
const std::string address;
const int port;
const MWTime response_timeout;
const bool logPumpCommands;

Expand Down

0 comments on commit 28c5bfa

Please sign in to comment.