OPC - interfața pentru integrarea sistemelor și dispozitivelor eterogene cu diferite protocoale de schimb. În prezent, mecanismul și specificațiile OPC sunt instrumentul principal pentru schimbul de date în sistemele de automatizare și contabilitate. OPC DA în acest sens este deja oarecum depășită, dar este încă cel mai comun standard. În locul ei, de câțiva ani, a venit un OPC UA nou, unificat, multiplatform, standard. În plus, există două mai puțin frecvente caietul de sarcini OPC HDA (cerere de date arhivate, adică, atunci când o etichetă are o altă dimensiune - timp) și OPC AE (un standard specific pentru transmiterea de alarme și evenimente). Nu am scris servere OPC HDA și AE, așa că nu vă pot spune nimic util.
Veți spune, dar cum rămâne cu sarcina de bază de a obține date de la dispozitive de calcul care stochează și stochează date pe canale în timp, deoarece nu pot fi transmise prin OPC DA? Ai dreptate - într-adevăr imposibil, dar eu fac în acest caz este un pic mai complicat și a trecut de timp felie consistentă într-o singură etichetă sau codificat într-o singură, și în acest caz, problema de decompresie a datelor pune pe OPC client, care, în multe cazuri, ne permite să pună în aplicare prelucrarea datelor în cadrul acestora.
Să revenim la scrierea unui simplu server OPC DA pentru un dispozitiv tipic. Apropo, nu este necesar ca serverul să fie scris pentru integrarea dispozitivelor. Nu, pe de altă parte, poate exista orice din baza de date, fișiere text sau chiar persoana care introduce aceste date prin orice interfață. În general, trebuie să cunoaștem numai interfața hardware cu obiectul la distanță și protocolul de schimb cu el.
Deci avem:
un anumit dispozitiv cu o interfață serială
protocolul de schimb are loc în modul cerere-răspuns
Master este un dispozitiv, client-server se conectează și solicită date
În mod normal, pentru alte interfețe din curs se vor trece la alte programe, de exemplu pentru snifferii de rețea ethernet. Pentru protocolul modbus, este util să instalați mai multe utilitare simultan, ceea ce vă va permite să consultați toate registrele pentru a verifica propriile ipoteze pentru corectitudine.
Acest lucru va fi necesar dacă aveți probleme de depanare a serverului și:- compararea parcelelor generate de serverul propriu și software-ul de service
- compararea registrelor de schimb valutar, perioadele de timp dintre parcele
- pentru a vă asigura că nu este vorba de cod
Serverul va scrie pe baza bibliotecii obișnuite pentru mine (www.ipi.ac.ru/lab43/lopc-ru.html). Există o mulțime de alte biblioteci și cadre pentru servere și clienți care pot fi găsite pe Internet, am lucrat cu multe altele, dar m-am oprit la lightopc din cauza simplității. Acest proiect nu a fost dezvoltat de mult timp și nu acceptă alte specificații decât OPC DA.
Acum continuați direct la scrierea serverului. Rulați VS, selectați tipul de proiect - aplicația consola, iar proiectul în sine este lăsat necompletat. Folosesc aplicația consola, deoarece este convenabil să se afișeze în vechea consolă bună ceea ce serverul se ocupă în prezent, inclusiv valorile curente ale parametrilor, solicitările / răspunsurile de la dispozitive sau erorile proprii.
Creați fișierul original al serverului gol. Apoi, nu voi intra în proiectarea și scrierea claselor, distribuția lor prin fișiere și module separate, deoarece sarcina mea este doar să arăt esența, adică să creez cel mai simplu exemplu de server. Deci, proiectul minimal de server OPC va consta dintr-un fișier sursă în C, un fișier antet, un fișier de interfață și mai multe biblioteci.
Creați și adăugați fișiere la proiect:
- device.h
- device.cpp
Este necesar să descărcați bibliotecile colectate lightopc-0.888-0313bin de pe site-ul lighopc (www.ipi.ac.ru/lab43/lopc-ru.html). În interiorul arhivei vor fi fișierele lightopc.dll, lightopc.lib, care trebuie adăugate la proiect.
De acolo, descarcăm biblioteca pentru logarea serverului unilog-0.55-1227. În interior vor fi fișiere unilog.dll și unilog.lib, ele sunt, de asemenea, adăugate la proiect.
Apoi, avem nevoie de o bibliotecă opcda de la SDK (opcfoundation.org), antetul său este un swoop acolo și ne conectăm din program.
#include "opcda.h"
Modulul de program al interfeței seriale poate utiliza oricare dintre multele prezentate pe internet sau poate scrie propria clasă. Voi folosi o clasă mică cu numele serialport corespunzător și funcții tipice pentru deschiderea, închiderea portului, scrierea și citirea din port. Codul sursă poate fi vizualizat în arhivă, care este atașat la acest articol.
Aceasta este decizia cea mai corectă, deși este mai flexibil pentru a crea un strat sub forma unui modul, care, în funcție de tipul de interfață pentru a utiliza anumite funcții pentru a lucra cu el, și apeluri în afara din programul principal va rămâne același. Acest lucru vă va permite să aveți un singur cod sursă și să comutați cu ușurință între tipurile de interfețe sau chiar să combinați atunci când rulați un server.
Dacă se utilizează un convertor de interfață (RS-CAN, RS-Ethernet), atunci va fi necesară o bibliotecă corespunzătoare cu API pentru a se conecta la proiect.
Ce anume trebuie determinat.
#define _WIN32_DCOM // Permite extensii DCOM
#define INITGUID // Inițializează constantele OLE
#define ECL_SID "opc.device" // identificatorul serverului OPC
Exact sunt necesare următoarele biblioteci.
#include // standard I / O
# include // funcțiile matematice
#include "server.h" // fișierul antet pentru acest server
#include "unilog.h" // biblioteca pentru fișierele jurnal
#include "opcda.h" // funcții de bază OPC: DA
#include "lightopc.h" // fișierul de antet OPC
Definiți variabilele pentru lightopc.
static const loVendorInfo vendor =; // versiune server (Major / Minor / Build / Reserve)
static int OPCstatus = OPC_STATUS_RUNNING; // Starea serverului OPC
loService * my_service; // instanta serverului lightOPC
Memorie pentru variabile - etichetele serverului pot fi alocate dinamic și le puteți lua în mod static, TAGS_NUM_MAX - numărul maxim de etichete.
static CHAR * tn [TAGS_NUM_MAX]; // Nume de etichete
static loTagValue tv [TAGS_NUM_MAX]; // Valori pentru etichete
ETICHETE statice [TAGS_NUM_MAX]; // ID-uri de etichete
#include "tinyxml / tinyxml.h" // parser XML lib
Clasa de bază myClassFactory: public IClassFactory și funcțiile de tip pe care le aleg de obicei într-un fișier separat, astfel încât să nu calculeze ochii. Acesta este neschimbat și codul său nu este necesar pentru tine pentru a rezolva la început. Doar descărcați și lipiți înainte de codul principal.
Toate posibilele intrări în program conduc la funcția noastră de memorie.
Mai departe dezasamblam conținutul principal al funcției, direct pe puncte.
INT mymain (HINSTANCE hInstance, INT argc, CHAR * argv []);
1) Creăm un fișier jurnal imediat, pentru a scrie toate acțiunile serverului. Fiecare dintre noi are propriile noastre viziuni pentru depanare. De exemplu, îmi place să scriu toate programului și toate valorile intermediare ale variabilelor în jurnalele, apoi la apalizirovat comportamentul pas programului cu pas și numai dacă există deja un fel de coliziune, apoi face pas cu pas de depanare în debugger integrat.
Deci, creez un fișier, aici LOG_FNAME este numele fișierului.
logg = unilog_Creați (ECL_SID, LOG_FNAME, NULL, 0, ll_DEBUG); // level [ll_FATAL. ll_DEBUG]
UL_INFO ((LOGID, "Porniți serverul OPC pornit"));>
2) Ce ar trebui să ruleze serverul nostru? Mai întâi de toate, funcționează și serverul funcționează împreună cu clientul OPC (nu, poate fi ciocănit și inactiv, dar fără clienți conectați nu va avea întotdeauna sens). Deși voi lăsa acest punct la discreția ta. În cele din urmă, nimeni nu vă interzice să scrieți un server care să funcționeze întotdeauna și chiar fără clienți conectați și să facă ceva la fel de util ca și furnizarea de date. De exemplu, am integrat în server funcția de scriere a datelor în fișiere text și o bază de date, iar clienții au transferat aceste date în timp ce erau conectate. Acest lucru nu este complet corect din punctul de vedere al ideologiei, dar este foarte practic.
Pentru ca clienții să se conecteze la server, trebuie să știe dacă există un server în lista serverelor înregistrate de pe aparatul local sau de la distanță. Pentru a face acest lucru, dispozitivul nostru trebuie să fie înregistrat în sistem.
Pentru aceasta, funcția loServerRegister (GID_ECLOPCserverExe, eProgID, eClsidName, argv0, 0)
GID_ECLOPCserverExe - UID-ul generat anterior
eProgID, eClsidName - identificator, nume server - const char eProgID [] = ECL_SID; const char eClsidName [] = ECL_SID;
argv0 - linia de comandă (de fapt, această cale și înregistrarea sistemului de operare în registru)
Propun să pornim această funcție în mod tradițional - prin trecerea cheii pe linia de comandă.
De exemplu, comutatorul / r sau / registrul va indica serverului că doriți să înregistrați serverul pe sistem.
Tasta / u sau / unregister va face clar pentru server că doriți să eliminați serverul din sistem.
Pentru eliminarea serverului din sistem, funcția loServerUnregister (GID_ECLOPCserverExe, eClsidName) este responsabilă.
Nu uitați să ștergeți instanța creată a jurnalului
unilog_Delete (logg); logg = NULL;
În alte cazuri, serverul ar trebui să funcționeze în modul normal.
3) Înainte de a începe lucrul, inițializați biblioteca cheie a obiectelor COM cu funcția CoInitializeEx (NULL, COINIT_MULTITHREADED).
4) Apoi, din nou, există două versiuni specifice ale dezvoltării evenimentelor. Încercăm să inițializăm interfața, să încercăm să găsim dispozitivul și, dacă reușim, să trecem mai departe, iar în caz de defecțiune să terminăm serverul. Fie în orice caz înregistrăm o instanță a serverului, fără a aștepta ca interfața și răspunsul dispozitivului să fie gata și inactiv, așteptând conexiunea. În cazul nostru, nu este foarte important, deci pentru starter, încep funcția de inițializare a interfeței InitDriver (), pe care o voi descrie mai târziu. Dacă inițializarea nu reușește, nu uit, cu excepția ștergerii plugin-ului modulului jurnal și a căutării dezinitializării COM CoUninitialize (). De asemenea, vom șterge loService în sine - loServiceDestroy (my_service).
5) Crearea și popularea structurii driverului de votare este alocată logic funcției InitDriver (), împreună cu inițializarea interfeței.
Pentru a parsa fișierul de configurare, nu voi deschide și nu va configura portul serial. În cele din urmă, nu vom lucra cu un dispozitiv real și nu vom derula programul până nu o fac. Dacă este necesar, voi descrie această secțiune mai târziu.
6) Inițializați etichetele. Creați două dispozitive falsificate cu câte 3 etichete fiecare. Prima etichetă va fi un număr întreg, iar celelalte două vor fi punct de plutire.
Aici tag_add este contorul etichetelor adăugate.
8) La conectarea unui contor nou client este incrementat cu unu, ceea ce face funcția clasei myClassFactory -> AddRef (), my_CF.Release () - dimpotrivă servește scopului și inversă distruge un client, reducând contorul de unul. Atâta timp cât numărul de clienți conectați este mai mare decât zero, in_use returnează unul.
9) Totul. Acum putem lucra pe deplin. De exemplu, interogați dispozitivul într-o buclă.
în timp ce (my_CF.in_use ()) dacă (WorkEnable) poll_device (); Adică, în timp ce cel puțin un client este conectat la server și este setat steagul propriu al serverului, vom sondajul dispozitivului într-o buclă. Intram în propriul steag WorkEnable pentru a putea opri serverul în orice moment fără a aștepta închiderea tuturor clienților.
10) Funcția de sondare a datelor poll_device () este funcția principală a serverului, în care dispozitivul este interogat și valorile etichetelor sunt actualizate. Dacă utilizați mai multe instanțe ale aceeași interfață (da, chiar dacă acestea nu intenționează să facă acest lucru acum), va trebui să fie alocate pentru fiecare flux separat, sau formarea / cerere / procesare de răspuns va fi efectuată secvențial într-o singură, care este un impact extrem de negativ asupra viteza întregului server. Pur și simplu, fiecare port COM, fiecare soclu și fiecare client alocăm un fir separat, care se va ocupa de schimbul de date pe această interfață.
Iată un exemplu de organizare a unei astfel de lucrări:
Un exemplu de fișier jurnal pentru un server care funcționează corect. Descărcați un proiect