openscada 経由で OPC サーバーに接続しようとしています。
MatrikonOPC エクスプローラーによって提供された OPC サーバー プログラム ID は既に知っていますが、おそらく DCOM 構成が間違っているために接続が機能しません。先に進む前に、サーバー CLSID 経由で接続を試みたいと思います。
OPC サーバーの CLSID を確認するにはどうすればよいですか?
openscada 経由で OPC サーバーに接続しようとしています。
MatrikonOPC エクスプローラーによって提供された OPC サーバー プログラム ID は既に知っていますが、おそらく DCOM 構成が間違っているために接続が機能しません。先に進む前に、サーバー CLSID 経由で接続を試みたいと思います。
OPC サーバーの CLSID を確認するにはどうすればよいですか?
私は OpenSCADA に精通していません。しかし、私は C++ で OPC クライアントを開発しました。ローカルで実行されている OPC サーバーのクラス ID を探している場合は、次の関数を使用できます。
CLSID clsid;
hr = CLSIDFromProgID(L"Matrikon.OPC.Simulation", &clsid);
printf("OPC Server clsid: %p %p %p %p%p%p%p%p%p%p%p\n", (void*)opcServerId.Data1, (void*)opcServerId.Data2, (void*)opcServerId.Data3, (void*)opcServerId.Data4[0], (void*)opcServerId.Data4[1], (void*)opcServerId.Data4[2], (void*)opcServerId.Data4[3], (void*)opcServerId.Data4[4], (void*)opcServerId.Data4[5], (void*)opcServerId.Data4[6], (void*)opcServerId.Data4[7]);
ローカルにインストールされていないリモート コンピュータにインストールされている OPC サーバーに接続する場合は、OPCEnum への接続を作成する必要があります (ローカル コンピュータとリモート コンピュータの両方にインストールされていることが望ましい)。OPC サーバー名を CLSID に変換する COM オブジェクトをリモート サーバー上に作成できます。これを示すコードを次に示します。
//the first part of an OPC client is to connect to the OPCEnum service on the remote machine so we can look up the clsid of the OPC Server (given as a string).
//This code should get a list of OPC servers on a remote or local machine assuming that OPCEnum is running.
const CLSID CLSID_OpcServerList = {0x13486D51,0x4821,0x11D2, {0xA4,0x94,0x3C, 0xB3,0x06,0xC1,0x0,0x0}}; //{ 0x50fa5e8c, 0xdfae, 0x4ba7, { 0xb6, 0x9a, 0x8f, 0x38, 0xc2, 0xfd, 0x6c, 0x27 } }; //{0x13486D50,0x4821,0x11D2, {0xA4,0x94,0x3C, 0xB3,0x06,0xC1,0x0,0x0}};
const IID IID_IOPCServerList = {0x13486D50,0x4821,0x11D2, {0xA4,0x94,0x3C, 0xB3,0x06,0xC1,0x0,0x0}}; //for some reason the interface IID is the same as the CLSID.
const IID IID_IOPCServerList2 = {0x9DD0B56C,0xAD9E,0x43EE, {0x83,0x05,0x48, 0x7F,0x31,0x88,0xBF,0x7A}};
IOPCServerList *m_spServerList = NULL;
IOPCServerList2 *m_spServerList2 = NULL;
COSERVERINFO ServerInfo = {0};
ServerInfo.pwszName = hostName; //L"localhost";
ServerInfo.pAuthInfo = NULL;
MULTI_QI MultiQI [2] = {0};
MultiQI [0].pIID = &IID_IOPCServerList;
MultiQI [0].pItf = NULL;
MultiQI [0].hr = S_OK;
MultiQI [1].pIID = &IID_IOPCServerList2;
MultiQI [1].pItf = NULL;
MultiQI [1].hr = S_OK;
// Create the OPC server object and query for the IOPCServer interface of the object
HRESULT hr = CoCreateInstanceEx (CLSID_OpcServerList, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, &ServerInfo, 1, MultiQI); // ,IID_IOPCServer, (void**)&m_IOPCServer);
//hr = CoCreateInstance (CLSID_OpcServerList, NULL, CLSCTX_LOCAL_SERVER ,IID_IOPCServerList, (void**)&m_spServerList);
if (hr == S_OK)
{
printf("Part1 okay\n");
m_spServerList = (IOPCServerList*)MultiQI[0].pItf;
//m_spServerList2 = (IOPCServerList2*)MultiQI[1].pItf;
}
else
{
printf("Co create returned: %p\n",(void *)hr);
m_spServerList = NULL;
//qDebug() << (void *)REGDB_E_CLASSNOTREG;
}
//try and get the class id of the OPC server on the remote host
CLSID opcServerId;
CLSID clsid;
if (m_spServerList)
{
hr=m_spServerList->CLSIDFromProgID(serverName,&opcServerId);
m_spServerList->Release();
}
else
{
hr = S_FALSE;
opcServerId.Data1 = 0;
clsid.Data1 = 0;
}
//try to attach to an existing OPC Server (so our OPC server is a proxy)
if (hr != S_OK)
{
wprintf(L"Couldn't get class id for %s\n Return value: %p", serverName, (void *)hr);
}
else
{
printf("OPC Server clsid: %p %p %p %p%p%p%p%p%p%p%p\n", (void*)opcServerId.Data1, (void*)opcServerId.Data2, (void*)opcServerId.Data3, (void*)opcServerId.Data4[0], (void*)opcServerId.Data4[1], (void*)opcServerId.Data4[2], (void*)opcServerId.Data4[3], (void*)opcServerId.Data4[4], (void*)opcServerId.Data4[5], (void*)opcServerId.Data4[6], (void*)opcServerId.Data4[7]);
}
// Create the OPC server object and query for the IOPCServer interface of the object.
//Do it on the remote computer.
MultiQI [0].pIID = &IID_IOPCServer;
MultiQI [0].pItf = NULL;
MultiQI [0].hr = S_OK;
if (opcServerId.Data1)
{
hr = CoCreateInstanceEx (opcServerId, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER, &ServerInfo, 1, MultiQI);
}
else
{
hr = S_FALSE;
}
if (hr != S_OK)
{
m_IOPCServer = NULL;
printf("Couldn't create server.\n");
}
else
{
//CoCreateInstanceEx should have returned an array of pointers to interfaces. Since we only asked for 1, lets just get it.
m_IOPCServer = (IOPCServer*) MultiQI[0].pItf;
printf("Created remote OPC server.\n");
}
ここには、すべてのリモート OPC サーバーを列挙する方法を示すコメント アウトされたコードもあります。興味深いことに、これらのリモート サーバーの CLSID を取得できますが、これらの読み取り可能な名前を逆算するために、リモート コンピューター上に別の COM オブジェクトを作成する必要があると思います。(調べてみましたが、自分では必要なかったので、どのインターフェースだったか忘れてしまいました)。
//list all the OPC interfaces.
CLSID catid[2];
catid[0]=CATID_OPCDAServer10; //= CATID_OPCDAServer10; //OPC1.0
catid[1] = CATID_OPCDAServer20;
IOPCEnumGUID *pEnumGUID;
hr = m_spServerList->EnumClassesOfCategories(2, catid, 0, NULL, (IEnumGUID**)&pEnumGUID);
printf("Enum Result: %u", (void*) hr);
if (hr == S_OK)
{
//pEnumGUID->Reset();
GUID serverGUID;
ULONG numberServers = 8;
//pEnumGUID->Next(maxServers,serverGUIDs, &numberServers);
while ((hr = pEnumGUID->Next (1, &serverGUID, &numberServers)) == S_OK)
{
WCHAR* wszProgID;
hr = ProgIDFromCLSID (serverGUID, &wszProgID); //This probably won't work unless the same OPC server is installed locally.
printf("server: %ls \n",wszProgID);
CoTaskMemFree (wszProgID);
};
}
Prosys OPC クライアントを使用してサーバーを参照できます。選択したサーバーの CLSID が表示され、クリップボードにコピーできます。
レジストリをローカルで使用して、CLSID を見つけることができます。リモートからレジストリにアクセスすることはめったにありませんが、クライアント アプリケーションはそのために OpcEnum を使用できます。
通常、リモートで接続しようとしている場合、アプリケーションがローカル レジストリを使用して ProgID を CLSID に変換しようとすると、接続が ProgID で失敗することがあります。また、サーバーがローカルにインストールされていない場合、この情報は利用できません。