opc server 开发心得

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/carelg/article/details/5490344

这段时间公司项目上需要把数据通过OPC SERVER 开放接口,在网上及CSDN上 SEARCH 了好久,感觉CSDN上的资料还是比较专业的,但也有做广告的,我摸索了一周时间,有点心得,并用在工程上,一下是一周体会;

一:WINTECH 的开发包

WINTECH 网站上可下载开发包,WTOPCSVR.DLL 有VB VC VC#的例程,但是DEMO DLL,可运行30分钟,比较容易上手,但对开发者来说不能从内核上了解OPC SERVER ,而且需要银子。

二:南京一位高手写的 OPC 开发指南,并且提供SERVER 代码,代码是可以编译通过的,但是用客户机程序测试,连接也成功,就是添加TAG时提示找系统保留项目,ERROR,我用远程需跟踪了一下,是ADDTIEM 函数中XXXPAN 指针为空,研究一天,没有结果,放弃。说实话书写的确实挺实用的。

三:开源LIGHTOPC 开发

CSDN上下载俄罗斯高手开源LIGHTOPC源代码,很好能编译过去,两个DLL,头文件及LIB库,同时又下载了高手写的MODBUSTOOPC MFC程序,用的就是LIGHTOPC 支持,说实话,MFC是十分灵活,但开发UI就差强人意了,CLOCLEMODBUS 及CMODBUS 两个RTU方式通讯堪称经典,几乎就是MODBUS 的核心,核心是myClassFactory类及opcsrv.cpp ,详细的代码描述了如何引用LIGHTOPC服务器,关键函数是int driver_init(int lflags)初始化TAG及work_loop(unsigned pause, unsigned *nTerminate) 刷新TAG数据,我们只要更改这两个函数的内容就可以将自己的数据放入OPC SERVER ,其他OPC CLIENT 如KEPWARE 及组态王等就可取得我们的数据了。

下面是两个函数的引用

