OK, as no one offered a solution, I did my own thing - the 'TCP Modbus Broker'...

I found out that the Modbus-TCP-packet still contains the slave address which can be entered in each row of the Modbus plugin.
So I wrote a piece of software which presents itself to Mach3 as a single Modbus-TCP Client and uses the slave address to divert the Modbus telegrams to different IP adresses (or DNS-names)
For portability reasons it is done in Python (ver 3.3.).
It has to be started before Mach3 manually or in a batch unsing the command line
mmb.py [-test] [-quiet] Client-Address_1 ...[Client-Address_n]
-quit Makes the program just count packets as indication of proper operation. If it is omitted, full data of all packages is displayed
-test Allows local testing with TCP echo-clients by diverting to two adresses and using port 503 and 504 instead of modbus port 502. Not for production usage!
In Mach3 Modbus-TCP-plugin you enter 127.0.0.1 as IP-address if the program runs on the same computer. If you decide to run it on an other computer (i.e. Raspberry or cheap Orange-Pi), use the appropriate address of the device.
Have fun!
Uli
#UHU Modbus TCP Message Broker
#
# Ermöglicht die Nutzung mehrerer Modbus-TCP-Devices an Mach3, das im Modbus-TCP-Plugin zwar Slave-
# Adressen zur Eingabe anbietet, aber leider nur eine IP-Adresse verwalten kann.
#
# Das Programm stellt sich gegenüber Mach3 als Modbus-TCP-Device dar und verteilt Modbus-Anfragen
# auf die angegebenen im Netz befindlichen Clients (Netzwerkadressen oder DNS-Namen)
# Dabei entspricht der erste angegebene Client dem Slave #1 aus der Mach3 Modbus TCP Konfiguration.
#
# Voraussetzung: Installierter Python Interpreter >Ver3.0 (frei im Web verfuegbar)
#
# Aufruf mit mmb.py [-test] [-quiet] Client-Adresse_1 [Client-Aderess_n]
#
# -quiet Statt der Ausgabe der vollständigen Daten der Modbus-Telegramme wird nur ein Zähler
# dargestellt, an dem man erkennen kann ob Pakete einwandfrei transportiert werden.
#
# -test baut eine Verbindung zu zwei angebbaren Clients unter den Portadressen 503 und 504 auf.
# Dies ist nützlich, wen man für Tests statt echter Hardware lokale, virtuelle Modbus-TCP Clients
# mit dem Tool TCP-Responder.py nutzen möchte
#
# Eingebaute Features:
# Reconnect bei Ausfall eines (oder aller) Clients
# Reconnect bei Neustart von Mach3. Leider baut Mach3 eine abgebrochene Verbindung zu einem Modbus-TCP-Client
# nicht von selbst wieder auf, auch nicht durch RESET. Dies kann ein Problem geben, wenn der Modbus-Broker
# auf einem anderen Rechner läuft und dieser z.B. neu startet. Dann muss man Mach3 neu starten oder in
# den Einstellungen für das TCP-Modbus-Plugin 'APPLY' drücken.
#
#
import socket
import sys
import struct
PORT = 502
def printInline(t):
global pkgCtr
if quiet:
pkgCtr +=1
t = str(pkgCtr)
delete = "\b" * len(t)
print("{0}{1}".format(delete, t), end="")
sys.stdout.flush()
else:
print(t)
def connHost(sockH):
print ("Waiting for Modbus Host on port " + str(PORT))
connection, NetAddr = sockH.accept()
print( 'Connected by '+str(NetAddr))
print()
return connection
def connSlave(NetAddr,i):
if not testClients:
port = PORT
else:
if i == 1:
port = 503
else:
port = 504
print ("\nConnecting slave " + str(i) + " to " + NetAddr + " on port " + str(port))
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connected = False
while not connected:
try:
sock.connect((NetAddr, port))
sock.settimeout(2)
print ("Slave " +str(i) + " connected to " + NetAddr)
connected = True
except Exception:
pass
return sock
def connectSlaves(Slaves):
for s in Slaves:
SlaveArray = Slaves[s]
i = SlaveArray[0]
SlaveArray[1] = connSlave(s,i)
Slaves[s] = SlaveArray
print("\n------------------------------------------")
return Slaves
def closeSlaves(Slaves):
print()
for s in Slaves:
SlaveArray = Slaves[s]
print ("Closing slave " + str(SlaveArray[0]) + " : " + s)
SlaveArray[1].close()
def relay(Slaves):
HOST = ''
sockH = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sockH.bind((HOST, PORT))
sockH.listen(1)
Host = connHost(sockH)
Slaves = connectSlaves(Slaves)
print ("Connected to Modbus devices")
while 1:
try:
dataOut = Host.recv(1024)
except socket.timeout:
print ('Timeout')
Host.close()
Host = connHost(sockH)
break
except Exception as e:
print("Exception")
print(e)
break
if not dataOut: break
printInline('%15s %30s' %('Host: ',repr(dataOut)))
modSlave = dataOut[6]
for s in Slaves:
SlaveArray = Slaves[s]
i = SlaveArray[0]
if str(modSlave) == str(SlaveArray[0]):
try:
SlaveArray[1].send(dataOut)
except Exception:
closeSlaves(Slaves)
Slaves = connectSlaves(Slaves)
try:
dataIn= SlaveArray[1].recv(1024)
printInline('%15s %30s' %(s +': ',repr(dataIn)))
if len(dataIn) < 100:
Host.send(dataIn)
except Exception:
closeSlaves(Slaves)
Slaves = connectSlaves(Slaves)
Host.close()
closeSlaves(Slaves)
def _main():
global pkgCtr
global testClients
global quiet
Slaves = {}
testClients = False
quiet = False
pkgCtr = 1
if len(sys.argv)== 1:
print("\n\nAufruf: mmb.y [-test] [-quiet] Net-Address1 [Net-Address2] ... [NetAddress(n)]\n\n")
while 1: pass
else:
CntSlaves = 0
for i in range(1, len(sys.argv)):
if sys.argv[i] == "-test":
testClients = True
print("Option: Test")
if sys.argv[i] == "-quiet":
quiet = True
print("Option: Quiet")
if not sys.argv[i][0] == "-":
CntSlaves += 1
NetAddr = sys.argv[i]
SlaveArray = [CntSlaves,"x"]
Slaves[NetAddr] = SlaveArray
relay(Slaves)
if __name__=='__main__':
_main()