EPICS asyn连接测试例程

1) 用makeBaseApp.pl新建一个EPICS IOC的目录结构:

root@orangepi5:/usr/local/EPICS/program/testConnect# ls
configure  iocBoot  Makefile  testConnectApp

2) 编辑configure目录下的RELEASE文件,定义asyn模块所在的路径:

...
SUPPORT=/usr/local/EPICS/synApps/support
ASYN=$(SUPPORT)/asyn
...

3) 进入testConnectApp目录下的src目录,编写用于连接测试的c++源程序myConnect.cpp:

此程序运行时,每个1秒,向网络输出一个"Hello World"字符串并且从网络回读一个字符串。

/* myConnect derived from asynPortDriver to test Connect */
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "epicsThread.h"
#include "epicsString.h"
#include "iocsh.h"
#include "asynPortDriver.h"
#include "asynOctetSyncIO.h"
#include "epicsExport.h"

static const char * driverName = "myConnect";

#define POLLER_PERIOD 1
#define MAX_RESPONSE_LEN 256

class myConnect : public asynPortDriver {
public:
        myConnect(const char * portName, const char * IPPortName, const char * outputString);
        void pollerTask(void);

private:
        asynUser * pasynUserIPPort_; // asynUser to connect to this driver
        const char * outputString_; // used for storing the input string
};


static void pollerTask(void * drvPvt);

myConnect::myConnect(const char * portName, const char * IPPortName, const char * outputString)
        : asynPortDriver(portName, 1, asynOctetMask, 0, ASYN_CANBLOCK, 1, 0, 0)
{
        asynStatus status;
        const char * functionName = "myConnect";

        // copy the input string, and point to the copy
        outputString_ = epicsStrDup(outputString);
        // connect to the driver and get the asynUser
        // parameters:1) char *port, 2)int addr,  3)asynUser **ppasynUser, 4) const char *drvInfo
        status = pasynOctetSyncIO->connect(IPPortName, 0, &pasynUserIPPort_, NULL);

        if (status)
        {
                printf("%s:%s:pasynOctetSyncIO->connect failure, status = %d\n", driverName, functionName, status);
                return;
        }

        // if NULL, create epicsThread failed
        status = (asynStatus)(epicsThreadCreate("myConnectTask", epicsThreadPriorityMedium,
                        epicsThreadGetStackSize(epicsThreadStackMedium),
                        (EPICSTHREADFUNC)::pollerTask, this) == NULL);

        if (status){
                printf("%s:%s: epicsThreadCreate failure, status: %d\n", driverName, functionName, status);
                return;
        }
}

static void pollerTask(void * drvPvt)
{
        myConnect * pPvt = (myConnect *)drvPvt;

        pPvt->pollerTask();
}

void myConnect::pollerTask(void)
{
        asynStatus status;
        char response[MAX_RESPONSE_LEN];
        size_t numWrite, numRead;
        int isConnected;
        int eomReason;
        static const char * functionName = "pollerTask";

        while (1){
                lock();
                status = pasynManager->isConnected(pasynUserIPPort_, &isConnected);

                if (status){
                        // pasynUserSelf used to connect to ourselves for asynTrace, it defined in asynPortDriver
                        asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: error calling pasynManager->isConnected, status=%d, error=%s\n",
                                        driverName, functionName, status, pasynUserIPPort_->errorMessage);
                }

                asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: isConnected = %d\n",
                                driverName, functionName, isConnected);

                status = pasynOctetSyncIO->writeRead(pasynUserIPPort_, outputString_, strlen(outputString_),
                                response, sizeof(response), 1.0, &numWrite, &numRead, &eomReason);

                if (status){
                        asynPrint(pasynUserSelf, ASYN_TRACE_ERROR, "%s:%s: error calling pasynOctetSyncIO->writeRead, status=%d, error=%s\n",
                                        driverName, functionName, status, pasynUserIPPort_->errorMessage);
                }
                else{
                        asynPrint(pasynUserSelf, ASYN_TRACEIO_DRIVER, "%s:%s: numWrite=%ld, wrote:%s, numRead=%ld, response=%s\n",
                                        driverName, functionName, (long)numWrite, outputString_, (long)numRead, response);
                }
                unlock();
                epicsThreadSleep(POLLER_PERIOD);
        }

}

extern "C"{
int myConnectConfigure(const char * portName, const char * IPPortName, const char * outputString)
{
        new myConnect(portName, IPPortName, outputString);
        return asynSuccess;
}

/*  EPICS iocsh shell commands */
static const iocshArg initArg0 = {"portName", iocshArgString};
static const iocshArg initArg1 = {"IPPortName", iocshArgString};
static const iocshArg initArg2 = {"output string", iocshArgString};
static const iocshArg * const initArgs[]  = {&initArg0, &initArg1, &initArg2};
static const iocshFuncDef initFuncDef = {"myConnectConfigure", 3, initArgs};
static void initCallFunc(const iocshArgBuf * args)
{
        myConnectConfigure(args[0].sval, args[1].sval, args[2].sval);
}

void myConnectRegister(void)
{
        iocshRegister(&initFuncDef, initCallFunc);
}

epicsExportRegistrar(myConnectRegister);
}

4)编写3)步骤中编写源程序对应的支持文件myConnect.dbd:

registrar(myConnectRegister)

5)编写与3)和4)相同路径下的Makefile文件,添加以下内容:

...
# support dbd file
testConnect_DBD += asyn.dbd
testConnect_DBD += drvAsynIPPort.dbd
testConnect_DBD += myConnect.dbd


# Add all the support libraries needed by this IOC
testConnect_LIBS += asyn