//初始化ITEM TAGS
int driver_init(int lflags)
{
    loDriver ld;
    VARIANT var;
    int ecode;
   
    setlocale(LC_CTYPE, "");
   
    if (my_service)
    {
        UL_ERROR((LOGID, "Driver already initialized!"));
        return 0;
    }
   
    memset(&ld, 0, sizeof(ld));   /* basic server parameters: */
    //  ld.ldRefreshRate = 3;//10;
    ld.ldSubscribe = activation_monitor;
    ld.ldWriteTags = WriteTags;
    ld.ldReadTags = ReadTags;
    ld.ldConvertTags = ConvertTags;
#if 0
    ld.ldAskItemID = AskItemID;
#endif
    ld.ldFlags = lflags | loDF_IGNCASE |  /*loDf_FREEMARSH | loDf_BOTHMODEL | */
    /*loDF_NOCOMP| */ loDf_NOFORCE & 0 /*| loDF_SUBSCRIBE_RAW*/;
    /*Fix the Bug in ProTool *//*|loDF_IGNCASE */ ;
    ld.ldBranchSep = '/'; /* Hierarchial branch separator */
   
    ecode = loServiceCreate(&my_service, &ld, TI_MAX /* number of tags in the cache */);
    /* 500000 is ok too */
    UL_TRACE((LOGID, "%!e loCreate()=", ecode));
    if (ecode) return -1;
   
    InitializeCriticalSection(&lk_values);
    memset(tv, 0, sizeof(tv));    /* instead of VariantInit() for each of them */
    memset(ta, 0, sizeof(ta));    /* instead of VariantInit() for each of them */
   
    VariantInit(&var);
   
    /* OPTIONAL: Tags creation. */
    {
        char name[64];
        unsigned int ii, i;
        memset(name, 0, sizeof(name));

        V_I2(&var) = 0;
        V_VT(&var) = VT_I2; //all tags are 2-byte integers
        ii = i = 0;
        for(; ii < i + tnum[TI_holding_register]  && !ecode; ii++)
        {
            sprintf(name, "%s%s%02u", ttn, tcn[TI_holding_register], ii-i);
           
            V_I2(&var) = 0;
            V_VT(&var) = VT_I2;
            ecode = loAddRealTag(my_service,      /* actual service context */
                &ti[ii+1],    /* returned TagId */
                (loRealTag) (ii+1),     /* != 0 driver's key */
                name,     /* tag name */
                0,       /* loTF_ Flags */
                OPC_READABLE | OPC_WRITEABLE, &var, 0, 0);
            tg[ii+1] = TI_holding_register;
            tin[ii+1] = ii-i;
        }
        UL_TRACE((LOGID, "%!e num of loAddRealTag(%s) = %u ", ecode, tcn[TI_holding_register], ii-i));
        LogMsg( 0, "Added %d tags to group %s ", ii-i, tcn[TI_holding_register] );
       
       
        i = ii;   
        for(; ii < i+ tnum[TI_output_coil]  && !ecode; ii++)
        {
            sprintf(name, "%s%s%02u", ttn, tcn[TI_output_coil], ii);
           
            ecode = loAddRealTag(my_service,      /* actual service context */
                &ti[ii+1],    /* returned TagId */
                (loRealTag)(ii+1),  /* != 0 driver's key */
                name,     /* tag name */
                0,       /* loTF_ Flags */
                OPC_READABLE | OPC_WRITEABLE, &var, 0, 0);
            tg[ii+1] = TI_output_coil;
            tin[ii+1] = ii-i;
        }
        UL_TRACE((LOGID, "%!e num of loAddRealTag(%s) = %u ", ecode, tcn[TI_output_coil], ii-i));
        LogMsg( 0, "Added %d tags to group %s ", ii-i, tcn[TI_output_coil] );

        i = ii;   
        for(; ii < i + tnum[TI_input_status]  && !ecode; ii++)
        {
            sprintf(name, "%s%s%02u", ttn, tcn[TI_input_status], ii);
           
            V_I2(&var) = 0;
            V_VT(&var) = VT_I2;
            ecode = loAddRealTag(my_service,      /* actual service context */
                &ti[ii+1],    /* returned TagId */
                (loRealTag) (ii+1),     /* != 0 driver's key */
                name,     /* tag name */
                0,       /* loTF_ Flags */
                OPC_READABLE, &var, 0, 0);
            tg[ii+1] = TI_input_status;
            tin[ii+1] = ii-i;
        }
        UL_TRACE((LOGID, "%!e num of loAddRealTag(%s) = %u ", ecode, tcn[TI_input_status], ii-i));
        LogMsg( 0, "Added %d tags to group %s ", ii-i, tcn[TI_input_status] );

        i = ii;   
        for(; ii < i + tnum[TI_input_register]  && !ecode; ii++)
        {
            sprintf(name, "%s%s%02u", ttn, tcn[TI_input_register], ii);
           
            V_I2(&var) = 0;
            V_VT(&var) = VT_I2;
            ecode = loAddRealTag(my_service,      /* actual service context */
                &ti[ii+1],    /* returned TagId */
                (loRealTag) (ii+1),     /* != 0 driver's key */
                name,     /* tag name */
                0,       /* loTF_ Flags */
                OPC_READABLE, &var, 0, 0);
            tg[ii+1] = TI_input_register;
            tin[ii+1] = ii-i;
        }
        UL_TRACE((LOGID, "%!e num of loAddRealTag(%s) = %u ", ecode, tcn[TI_input_register], ii-i));
        LogMsg( 0, "Added %d tags to group %s ", ii-i, tcn[TI_input_register] );
    }
   
    return 0;
}


void driver_destroy(void)
{
    if (my_service)
    {
        int ecode = loServiceDestroy(my_service);
        UL_INFO((LOGID, "%!e loDelete(%p) = ", ecode));
       
        for(ecode = 0; ecode < sizeof(tv) / sizeof(tv[0]); ecode++)
            VariantClear(&tv[ecode].tvValue);
       
        DeleteCriticalSection(&lk_values);
       
        my_service = 0;
    }
}

/********* Data workloop stuff ************************************************/

