1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 <2018> 2019 2020 2021 2022 2023 2024 2025 | Index | 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 <2018> 2019 2020 2021 2022 2023 2024 2025 |
<== Date ==> | <== Thread ==> |
---|
Subject: | RE: asyn IP server port driver with streamDevice |
From: | Mark Rivers <[email protected]> |
To: | Rod Nussbaumer <[email protected]>, EPICS Mailing list <[email protected]> |
Date: | Thu, 25 Jan 2018 23:07:17 +0000 |
Hi Rod, I have changed the drvAsynIPServer port driver so that it should work for your application. This is the commit log: ********************************* commit 94e018a7e52b94a01a78ad246d9d97ac87744d59 Author: Mark Rivers <[email protected]> Date: Thu Jan 25 16:34:25 2018 -0600 Changed the logic so it creates maxClients drvAsynIPPort drivers at initialization rather than on-demand when clients connect. This makes it possible to use these ports before iocInit, which is much more useful. They can be used in input and output links in EPICS records, for example. This change should be backwards compatible, since it was transparent when the ports were created. The first drvAsynIPPort created is now named PORT_NAME:0 rather than PORT_NAME:1, where PORT_NAME is the name of the drvAsynIPServer port created by this driver. This might break backwards compatibility, but most clients were getting the name from a callback, so probably not. Set the socket option SO_REUSEADDR when creating the listening socket. Previously if a client was connected and the IOC exited it could not be restarted again immediately. One needed to wait for the OS to time out the connection. This change allows the IOC to be run again with the same listening port immediately, without waiting for the OS timeout. Added an exit handler to close the listen socket and delete the private structure. Improved the report() function so it shows all the drvAsynIPPort drivers that were created. ********************************* I added a new database, testIPServerApp/Db/testIPServer1.db. It contains a stringin and a stringout record. record(stringin,"$(P)stringInput") { field(DTYP, "asynOctetRead") field(INP, "@asyn($(PORT),0,1)") } record(stringout,"$(P)stringOutput") { field(DTYP, "asynOctetWrite") field(OUT, "@asyn($(PORT),0,1)") I added a new startup script, iocBoot/ioctestIPServer/st.cmd.testIPServer1. This creates a drvAsynIPServerPort called P5001 that listens for connections on localhost:5001. It creates a normal drvAsynIPPort called P5001:0 that will be
connected to a TCP client that connects to localhost:5000. It loads the testIPServer1.db database shown above. I have tested that it works correctly. If I telnet to localhost:5001, type some text and process the stringInput record it contains the text I typed in telnet. Similarly if I do a caput of some text to the stringOutput record then that
text appears in the telnet session. I have not yet had a chance to test with StreamDevice, but I am quite sure it will work, since P5001:0 is just a normal drvAsynIPPort. The new code is on Github
https://github.com/epics-modules/asyn in the new_drvAsynIPServerPort branch, Please test when you get a chance and let me know if it works for you. Mark From: Mark Rivers
Hi Rod, The existing implementation of drvAsynIPServer port works as follows, as documented in
https://epics.anl.gov/modules/soft/asyn/R4-32/asynDriver.html *************************************** This driver implements the asynOctet interface. The only methods it supports are registerInterruptUser and cancelInterruptUser. Calling the other asynOctet
methods will result in an error. The following happens when a new connection is received on the port specified in drvAsynIPServerPortConfigure:
*************************************** So the way it works is that drvAsynIPServer port creates a thread that listens for incoming connections on the specified TCP port. The first time a connection is received it creates a drvAsynIPPort that is connected to the file description
for that connection. It is this drvAsynIPPort that you need to pass to StreamDevice. The problem is that in the current implementation that devAsynIPPort is only created when the first connection is received. This is not what you want, because typically that won’t happen until after iocInit, when it is too late for your
application. You need the port to exist before iocInit the StreamDevice INP and OUT links will connect at iocInit. Here is a simple demonstration of the existing implementation using the testAsynIPServerApp application in asyn. It creates a drvAsynIPServer port called P5001 listening on TCP port 5001. It accepts a single incoming connection. corvette:asyn/iocBoot/ioctestIPServer>../../bin/linux-x86_64/testIPServer st.cmd.test < envPaths epicsEnvSet("IOC","ioctestIPServer") epicsEnvSet("TOP","/home/epics/devel/asyn-4-33") epicsEnvSet("SUPPORT","/corvette/home/epics/devel") epicsEnvSet("IPAC","/corvette/home/epics/devel/ipac-2-14") epicsEnvSet("SNCSEQ","/corvette/home/epics/devel/seq-2-2-5") epicsEnvSet("EPICS_BASE","/corvette/usr/local/epics-devel/base-7.0.1") dbLoadDatabase("../../dbd/testIPServer.dbd") testIPServer_registerRecordDeviceDriver(pdbbase) #The following command starts a server on port 5001 drvAsynIPServerPortConfigure("P5001","localhost:5001",1,0,0,0) serverPort: 5001 #asynSetTraceFile("P5001",-1,"") asynSetTraceIOMask("P5001",-1,0x2) #asynSetTraceMask("P5001",-1,0xff) iocInit() Starting iocInit ############################################################################ ## EPICS R7.0.1.1 ## EPICS Base built Jan 3 2018 ############################################################################ iocRun: All initialization complete ipEchoServer("P5001") epics> I now run asynReport to show all asynPortDrivers in this IOC. Note that there is only 1, P5001. epics> asynReport 1 P5001 multiDevice:No canBlock:Yes autoConnect:Yes enabled:Yes connected:Yes numberConnects 1 nDevices 0 nQueued 0 blocked:No asynManagerLock:No synchronousLock:No exceptionActive:No exceptionUsers 0 exceptionNotifys 0 traceMask:0x1 traceIOMask:0x2 traceInfoMask:0x1 Port P5001: Connected fd: 4 Max. clients: 1 Num. clients: 0 In another terminal session I executed the command “ telnet localhost 5001”. That caused a new drvAsynIPPort port (P5001:1) to be created, as shown by asynReport. epics> asynReport 1 P5001 multiDevice:No canBlock:Yes autoConnect:Yes enabled:Yes connected:Yes numberConnects 1 nDevices 0 nQueued 0 blocked:No asynManagerLock:No synchronousLock:No exceptionActive:No exceptionUsers 0 exceptionNotifys 0 traceMask:0x1 traceIOMask:0x2 traceInfoMask:0x1 Port P5001: Connected fd: 4 Max. clients: 1 Num. clients: 1 P5001:1 multiDevice:No canBlock:Yes autoConnect:No enabled:Yes connected:Yes numberConnects 1 nDevices 0 nQueued 0 blocked:No asynManagerLock:No synchronousLock:Yes exceptionActive:No exceptionUsers 1 exceptionNotifys 0 traceMask:0x1 traceIOMask:0x2 traceInfoMask:0x1 Port localhost:5001: Connected StreamDevice could now connect to that port and it should do what you want. But as I said above, it is too late, that port needed to exist before iocInit. I think the solution is simple. drvAsynIPServer port should be changed so that it creates the drvAsynIPPorts in its constructor, before any connections are made. It will be in the “disconnected” state. Once a connection comes in it will
connect to that file description. I think this change makes a lot of sense, since it makes things simpler. I will make this change and push to a branch on Github where you can test it. Once we are happy I will poll tech-talk to see if anyone is using drvAsynIPServerPort and would object to this change. It would require a small change to their
application. If there is no objection I will replace drvAsynIPServerPort. If there is objection I will call the new driver something else. Mark From: Mark Rivers
Hi Rod, I’ve looked at this more closely, and my initial answer is incorrect. This is not how it works. I need to think about how it could work for your use case. Mark Sent from my iPhone
|