# source files to be compiled
testConnect_SRCS += myConnect.cpp

...

6) 回到顶层目录,执行make,进行编译。

7)进入IOC启动目录,编辑启动文件st.cmd:

#!../../bin/linux-aarch64/testConnect

#- You may have to change testConnect to something else
#- everywhere it appears in this file

< envPaths

cd "${TOP}"

## Register all support components
dbLoadDatabase "dbd/testConnect.dbd"
testConnect_registerRecordDeviceDriver pdbbase


# This is a real IP address that will connect
drvAsynIPPortConfigure("IPPort", "192.168.3.208:9999", 0, 0, 1);
myConnectConfigure("MYPORT", "IPPort", "Hello World")
#asynSetTraceMask("PORT1",0,9)
asynSetTraceIOMask("MYPORT",0,0x2)

dbLoadRecords("$(ASYN)/db/asynRecord.db","P=testConnect:,R=asyn1,PORT=MYPORT,ADDR=0,OMAX=80,IMAX=80")


cd "${TOP}/iocBoot/${IOC}"
iocInit

8) 编写一个python tcp服务程序,用于对测试以上连接程序:

此程序运行时,监听网络,接收一个套接字连接后建立TCP连接,从连接套接字读取字符串,并且在这个字符串前加上Hello后,重新发送给连接套接字。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import socket
import threading
import time

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

s.bind(('0.0.0.0', 9999))

s.listen(5)
print('Waiting for connection...')

def tcplink(sock, addr):
    print('Accept new connection from %s:%s...' %addr)
    sock.send(b'Welcome!')
    while True:
        data = sock.recv(1024)
        print(data)
        time.sleep(0.2)
        if not data or data.decode('utf-8') == 'exit':
            break
        sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
    sock.close()
    print('Connection from %s:%s closed.' %addr)


while True:
    sock, addr = s.accept()
    t = threading.Thread(target=tcplink, args=(sock, addr))
    t.start()

9)启动以上python程序,等待连接:

orangepi@orangepizero2:~/python$ ./tcpserver.py
Waiting for connection...

10) 启动IOC程序:

root@orangepi5:/usr/local/EPICS/program/testConnect/iocBoot/ioctestConnect# ../../bin/linux-aarch64/testConnect st.cmd
#!../../bin/linux-aarch64/testConnect
< envPaths
epicsEnvSet("IOC","ioctestConnect")
epicsEnvSet("TOP","/usr/local/EPICS/program/testConnect")
epicsEnvSet("SUPPORT","/usr/local/EPICS/synApps/support")
epicsEnvSet("ASYN","/usr/local/EPICS/synApps/support/asyn")
epicsEnvSet("EPICS_BASE","/usr/local/EPICS/base")
cd "/usr/local/EPICS/program/testConnect"
## Register all support components
dbLoadDatabase "dbd/testConnect.dbd"
testConnect_registerRecordDeviceDriver pdbbase
# This is a real IP address that will connect
drvAsynIPPortConfigure("IPPort", "192.168.3.208:9999", 0, 0, 1);
myConnectConfigure("MYPORT", "IPPort", "Hello World")
#asynSetTraceMask("PORT1",0,9)
asynSetTraceIOMask("MYPORT",0,0x2)
dbLoadRecords("/usr/local/EPICS/synApps/support/asyn/db/asynRecord.db","P=testConnect:,R=asyn1,PORT=MYPORT,ADDR=0,OMAX=80,IMAX=80")
cd "/usr/local/EPICS/program/testConnect/iocBoot/ioctestConnect"
iocInit
Starting iocInit
############################################################################
## EPICS R7.0.7
## Rev. 2023-05-26T09:07+0000
## Rev. Date build date/time:
############################################################################
iocRun: All initialization complete
## Start any sequence programs
#seq sncxxx,"user=orangepi"
epics> dbl
testConnect:asyn1
epics>

11) 观察python tcp服务程序的输出,不断接收到"Hello World"

orangepi@orangepizero2:~/python$ ./tcpserver.py
Waiting for connection...
Accept new connection from 192.168.3.191:59798...
b'Hello World'
b'Hello World'
b'Hello World'
b'Hello World'
b'Hello World'
b'Hello World'
...

13) 启动asynRecord.adl界面:

medm -x -macro "P=testConnect:,R=asyn1"  asynRecord.adl &

 启动traceIODriver选项,可以观察到服务器发送给这个连接测试程序的应答字符串:

epics> 2023/08/24 11:13:15.131 myConnect:pollerTask: isConnected = 1
2023/08/24 11:13:15.334 myConnect:pollerTask: numWrite=11, wrote:Hello World, numRead=19, response=Hello, Hello World!
2023/08/24 11:13:16.335 myConnect:pollerTask: isConnected = 1
2023/08/24 11:13:16.537 myConnect:pollerTask: numWrite=11, wrote:Hello World, numRead=19, response=Hello, Hello World!
2023/08/24 11:13:17.538 myConnect:pollerTask: isConnected = 1
2023/08/24 11:13:17.741 myConnect:pollerTask: numWrite=11, wrote:Hello World, numRead=19, response=Hello, Hello World!
...

启动traceError选项后,断开IOC所在主机的网络后,可以观察到IOC的报错信息:

2023/08/24 11:15:33.378 myConnect:pollerTask: error calling pasynOctetSyncIO->writeRead, status=1, error=192.168.3.208:9999 timeout: Resource temporarily unavailable
2023/08/24 11:15:35.380 myConnect:pollerTask: error calling pasynOctetSyncIO->writeRead, status=1, error=192.168.3.208:9999 timeout: Resource temporarily unavailable
...
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值