void work_loop(unsigned pause, unsigned *nTerminate)
{
    DWORD hitime = 0;
    unsigned starttime = GetTickCount();
    FILETIME ft;
    GetSystemTimeAsFileTime(&ft); /* awoke */
    UL_WARNING((LOGID, "Workloop Started..."));
   
    EnterCriticalSection(&lk_values);

    /* Set up initial values for the tags we didn't initialized in driver_init(): */
    int ii = 0;
    for (ii = 0; ii < TI_MAX;ii++)
    {
        tv[ii+1].tvTi = ti[ii+1];
        tv[ii+1].tvState.tsError = S_OK;
        tv[ii+1].tvState.tsQuality = OPC_QUALITY_UNCERTAIN;
        V_VT(&tv[ii+1].tvValue) = VT_I2;
        //V_I2(&tv[ii+1].tvValue) = 0;
        V_I2(&tv[ii+1].tvValue) = ii;
        tv[ii+1].tvState.tsTime = ft;
    }
    loCacheUpdate(my_service, TI_MAX, tv, 0);
    LeaveCriticalSection(&lk_values);
   
    /**** Then do simulate ***********/
   
    while(0 != my_CF.server_inuse && !(*nTerminate) ) /* still working? */
    {
        Sleep(10);
        GetSystemTimeAsFileTime(&ft); /* awoke */

        /* OPTIONAL: reload log's configuration fromtime to time */
        if (hitime != (ft.dwLowDateTime & 0xf8000000))    /* 13.5 sec */
        {                       /* 0xff000000 is about 1.67 sec */
            hitime = ft.dwLowDateTime & 0xf8000000;
#if 0
            unilog_Refresh(0);    /* all logs */
#else
            unilog_Refresh("XXX-OPC");
            unilog_Refresh("XXX-OPC-MODBUS-EXE");
#endif
        }
       
        //The main job: poll data from PLC, update the values
        /*int ii = 0;
        for (ii = 0; ii < TI_MAX; )
        {
            if ( !ta[ii+1] )
            {
                ii++;
                continue;
            }
            //read 10 words a time, speed up polling
            EnterCriticalSection(&lk_values);
            WORD wa[10]={0};
            if ( GetDrvData10( DEVICE_ID, tg[ii+1], tin[ii+1], wa ))
            {
                for ( int i = 0 ; i < 10; i++)
                {
                    V_I2(&tv[ii+1+i].tvValue) = (short)wa[i];
                    V_VT(&tv[ii+1+i].tvValue) = VT_I2;
                    tv[ii+1+i].tvState.tsQuality = OPC_QUALITY_GOOD;
                    tv[ii+1+i].tvState.tsError = S_OK;
                    tv[ii+1+i].tvState.tsTime = ft;
                }
            }
            else
            {
                for ( int i = 0 ; i < 10; i++)
                {
                    V_VT(&tv[ii+1+i].tvValue) = VT_EMPTY;
                    tv[ii+1+i].tvState.tsQuality = OPC_QUALITY_DEVICE_FAILURE;
                    tv[ii+1+i].tvState.tsError = S_FALSE;
                    tv[ii+1+i].tvState.tsTime = ft;
                }
            }
            ii += 10;
            // MANDATORY: send all the values into the cache:
            loCacheUpdate(my_service, TI_MAX, tv, 0);
            LeaveCriticalSection(&lk_values);
            Sleep(10);   // main delay
        }*/  
        int ii = 0;
        for (ii = 0; ii < TI_MAX; )
        {
            if ( !ta[ii+1] )
            {
                ii++;
                continue;
            }
            //read 10 words a time, speed up polling
            EnterCriticalSection(&lk_values);
            WORD wa[10]={0};
            for ( int i = 0 ; i < 10; i++)
            {
                srand(time(NULL));//因为这个地方要以时间为轴作种子 
                wa[i]=(WORD)rand()%100; //这里表示产生0~9的随机数 
                V_I2(&tv[ii+1+i].tvValue) = (short)wa[i];
                V_VT(&tv[ii+1+i].tvValue) = VT_I2;
                tv[ii+1+i].tvState.tsQuality = OPC_QUALITY_GOOD;
                tv[ii+1+i].tvState.tsError = S_OK;
                tv[ii+1+i].tvState.tsTime = ft;
            }
          
            ii += 10;
            // MANDATORY: send all the values into the cache:
            loCacheUpdate(my_service, TI_MAX, tv, 0);
            LeaveCriticalSection(&lk_values);
            Sleep(10);   // main delay
        }  
        if (pause)
        {
        /* IMPORTANT: force unloading of an out-of-proc
            if not connected during PAUSE millisec */
            unsigned tnow = GetTickCount();
            if (tnow - starttime >= pause)
            {
                pause = 0;
                my_CF.serverRemove();
            }
        }
    } /* end of loop */
   
    if (0 != my_CF.server_inuse && (*nTerminate) )
    {
        loSetState( my_service, 0, loOP_SHUTDOWN, 0, "User aborted" );
    }
    if ( *nTerminate )
        UL_MESSAGE((LOGID, "Aborting..."));
    else
        UL_MESSAGE((LOGID, "All clean. exiting..."));
}


/***************************************************************************
EXE-specefic stuff
***************************************************************************/

const char eClsidName[] = "XXX OPC Server for Modbus";
const char eProgID[] = "XXX.OPC.MODBUS";

HMODULE server_module = 0;
int callsvr(HINSTANCE hInstance, unsigned *nTerminate, BOOL bEmbedding, unsigned nRegister );


