Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
564a206
PYM-2:
Nov 20, 2017
da155a8
PYM-2: Checking the incoming framer. If te Framer is a Binary Framer,
Nov 20, 2017
a7fbe8c
PYM-2: Changed the modbus binary header size from 2 to 1.
Nov 20, 2017
63d3024
PYM-3: Script is now compatible with both python2 and python3
Nov 20, 2017
df9e74e
WIP
Nov 20, 2017
dbabc9e
PYM-2: Added a new switch: -t --transaction
Nov 20, 2017
673de54
PYM-2: Removing additional dependancy and making use of existing port…
Nov 20, 2017
8dc6dde
PYM-3: Removing additional dependancy and making use of existing port…
Nov 20, 2017
5cf449f
Merged in PYM-2-message-parserpy-fix-and-port (pull request #4)
Nov 21, 2017
9cd2ada
Merged in PYM-3-fix-generatorpy-for-binary-and-port (pull request #5)
Nov 21, 2017
9c03448
Initial Bitbucket Pipelines configuration
dhoomakethu Nov 21, 2017
e05fd3f
bitbucket-pipelines.yml edited online with Bitbucket
dhoomakethu Nov 21, 2017
723109d
bitbucket-pipelines.yml edited online with Bitbucket
dhoomakethu Nov 21, 2017
ec936f7
PYM-2: Updated the transaction tests for BinaryFramerTransaction.
Nov 21, 2017
e2c7b1d
PYM-6: Minor Cleanup task
Nov 21, 2017
85a628d
PYM-6:
Nov 21, 2017
c8090a4
PYM-6: Removing uneccessary check if handler is of type ModbusBaseReq…
Nov 21, 2017
1dead0d
PYM-8: Example that read from a database as a datastore
Nov 21, 2017
4554571
PYM-8: Added two new datastores that can be used.
Nov 21, 2017
e1393c4
Small fixes
Nov 21, 2017
eed9f05
Small fixes
Nov 21, 2017
b05d905
Small fixes
Nov 21, 2017
e36c32c
Cleanup
Nov 21, 2017
9f6b23c
PYM-8: Updated the example to first write a random value at a random …
Nov 22, 2017
eec4eb5
PYM-8: Added neccessary checks and methods to allow hassle free write…
Nov 22, 2017
1e0c308
Cleanup: Removing pdb placed during testing and other comments
Nov 22, 2017
7d611c9
Merged dev into PYM-2-message-parserpy-fix-and-port
dhoomakethu Nov 22, 2017
c86a02a
bitbucket-pipelines.yml deleted online with Bitbucket
dhoomakethu Nov 22, 2017
76f3201
Merged in PYM-6-fix-passing-handler-to-tcp-sync-server (pull request #7)
Nov 24, 2017
699875c
Merged in PYM-8-remote-client-reading-database-store-fix (pull reques…
Nov 24, 2017
14d3bcb
Merged in PYM-2-message-parserpy-fix-and-port (pull request #6)
Nov 24, 2017
37b8727
#240 Fix PR failures
dhoomakethu Nov 24, 2017
8937d34
#240 fix Travis build failures
dhoomakethu Nov 24, 2017
3b7643a
#190 fix import error in dbstore-update-server example
dhoomakethu Nov 24, 2017
0e492e7
Small changes and typo fixed in pymodbus utilities and redis datastor…
Nov 27, 2017
83f349e
Added tests for redis datastore helpers
Nov 27, 2017
f6b45f8
Minor fixes to SQL datastore
Nov 28, 2017
d9f1043
Unit tests for SQL datastore - 100% coverage
Nov 28, 2017
f527c1d
Tests now compatible with python3 and python2
Nov 28, 2017
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
94 changes: 94 additions & 0 deletions examples/common/dbstore-update-server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'''
Pymodbus Server With Updating Thread
--------------------------------------------------------------------------
This is an example of having a background thread updating the
context in an SQLite4 database while the server is operating.

This scrit generates a random address range (within 0 - 65000) and a random
value and stores it in a database. It then reads the same address to verify
that the process works as expected

This can also be done with a python thread::
from threading import Thread
thread = Thread(target=updating_writer, args=(context,))
thread.start()
'''
#---------------------------------------------------------------------------#
# import the modbus libraries we need
#---------------------------------------------------------------------------#
from pymodbus.server.async import StartTcpServer
from pymodbus.device import ModbusDeviceIdentification
from pymodbus.datastore import ModbusSequentialDataBlock
from pymodbus.datastore import ModbusServerContext
from pymodbus.datastore.database import SqlSlaveContext
from pymodbus.transaction import ModbusRtuFramer, ModbusAsciiFramer
import random

#---------------------------------------------------------------------------#
# import the twisted libraries we need
#---------------------------------------------------------------------------#
from twisted.internet.task import LoopingCall

#---------------------------------------------------------------------------#
# configure the service logging
#---------------------------------------------------------------------------#
import logging
logging.basicConfig()
log = logging.getLogger()
log.setLevel(logging.DEBUG)

#---------------------------------------------------------------------------#
# define your callback process
#---------------------------------------------------------------------------#
def updating_writer(a):
''' A worker process that runs every so often and
updates live values of the context which resides in an SQLite3 database.
It should be noted that there is a race condition for the update.
:param arguments: The input arguments to the call
'''
log.debug("Updating the database context")
context = a[0]
readfunction = 0x03 # read holding registers
writefunction = 0x10
slave_id = 0x01 # slave address
count = 50

# import pdb; pdb.set_trace()

rand_value = random.randint(0, 9999)
rand_addr = random.randint(0, 65000)
log.debug("Writing to datastore: {}, {}".format(rand_addr, rand_value))
# import pdb; pdb.set_trace()
context[slave_id].setValues(writefunction, rand_addr, [rand_value])
values = context[slave_id].getValues(readfunction, rand_addr, count)
log.debug("Values from datastore: " + str(values))



#---------------------------------------------------------------------------#
# initialize your data store
#---------------------------------------------------------------------------#
block = ModbusSequentialDataBlock(0x00, [0]*0xff)
store = SqlSlaveContext(block)

context = ModbusServerContext(slaves={1: store}, single=False)


#---------------------------------------------------------------------------#
# initialize the server information
#---------------------------------------------------------------------------#
identity = ModbusDeviceIdentification()
identity.VendorName = 'pymodbus'
identity.ProductCode = 'PM'
identity.VendorUrl = 'http://github.com/bashwork/pymodbus/'
identity.ProductName = 'pymodbus Server'
identity.ModelName = 'pymodbus Server'
identity.MajorMinorRevision = '1.0'

#---------------------------------------------------------------------------#
# run the server you want
#---------------------------------------------------------------------------#
time = 5 # 5 seconds delay
loop = LoopingCall(f=updating_writer, a=(context,))
loop.start(time, now=False) # initially delay by time
StartTcpServer(context, identity=identity, address=("", 5020))
27 changes: 16 additions & 11 deletions examples/contrib/message-generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* binary - `./generate-messages.py -f binary -m tx -b`
'''
from optparse import OptionParser
import codecs as c
#--------------------------------------------------------------------------#
# import all the available framers
#--------------------------------------------------------------------------#
Expand All @@ -30,6 +31,7 @@
from pymodbus.mei_message import *
from pymodbus.register_read_message import *
from pymodbus.register_write_message import *
from pymodbus.compat import IS_PYTHON3

#--------------------------------------------------------------------------#
# initialize logging
Expand All @@ -51,17 +53,17 @@
WriteSingleRegisterRequest,
WriteSingleCoilRequest,
ReadWriteMultipleRegistersRequest,

ReadExceptionStatusRequest,
GetCommEventCounterRequest,
GetCommEventLogRequest,
ReportSlaveIdRequest,

ReadFileRecordRequest,
WriteFileRecordRequest,
MaskWriteRegisterRequest,
ReadFifoQueueRequest,

ReadDeviceInformationRequest,

ReturnQueryDataRequest,
Expand Down Expand Up @@ -97,7 +99,7 @@
WriteSingleRegisterResponse,
WriteSingleCoilResponse,
ReadWriteMultipleRegistersResponse,

ReadExceptionStatusResponse,
GetCommEventCounterResponse,
GetCommEventLogResponse,
Expand Down Expand Up @@ -149,13 +151,13 @@
'write_registers' : [0x01] * 8,
'transaction' : 0x01,
'protocol' : 0x00,
'unit' : 0x01,
'unit' : 0xff,
}


#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# generate all the requested messages
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
def generate_messages(framer, options):
''' A helper method to parse the command line options

Expand All @@ -168,13 +170,16 @@ def generate_messages(framer, options):
print ("%-44s = " % message.__class__.__name__)
packet = framer.buildPacket(message)
if not options.ascii:
packet = packet.encode('hex') + '\n'
print (packet) # because ascii ends with a \r\n
if not IS_PYTHON3:
packet = packet.encode('hex')
else:
packet = c.encode(packet, 'hex_codec').decode('utf-8')
print ("{}\n".format(packet)) # because ascii ends with a \r\n


#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# initialize our program settings
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
def get_options():
''' A helper method to parse the command line options

Expand Down
40 changes: 31 additions & 9 deletions examples/contrib/message-parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,24 @@
* rtu
* binary
'''
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# import needed libraries
#---------------------------------------------------------------------------#
from __future__ import print_function
import sys
import collections
import textwrap
from optparse import OptionParser
import codecs as c

from pymodbus.utilities import computeCRC, computeLRC
from pymodbus.factory import ClientDecoder, ServerDecoder
from pymodbus.transaction import ModbusSocketFramer
from pymodbus.transaction import ModbusBinaryFramer
from pymodbus.transaction import ModbusAsciiFramer
from pymodbus.transaction import ModbusRtuFramer
from pymodbus.compat import byte2int, int2byte, IS_PYTHON3


#--------------------------------------------------------------------------#
# Logging
Expand All @@ -33,9 +37,9 @@
modbus_log = logging.getLogger("pymodbus")


#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# build a quick wrapper around the framers
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
class Decoder(object):

def __init__(self, framer, encode=False):
Expand All @@ -52,7 +56,10 @@ def decode(self, message):

:param message: The messge to decode
'''
value = message if self.encode else message.encode('hex')
if IS_PYTHON3:
value = message if self.encode else c.encode(message, 'hex_codec')
else:
value = message if self.encode else message.encode('hex')
print("="*80)
print("Decoding Message %s" % value)
print("="*80)
Expand All @@ -64,7 +71,7 @@ def decode(self, message):
print("%s" % decoder.decoder.__class__.__name__)
print("-"*80)
try:
decoder.addToFrame(message.encode())
decoder.addToFrame(message)
if decoder.checkFrame():
decoder.advanceFrame()
decoder.processIncomingPacket(message, self.report)
Expand All @@ -86,7 +93,7 @@ def report(self, message):
:param message: The message to print
'''
print("%-15s = %s" % ('name', message.__class__.__name__))
for k,v in message.__dict__.iteritems():
for (k, v) in message.__dict__.items():
if isinstance(v, dict):
print("%-15s =" % k)
for kk,vv in v.items():
Expand All @@ -102,9 +109,9 @@ def report(self, message):
print("%-15s = %s" % ('documentation', message.__doc__))


#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
# and decode our message
#---------------------------------------------------------------------------#
#---------------------------------------------------------------------------#
def get_options():
''' A helper method to parse the command line options

Expand Down Expand Up @@ -136,6 +143,10 @@ def get_options():
help="The file containing messages to parse",
dest="file", default=None)

parser.add_option("-t", "--transaction",
help="If the incoming message is in hexadecimal format",
action="store_true", dest="transaction", default=False)

(opt, arg) = parser.parse_args()

if not opt.message and len(arg) > 0:
Expand All @@ -150,8 +161,19 @@ def get_messages(option):
:returns: The message iterator to parse
'''
if option.message:
if option.transaction:
msg = ""
for segment in option.message.split():
segment = segment.replace("0x", "")
segment = "0" + segment if len(segment) == 1 else segment
msg = msg + segment
option.message = msg

if not option.ascii:
option.message = option.message.decode('hex')
if not IS_PYTHON3:
option.message = option.message.decode('hex')
else:
option.message = c.decode(option.message.encode(), 'hex_codec')
yield option.message
elif option.file:
with open(option.file, "r") as handle:
Expand Down
7 changes: 7 additions & 0 deletions pymodbus/datastore/database/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from pymodbus.datastore.database.sql_datastore import SqlSlaveContext
from pymodbus.datastore.database.redis_datastore import RedisSlaveContext

#---------------------------------------------------------------------------#
# Exported symbols
#---------------------------------------------------------------------------#
__all__ = ["SqlSlaveContext", "RedisSlaveContext"]
Loading