- Обязательно представиться на русском языке кириллицей (заполнить поле "Имя").
- Фиктивные имена мы не приветствуем. Ивановых и Пупкиных здесь уже достаточно.
- Не надо писать свой вопрос в первую попавшуюся тему - всегда лучше создать новую тему.
- За поиск, предложение и обсуждение пиратского ПО и средств взлома - бан без предупреждения. Непонятно? - Читать здесь.
- Рекламу и частные объявления "куплю/продам/есть халтура" мы не размещаем ни на каких условиях.
- Перед тем как что-то написать - читать здесь, а затем здесь и здесь.
- Не надо писать в ЛС администраторам свои технические вопросы. Администраторы форума отлично знают как работает форум, а не все-все контроллеры, о которых тут пишут.
WinCC, C-Script, db.h - запрос к БД из С-скрипта
Модератор: Глоб.модераторы
-
- частый гость
- Сообщения: 409
- Зарегистрирован: 20 ноя 2012, 13:45
- Имя: :.О.N.Ф
- Страна: Россия
- Благодарил (а): 3 раза
- Поблагодарили: 7 раз
WinCC, C-Script, db.h - запрос к БД из С-скрипта
Как работать с функцией DBGetRecordSQL? Что за параметр LPVOID lpUser? В каком виде возвращается результат запроса?
«Сразу видно внимание к каждой мелочи, неиспорченным не осталось ничто».
-
- освоился
- Сообщения: 208
- Зарегистрирован: 16 дек 2011, 15:13
- Имя: Алексей
- Страна: Россия
- Благодарил (а): 67 раз
- Поблагодарили: 53 раза
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
В ODK хелпе написано, что функция перечисляет записи, содержащиеся в таблице...
Посмотрел пример DB01.cpp в сэмплах ODK - там расписана работа с таблицей MCPTVARIABLEDESC в базе данных WinCC. По опыту знаю, что там находятся данные о тегах в проекте WinCC...сам недавно с ней работал, но только не через ODK, а напрямую SQL запросами.
Вот накидал класс работы с ней (я сам пишу все на C#, поэтому вызовы из библиотек приходится маршалить), по комментариям в коде можно разобраться
что делать нужно, если будут вопросы - обращайтесь, поясню.
файл db.cs - класс, отвечающий за вызов из библиотеки функции и объявление делегата коллбека
db.cs
Далее, например создаем WindowsFormsApplication приложение, добавляем файл db.cs
Form1.cs
Вкратце, LPVOID lpUser - "пользовательский" объект, который передаешь в функцию вызова (DBGetRecordSQL) и получаешь обратно в callback
Для чего это нужно - для того чтобы не возникло ошибки доступа к объекту не в том потоке, в котором он был создан.
В коде это прокомментировано.
Посмотрел пример DB01.cpp в сэмплах ODK - там расписана работа с таблицей MCPTVARIABLEDESC в базе данных WinCC. По опыту знаю, что там находятся данные о тегах в проекте WinCC...сам недавно с ней работал, но только не через ODK, а напрямую SQL запросами.
Вот накидал класс работы с ней (я сам пишу все на C#, поэтому вызовы из библиотек приходится маршалить), по комментариям в коде можно разобраться
что делать нужно, если будут вопросы - обращайтесь, поясню.
файл db.cs - класс, отвечающий за вызов из библиотеки функции и объявление делегата коллбека
db.cs
Код: Выделить всё
using System;
using System.Runtime.InteropServices;
namespace DBWinCC
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public class CMN_ERROR
{
[MarshalAs(UnmanagedType.U4)]
public UInt32 dwError1;
public UInt32 dwError2;
public UInt32 dwError3;
public UInt32 dwError4;
public UInt32 dwError5;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 512)]
public String szErrorText;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate bool DB_ENUM_RECORDS_CALLBACK(uint hDB, IntPtr hRecord, IntPtr lpUser);
public class DatabaseWinCC
{
[DllImport("db.dll", CharSet = CharSet.Ansi, EntryPoint = "DBGetRecordSQL", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
internal static extern bool DBGetRecordSQL(ref UInt32 hDB,
[In] DB_ENUM_RECORDS_CALLBACK lpfnCallback, [In] [MarshalAs(UnmanagedType.LPTStr)] string lpSQLStatement,
[In] IntPtr lpvUser, [In, Out] [MarshalAs(UnmanagedType.LPStruct)] CMN_ERROR lpdmError);
}
}
Form1.cs
Код: Выделить всё
public partial class Form1 : Form
{
List<string> userData = new List<string>();
private DB_ENUM_RECORDS_CALLBACK callback;
// делегат - ссылка на метод callback - обратного вызова, данную переменную нужно объявлять глобальной! а не локальной
uint hDB = 0; // идентификатор полученный в результате DBOpen
public Form1()
{
InitializeComponent();
//тут DBConnect(.., DSN, ...)
//потом DBOpen(.., HDB, ...)
callback = CallbackProc;
}
private void button1_Click(object sender, EventArgs e)
{
GCHandle handle1 = GCHandle.Alloc(userData);
IntPtr userDataPtr = GCHandle.ToIntPtr(handle1);
CMN_ERROR error = new CMN_ERROR();
bool result = DatabaseWinCC.DBGetRecordSQL(ref hDB, callback, null, userDataPtr, error);
handle1.Free();
}
private bool CallbackProc(uint hDB, IntPtr hRecord, IntPtr lpUser)
{
//здесь по идее должны нам отдаваться данные в результате вызова менеджером WinCC нашего callback
// это по видимому hRecord , вот только какого это типа данные - надо разобраться
//int FieldNum = 5; //столбец 5 к примеру
//string Data = "";
//CMN_ERROR error = new CMN_ERROR();
//DBGetFieldData(hrecord, FieldNum, Data, error);
//Data - данные из таблицы
//здесь наш лист получаем и работаем с ним - напрямую обращаться к нему нельзя, иначе
//вывалится ошибка доступа из другого потока , не принадлежащего нам (поток WinCC)
GCHandle handle2 = GCHandle.FromIntPtr(lpUser);
List<string> userData = handle2.Target as List<string>;
userData.Add(string.Format("{0} - WinCC прислал данные", DateTime.Now.ToShortTimeString()));
handle2.Free();
return true;
}
}
Вкратце, LPVOID lpUser - "пользовательский" объект, который передаешь в функцию вызова (DBGetRecordSQL) и получаешь обратно в callback
Для чего это нужно - для того чтобы не возникло ошибки доступа к объекту не в том потоке, в котором он был создан.
В коде это прокомментировано.
-
- частый гость
- Сообщения: 409
- Зарегистрирован: 20 ноя 2012, 13:45
- Имя: :.О.N.Ф
- Страна: Россия
- Благодарил (а): 3 раза
- Поблагодарили: 7 раз
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
Не, использовать эту беду нужно в скрипте самой WinCC. С вопросом LPVOID lpUser прояснилось, и, кстати, это не обязательно целое число (у вас IntPtr), туда очень удобно структуру положить.
Тут ещё нюанс выяснился, что эта db.dll внезапно не все запросы хавает, на особо извращённые отвечает "enum first failed". Плохая длл-ка. Вы не знаете, как ещё можно читать из базы C-Sript'ами?
Тут ещё нюанс выяснился, что эта db.dll внезапно не все запросы хавает, на особо извращённые отвечает "enum first failed". Плохая длл-ка. Вы не знаете, как ещё можно читать из базы C-Sript'ами?
«Сразу видно внимание к каждой мелочи, неиспорченным не осталось ничто».
-
- освоился
- Сообщения: 208
- Зарегистрирован: 16 дек 2011, 15:13
- Имя: Алексей
- Страна: Россия
- Благодарил (а): 67 раз
- Поблагодарили: 53 раза
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
Здравствуйте!
По поводу LPVOID lpUser: Вы правы, туда можно любой свой объект положить, а IntPtr это не целое число, а "указатель" в C#, в своем примере я положил туда объект List...ну да ладно...
Приведу пример еще один с коллбеком, принцип похожий, может что и получится:
метод SaveLimits - сохраняет в файл значения переменных, список переменных задается фильтром
Сам коллбек
Опишите задачу, а то я не знаю даже чем помочь
По поводу LPVOID lpUser: Вы правы, туда можно любой свой объект положить, а IntPtr это не целое число, а "указатель" в C#, в своем примере я положил туда объект List...ну да ладно...
Приведу пример еще один с коллбеком, принцип похожий, может что и получится:
метод SaveLimits - сохраняет в файл значения переменных, список переменных задается фильтром
Код: Выделить всё
#include "apdefap.h"
#include "dmclient.h"
//тут объявлен внешний метод
extern BOOL SaveLimitsEnumVariables(LPDM_VARKEY key, LPVOID lpvUser);
void SaveLimits()
{
#pragma code ("User32.dll")
int WINAPI MessageBoxA(DWORD handle, char* text, char* title, DWORD flags);
#pragma code ()
char projFile[_MAX_PATH];
char limitFile[_MAX_PATH];
char drive[_MAX_DRIVE];
char dir[_MAX_DIR];
char fname[_MAX_FNAME];
char ext[_MAX_EXT];
DM_VARFILTER flt;
CMN_ERROR err;
FILE* file;
DWORD dwFilterTypes[3];
memset(projFile, 0, _MAX_PATH);
memset(limitFile, 0, _MAX_PATH);
memset(&err,0,sizeof(err));
memset(&flt, 0, sizeof(DM_VARFILTER));
//фильтр по имени и типу переменной
flt.dwFlags = DM_VARFILTER_NAME | DM_VARFILTER_TYPE;
dwFilterTypes[0] = DM_VARTYPE_FLOAT;
flt.pdwTypes = dwFilterTypes;
flt.dwNumTypes = 1;
//фильтр по имени тега, который начинается со слова Limit
flt.lpszName = "Limit*";
if (!DMGetRuntimeProject(projFile, sizeof(projFile), &err)) {
MessageBoxA(0, err.szErrorText, "DMGetRuntimeProject", MB_OK);
return;
}
_splitpath(projFile, drive, dir, fname, ext);
_makepath(limitFile, drive, dir, "Limits", "txt");
file = fopen(limitFile,"w");
if (!file) {
//MessageBoxA(0, "Error Opening 'Limits.txt' for writing", "", MB_OK);
return;
}
// вызов перечисления переменных с коллбеком
if (!DMEnumVariables(projFile, &flt, SaveLimitsEnumVariables, file, &err)) {
//MessageBoxA(0, err.szErrorText, "DMEnumVariables", MB_OK);
}
fclose(file);
}
Код: Выделить всё
#include "apdefap.h"
#include "dmclient.h"
BOOL SaveLimitsEnumVariables(LPDM_VARKEY key, LPVOID lpvUser)
{
FILE* f = (FILE*)lpvUser;
float data = GetTagFloat(key->szName);
fprintf(f,"%s\t%f\n", key->szName, data);
return TRUE;
}
Вы имеете в виду из любой базы данных SQL? или конкретно WinCCевой базы?Exactamente писал(а):Вы не знаете, как ещё можно читать из базы C-Sript'ами?
Опишите задачу, а то я не знаю даже чем помочь
-
- частый гость
- Сообщения: 409
- Зарегистрирован: 20 ноя 2012, 13:45
- Имя: :.О.N.Ф
- Страна: Россия
- Благодарил (а): 3 раза
- Поблагодарили: 7 раз
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
С дллкой разобрался, дллка хорошая, запрос был плохой. Всё работает. Спасибо за помощь.
А вы с OPC .NET API от OPC Foundation случаем не работали? Решил поковырять, а инфы в гугле днём с огнём.
А вы с OPC .NET API от OPC Foundation случаем не работали? Решил поковырять, а инфы в гугле днём с огнём.
«Сразу видно внимание к каждой мелочи, неиспорченным не осталось ничто».
-
- освоился
- Сообщения: 208
- Зарегистрирован: 16 дек 2011, 15:13
- Имя: Алексей
- Страна: Россия
- Благодарил (а): 67 раз
- Поблагодарили: 53 раза
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
C OPC работал, используя OPCEnum и opcproxy.dll, начал после прочтения очень хорошей книжки Федоренко Дениса "Программирование OPC клиентов на C++ и C#. Часть 1. OPC DA". Подробно разжевано..
Еще есть OPC Automation (Wrapper) (opcdaauto.dll) - обертка над "классическим" OPC интерфейсами, предназначался в основном для Visual Basic, на opcfoundation.org написано что заменен как раз на OPC .NET API. На нем очень просто написать клиента.
OPC .NET API - эхххх, доступ только для корпоративных клиентов, видимо тех, кто входит в группу и вносит "членские" взносы )).
graybox opc toolkit - пробовал как то давно, получалось и сервер написать и клиент, только он платный)
Еще есть OPC Automation (Wrapper) (opcdaauto.dll) - обертка над "классическим" OPC интерфейсами, предназначался в основном для Visual Basic, на opcfoundation.org написано что заменен как раз на OPC .NET API. На нем очень просто написать клиента.
OPC .NET API - эхххх, доступ только для корпоративных клиентов, видимо тех, кто входит в группу и вносит "членские" взносы )).
graybox opc toolkit - пробовал как то давно, получалось и сервер написать и клиент, только он платный)
-
- частый гость
- Сообщения: 409
- Зарегистрирован: 20 ноя 2012, 13:45
- Имя: :.О.N.Ф
- Страна: Россия
- Благодарил (а): 3 раза
- Поблагодарили: 7 раз
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
Не-не, готовые тулкиты и SDK не катят, и платно, и вообще неспортивно)
Да не, вроде, OPC .NET API бесплатный и должен ставиться вместе с OPC Redistributables. А вот исходники и примеры - для корпоративных. Но на одном общеизвестном российском торрент-трекере есть раздача со всем этим добром, там и исходники, и примеры для HDA, UA и прочих есть, а для DA почему-то нету.
>Программирование OPC клиентов на C++ и C#. Часть 1. OPC DA
О! Это хороший документ, большое спасибо, изучу.
Да не, вроде, OPC .NET API бесплатный и должен ставиться вместе с OPC Redistributables. А вот исходники и примеры - для корпоративных. Но на одном общеизвестном российском торрент-трекере есть раздача со всем этим добром, там и исходники, и примеры для HDA, UA и прочих есть, а для DA почему-то нету.
>Программирование OPC клиентов на C++ и C#. Часть 1. OPC DA
О! Это хороший документ, большое спасибо, изучу.
«Сразу видно внимание к каждой мелочи, неиспорченным не осталось ничто».
-
- освоился
- Сообщения: 208
- Зарегистрирован: 16 дек 2011, 15:13
- Имя: Алексей
- Страна: Россия
- Благодарил (а): 67 раз
- Поблагодарили: 53 раза
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
давно хочу разобраться с UA, очень заманчиво отвязаться наконец от DCOM и плясок с бубном...но для этого надо хотя бы владеть SOAP и WCFExactamente писал(а): примеры для HDA, UA и прочих есть
-
- частый гость
- Сообщения: 409
- Зарегистрирован: 20 ноя 2012, 13:45
- Имя: :.О.N.Ф
- Страна: Россия
- Благодарил (а): 3 раза
- Поблагодарили: 7 раз
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
Но всё вокруг работает через DA, так что я пока на него ориентируюсь.
>заменен как раз на OPC .NET API. На нем очень просто написать клиента
И вот нифига. Я уже даже сэмплы нашёл, но они на WinForms, там ад и содомия =(
>заменен как раз на OPC .NET API. На нем очень просто написать клиента
И вот нифига. Я уже даже сэмплы нашёл, но они на WinForms, там ад и содомия =(
«Сразу видно внимание к каждой мелочи, неиспорченным не осталось ничто».
-
- здесь недавно
- Сообщения: 3
- Зарегистрирован: 25 ноя 2015, 15:36
- Имя: Спирин Дмитрий
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
Добрый день.
В описании к функции DBGetRecordSQL написано: "lpfnCallback - Pointer to your callback function which is called for each data record meeting the condition of lpSQLStatement.". Т.е. вызов callback-функции должен происходить для каждой записи, удовлетворяющей условию в поле lpSQLStatement.
У меня такого не получилось, функция вызывается только один раз.
Кто-нибудь сталкивался?
В описании к функции DBGetRecordSQL написано: "lpfnCallback - Pointer to your callback function which is called for each data record meeting the condition of lpSQLStatement.". Т.е. вызов callback-функции должен происходить для каждой записи, удовлетворяющей условию в поле lpSQLStatement.
У меня такого не получилось, функция вызывается только один раз.
Кто-нибудь сталкивался?
-
- здесь недавно
- Сообщения: 3
- Зарегистрирован: 25 ноя 2015, 15:36
- Имя: Спирин Дмитрий
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
вот мой код:
callback-функция:
Код: Выделить всё
#include "apdefap.h"
void OnClick(char* lpszPictureName, char* lpszObjectName, char* lpszPropertyName)
{
// WINCC:TAGNAME_SECTION_START
#define DSN "@DatasourceNameRT"
#define Table "AlgActivatedData"
// WINCC:TAGNAME_SECTION_END
CMN_ERROR Error;
HANDLE hDSN = NULL;
HANDLE hDB = NULL;
DWORD Num;
int ID;
char sDSN[MAX_PATH]="";
strcpy(sDSN,GetTagChar(DSN));
if (!DBConnect(&hDSN,sDSN,&Error))
{
printf("Error in DBConnect: E1= 0x%04x ; E2= 0x%04x ; %s \r\n",Error.dwError1, Error.dwError2, Error.szErrorText);
DBDisConnect ( hDSN, 0, &Error);
return;
}
if (!DBOpen (hDSN, &hDB, Table, 0, &Error))
{
printf("Error in DBOpen: E1= 0x%04x ; E2= 0x%04x ; %s \r\n",Error.dwError1, Error.dwError2, Error.szErrorText);
}
DBGetNumRecords(hDB,&Num,&Error);
printf("records = %d\r\n",Num);
DBGetRecordSQL(hDB, New_Function,"ID>0",&ID,&Error);
printf("IDex = %d \r\n",ID);
if (!DBDisConnect ( hDSN, 0, &Error))
{
printf("Error in DBConnect: E1= 0x%04x ; E2= 0x%04x ; %s \r\n",Error.dwError1, Error.dwError2, Error.szErrorText);
}
Код: Выделить всё
#pragma code ("db.dll");
#include "db.h";
#pragma code();
BOOL New_Function(HANDLE hDB,HRECORD hRecord, int *lpUser)
{
CMN_ERROR Error;
int ID;
if (!DBGetIntFieldData (hRecord, 1, &ID, &Error))
{
printf("Error in DBGetIntFieldData: E1= 0x%04x ; E2= 0x%04x ; %s \r\n",Error.dwError1, Error.dwError2, Error.szErrorText);
}
memcpy(lpUser,&ID,sizeof(int));
printf("IDin = %d \r\n",ID);
return 0;
}
У вас нет необходимых прав для просмотра вложений в этом сообщении.
-
- здесь недавно
- Сообщения: 3
- Зарегистрирован: 25 ноя 2015, 15:36
- Имя: Спирин Дмитрий
Re: WinCC, C-Script, db.h - запрос к БД из С-скрипта
была ошибка в callback-функции.
нужно "return 0" заменить на "return 1"
нужно "return 0" заменить на "return 1"