int callsvr(HINSTANCE hInstance, unsigned *nTerminate, BOOL bEmbedding, unsigned nRegister )
{
    int main_rc = 0;
    DWORD objid;
    int daemon = 0, pause = 20;
    const char *exit_msg = "Exiting...";
    server_module = hInstance;
   
    log = unilog_Create("XXX-OPC-exe", "|XXX-OPC-exe", "%!T", -1,       /* Max filesize: -1 unlimited, -2 -don't change */
        ll_MESSAGE);        /* level [ll_FATAL...ll_DEBUG] */
    unilog_Redirect("XXX-OPC-exe", "XXX-OPC", 0);
    unilog_Delete(log);
    log = unilog_Create("XXX-OPC-MODBUS-EXE", "|XXX-OPC-MODBUS-EXE", "", -1,    /* Max filesize: -1 unlimited, -2 -don't change */
        ll_TRACE);        /* level [ll_FATAL...ll_DEBUG] */
    //UL_DEBUG((LOGID, "WinMain(%s) invoked...", argv[0]));
   
    if ( 1 == nRegister )
    {
        char np[FILENAME_MAX + 32];
        GetModuleFileName(NULL, np + 1, sizeof(np) - 8);
        np[0] = '"'; strcat(np, "/"");
       
        if (loServerRegister(&CLSID_XXXOPCServerEXE,
            eProgID, eClsidName, np, 0))
        {
            UL_ERROR((LOGID, "%!L Reg <%s> <%s> Failed", eProgID, np));
            main_rc = 1;
           
            LogMsg( 0, "Failed to register server to system" );
        }
        else
        {
            UL_INFO((LOGID, "Reg <%s> <%s> Ok", eProgID, np));
            LogMsg( 0, "Registered server to system" );           
        }
    }
    else if ( 2 == nRegister )
    {
        if (loServerUnregister(&CLSID_XXXOPCServerEXE, eProgID))
        {
            UL_WARNING((LOGID, "%!L UnReg <%s>  Failed", eProgID));
            main_rc = 1;
        }
        else
        {
            UL_DEBUG((LOGID, "UnReg <%s> Ok", eProgID));
        }
    }

    /* Attempt to start the server */
   
    my_CF.is_out_of_proc = my_CF.server_inuse = 1;
   
    if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED)))
    {
        exit_msg = "CoInitializeEx() failed. Exiting...";
        UL_ERROR((LOGID, exit_msg));
        goto Finish;
    }
    if ( bEmbedding )
        pause = 6; /* 3 sec can be too short for remote connections */
    else
        pause = 0;
   
    if (driver_init(0))
    {
        exit_msg = "driver_init() failed. Exiting...";
        UL_ERROR((LOGID, exit_msg));
    }
    else if (FAILED(CoRegisterClassObject(CLSID_XXXOPCServerEXE, &my_CF,
        CLSCTX_LOCAL_SERVER |
        CLSCTX_REMOTE_SERVER |
        CLSCTX_INPROC_SERVER,
        REGCLS_MULTIPLEUSE, &objid)))
    {
        exit_msg = "CoRegisterClassObject() failed. Exiting...";
        UL_ERROR((LOGID, exit_msg));
    }
    else
    {
        if (daemon)
            pause = 0;          // infinite
        my_CF.serverAdd(); /* Oops. This will prewent our server from unloading
            till PAUSE elapsed and simulate() do my_CF.serverRemove() */
        /* STARTing the workloop */
        work_loop(pause * 1000, nTerminate); /* sec -> millisec; */
        /* FINISHED */
        if (FAILED(CoRevokeClassObject(objid)))
            UL_WARNING((LOGID, "CoRevokeClassObject() failed..."));
    }
    driver_destroy();
    CoUninitialize();
   
Finish:
    LogMsg( 0, "Exiting...");
   
    unilog_Delete(log);
    log = 0;
    return main_rc;
}
/******************* This is the end... ************************************/

感谢开源的俄罗斯哥们

感谢MODBUSTOOPC 开发者

感谢CSDN 下载,就是商业味太浓了,积分太少,上CSDN下资料的基本是学习及工作用的,不要太商业了,我反对。

李广,徐州恺尔电子设备公司 开发工程师

OPC CLIENT 比较简单,网上源码一堆,但是C#OPCAUTOMATION.DLL的引用提示创建组错误,按照网上提示安装KEPWARE SERVEREX后正常,是什么原因,同样代码在VB6上很好,这是谁的BUG ,难道是VS2005,????????

 

 

 

展开阅读全文

没有更多推荐了,返回首页