Skip to content

modbus service and entity #223

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
ARG img_user=ghcr.io/driplineorg
ARG img_repo=dripline-python
#ARG img_tag=develop-dev
ARG img_tag=receiver-test
ARG img_tag=v5.0.0-dev

FROM ${img_user}/${img_repo}:${img_tag}

COPY . /usr/local/src_dragonfly

WORKDIR /usr/local/src_dragonfly
RUN pip install pymodbus
RUN pip install docker
RUN pip install .

Expand Down
2 changes: 2 additions & 0 deletions dripline/extensions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@
# Modules in this directory

from .add_auth_spec import *
from .ethernet_modbus_service import *
#from .asteval_endpoint import *
from .thermo_fisher_endpoint import *
from .ethernet_thermo_fisher_service import *
101 changes: 101 additions & 0 deletions dripline/extensions/ethernet_modbus_service.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
try:
import pymodbus
from pymodbus.client import ModbusTcpClient
from pymodbus.payload import BinaryPayloadDecoder
except ImportError:
pass

import scarab

from dripline.core import calibrate, Entity, Service, ThrowReply

import logging
logger = logging.getLogger(__name__)

__all__ = []


__all__.append('EthernetModbusService')
class EthernetModbusService(Service):
'''
Service for connectivity to ModbusTCP instruments built on pymodbus library.
'''
def __init__(self,
ip_address,
**kwargs
):
'''
Args:
ip_address (str): properly formatted ip address of Modbus device

'''
if not 'pymodbus' in globals():
raise ImportError('pymodbus not found, required for EthernetModbusService class')

Service.__init__(self, **kwargs)

self.ip = ip_address
self.client = ModbusTcpClient(self.ip)
self._reconnect()

def _reconnect(self):
'''
Minimal connection method.
TODO: Expand to call on failed read/write, and add sophistication.
'''
if self.client.connected:
self.client.close()

if self.client.connect():
logger.debug('Connected to Alicat Device.')
else:
raise ThrowReply('resource_error_connection','Failed to Connect to Alicat Device')

def read_register(self, register):
'''
Currently only register read type #4, read_input_registers, is implemented.
Expand as desired according to other calls in https://pymodbus.readthedocs.io/en/latest/source/client.html#modbus-calls
'''
logger.debug('Reading register {}'.format(register))
try:
result = self.client.read_holding_registers(register, count=1)
except Exception as e:
logger.debug(f'read_holding_registers failed: {e}. Attempting reconnect.')
self._reconnect()
result = self.client.read_holding_registers(register, count=1)

logger.debug('Device returned {}'.format(result.registers))
return result.registers

def write_register(self, register, value):
logger.debug('writing {} to register {}'.format(value, register))
try:
response = self.client.write_register(register, value)
except Exception as e:
logger.debug(f'write_registers failed: {e}. Attempting reconnect.')
self._reconnect()
response = self.client.write_register(register, value)

logger.debug('device respond with {} '.format(response))
return value


__all__.append('ModbusEntity')
class ModbusEntity(Entity):
'''
Generic entity for Modbus read and write.
TODO: Add additional read-only or write-only versions
'''
def __init__(self,
register,
**kwargs):
self.register = register
Entity.__init__(self, **kwargs)

@calibrate()
def on_get(self):
result = self.service.read_register(self.register)
return result[0]

def on_set(self, value):
return self.service.write_register(self.register, value)