SAPфорум.RU
https://sapboard.ru/forum/

sap .NET connector
https://sapboard.ru/forum/viewtopic.php?f=13&t=92716
Страница 1 из 3

Автор:  _garycor_ [ Пт, мар 25 2016, 09:32 ]
Заголовок сообщения:  sap .NET connector

Добрый день, коллеги!
никогда не приходилось писать внешние программы для RFC-соединений. но видать пришла пора.

есть такая тема как diadoc.ru (API http://api-docs.diadoc.ru/ru/latest/)
нам нужно сделать саповский интерфейс для управления документами на diadoc.ru.
диадок предоставил нам DLL-ку, которая формирует HTTP-запросы для обмена данными.
так же они нам предоставили тестовый EXE-файл (использует DLL-ку), в котором реализована только одна функция авторизации.
Exe-шник прописывается в RFC-соединении (тип Т) с галкой Start on front-end Work Station. пишем RFC функцию авторизации. все работает.
логика работы: вызываем RFC. вызывается экзешник, который делает HTTP-запрос к диадок, получает ответ, преобразовывает его в формат RFC-параметров и возвращает в RFC.

решили расширять их экзешник для остальных запросов реализованных в DLL. декомпилировали и получили c#-овский исходник.
начали дорабатывать и сразу проблема:
если я вот так объявляю функцию в EXE
Code:
       
        [RfcServerFunction(Name = "ZDDK_LOGIN"),
        RepositoryFunctionParamameter("I_LOGIN", RfcDataType.STRING, RfcDirection.IMPORT),
        RepositoryFunctionParamameter("I_PASSWORD", RfcDataType.STRING, RfcDirection.IMPORT),
        RepositoryFunctionParamameter("O_ORGS", RfcDataType.STRING, RfcDirection.EXPORT),
        RepositoryFunctionParamameter("O_TOKEN", RfcDataType.STRING, RfcDirection.EXPORT)]
        public void diadoc_login(RfcServerContext context, IRfcFunction function)
        {
         .....
        }

то все работает на ура.
но как только хочу чтобы функция возвращала в RFC какую-то таблицу, то сразу перестает срабатывать даже тест соединения из SM59
делаю так
Code:
       
        [RfcServerFunction(Name = "ZDDK_LOGIN"),
        RepositoryFunctionParamameter("I_LOGIN", RfcDataType.STRING, RfcDirection.IMPORT),
        RepositoryFunctionParamameter("I_PASSWORD", RfcDataType.STRING, RfcDirection.IMPORT),
        RepositoryFunctionParamameter("O_ORGS", RfcDataType.STRING, RfcDirection.EXPORT),
        RepositoryFunctionParamameter("O_TOKEN", RfcDataType.STRING, RfcDirection.EXPORT),
        RepositoryFunctionParamameter("O_ORGS_T", RfcDataType.TABLE, RfcDirection.TABLES)]
        public void diadoc_login(RfcServerContext context, IRfcFunction function)
        {
          ....
        }


Собственно в этом и вопрос. Как из Exe возвращать заполненную внутреннюю таблицу в RFC?
хорошо бы примерчик какой-нибудь.

Используем sap nco 3.0 для net 4.0.

Автор:  John Doe [ Пт, мар 25 2016, 10:41 ]
Заголовок сообщения:  Re: sap .NET connector

А зачем все так сложно делать? Авторизоваться по http Вы можете и из SAP, используя интерфейс IF_HTTP_CLIENT. У нас решение именно так работает уже несколько лет.

Если нужны специфические методы Диадока, то мы используем Diadoc SDK, который устанавливается на пользовательском ПК под управлением Windows (плюс необходимо Microsoft .NET Framework 4 доставить). Дальше все через COM-объект решается.

Автор:  AFH [ Пт, мар 25 2016, 11:35 ]
Заголовок сообщения:  Re: sap .NET connector

Чтобы через IF_HTTP_CLIENT общаться с диадоком (в фоновом режиме) раньше надо было уметь из абапа работать с Protocol Buffers, естественно готовых решений для этого нет. Но теперь, как я посмотрел у них на сайте, они также поддерживают JSON что из абапа сделать гораздо проще, хоть руками конкатенировать, хоть с помощью XSLT, даже классы какие-то были для сериализации\десериализации JSON. Хотя сам Диадок не рекомендует JSON использовать.

Автор:  _garycor_ [ Пт, мар 25 2016, 12:18 ]
Заголовок сообщения:  Re: sap .NET connector

забыл написать. у нас 4.6 если что.

Автор:  _garycor_ [ Пт, мар 25 2016, 14:37 ]
Заголовок сообщения:  Re: sap .NET connector

Цитата:
Чтобы через IF_HTTP_CLIENT общаться с диадоком (в фоновом режиме) раньше надо было уметь из абапа работать с Protocol Buffers


и таки да...
в DLLке уже ведь все реализовано (PROTOBUF). поэтому и хочется именно ее использовать.

но вопрос не про то что проще или нет...

вопрос про то почему при добавлении RepositoryFunctionParamameter("O_ORGS_T", RfcDataType.TABLE, RfcDirection.TABLES) соединение перестает работать? и как из этого выходить? неужто нужно возвращать в RFC строку и ее потом парсить.

Автор:  AFH [ Пн, мар 28 2016, 03:59 ]
Заголовок сообщения:  Re: sap .NET connector

А из системы вы вызываете через TCP/IP RFC в котором указана ид зарегистрированной программы сервера? Обычно в таких случаях внешняя программа имеет собственное соединение в сторону SAP-сервера чтобы считать метаданные ФМ (тип и длины полей). Если такого соединения нет, то вроде как где-то надо явно все детали ФМ описывать (я сужу по SDK для C++).

Автор:  _garycor_ [ Пн, мар 28 2016, 11:12 ]
Заголовок сообщения:  Re: sap .NET connector

Цитата:
А из системы вы вызываете через TCP/IP RFC в котором указана ид зарегистрированной программы сервера?

точно так

Цитата:
Обычно в таких случаях внешняя программа имеет собственное соединение в сторону SAP-сервера чтобы считать метаданные ФМ (тип и длины полей). Если такого соединения нет, то вроде как где-то надо явно все детали ФМ описывать (я сужу по SDK для C++).

все это хорошо, только я не понимаю как объявлять функцию.
Если при объявлении функции я пишу RepositoryFunctionParamameter("O_ORGS_T", RfcDataType.TABLE, RfcDirection.TABLES), то перестает работать тест соединения в SM59 (получаю ошибку "timeout during allocate").
Если не писать этого, то тогда при запуске RFC функции в SE37 получаем дамп с ошибкой "Element O_ORGS_T of container metadata ZDDK_LOGIN unknown"

Автор:  John Doe [ Пн, мар 28 2016, 11:41 ]
Заголовок сообщения:  Re: sap .NET connector

Если я правильно понимаю, то RepositoryFunctionParamameter - это некая процедура из библиотеки Диадок, которая обрабатывает метаданные из интерфейса SAP модуля. Возможно, что там проблема и лежит и нужно дергать того, кто Вам ее предоставил.

И еще. Вы не пробовали прогнать O_ORGS_T через EXPORT или CHANGING параметр?

Автор:  _garycor_ [ Пн, мар 28 2016, 12:21 ]
Заголовок сообщения:  Re: sap .NET connector

Цитата:
Если я правильно понимаю, то RepositoryFunctionParamameter - это некая процедура из библиотеки Диадок

Нет. это приблуда SAP CONNECTOR-а.

Цитата:
И еще. Вы не пробовали прогнать O_ORGS_T через EXPORT или CHANGING параметр?

попробовал так RepositoryFunctionParamameter("ET_ORGS", RfcDataType.TABLE, RfcDirection.CHANGING) - тест соединения в SM59 не проходит
не нравится почему-то слово TABLE. Со строками все нормально. можно конечно какой-нить XML в виде строки возвращать. но это же потом в сапе парсить нужно. хотелось схалявить.
попробовал еще так RepositoryFunctionParamameter("ET_ORGS", RfcDataType.UNKNOWN, RfcDirection.CHANGING). результат тот же.

попробую диадоковцам написать. может научат как с этим сап-коннектором работать.

Автор:  John Doe [ Пн, мар 28 2016, 13:33 ]
Заголовок сообщения:  Re: sap .NET connector

_garycor_ написал:
Нет. это приблуда SAP CONNECTOR-а.

Вы под SAP CONNECTOR что конкретно понимаете - SAP .Net Connector или что-то иное? Меня очень смущает два фактора - орфографическая ошибка в названии процедуры и полная неосведомленность Google о ней.

Внутренний голос мне подсказывает, что к решениям SAP такой вызов отношения не имеет, по крайней мере прямого. Если же это все таки стандарт, подскажите, где бы можно было об этом почитать. Вопрос интересный, потому как возникают периодически проблемы с Google Protocol Buffers и хотелось бы с предложенным решением, в будущем, тоже ознакомиться.

Автор:  _garycor_ [ Пн, мар 28 2016, 14:01 ]
Заголовок сообщения:  Re: sap .NET connector

Цитата:
Вы под SAP CONNECTOR что конкретно понимаете - SAP .Net Connector или что-то иное?

да. именно sap .net connector.

Цитата:
Меня очень смущает два фактора - орфографическая ошибка в названии процедуры

что за ошибка? не эта случайно RepositoryFunctionParamameter? это c# при вводе так и предлагает выбрать. внутрях sapnco.dll наверное так и прописано.

Цитата:
Если же это все таки стандарт, подскажите, где бы можно было об этом почитать.

на счет стандарта.... не знаю... я уже не уверен, что нужно писать именно так
Code:
[RfcServerFunction(Name = "ZDDK_LOGIN"),
        RepositoryFunctionParamameter("I_LOGIN", RfcDataType.STRING, RfcDirection.IMPORT),
        RepositoryFunctionParamameter("I_PASSWORD", RfcDataType.STRING, RfcDirection.IMPORT),
        RepositoryFunctionParamameter("O_ORGS", RfcDataType.STRING, RfcDirection.EXPORT),
        RepositoryFunctionParamameter("O_TOKEN", RfcDataType.STRING, RfcDirection.EXPORT)]
        public void diadoc_login(RfcServerContext context, IRfcFunction function)
        {

я в первом посте писал, что этот код выдал декомпилятор.... может тут собака и зарыта. и нужно на самом деле по другому все делать. хотя без таблиц все работает.

Автор:  _garycor_ [ Пн, мар 28 2016, 17:17 ]
Заголовок сообщения:  Re: sap .NET connector

вот пример из самого сап коннектора
Code:
            [RfcServerFunction(Name = "STFC_CONNECTION")]
            public static void StfcConnection(RfcServerContext serverContext, IRfcFunction function)
            {
                ...
            String requtext = function.GetString("REQUTEXT");
                ...
               function.SetValue("ECHOTEXT", function.GetString("REQUTEXT"));
               function.SetValue("RESPTEXT", "NCO3: Hello world.");
                ...
            }

правда пример я не пытался компилировать

таких строк типа RepositoryFunctionParamameter("I_LOGIN", RfcDataType.STRING, RfcDirection.IMPORT) вообще нет.
у меня если такие строки убираю, то RFCшка при вызове начинает в дамп валиться с ошибкой типа "Element E_ORGS of container metadata ZDDK_DIADOC_TEST unknown"

Автор:  _garycor_ [ Вт, мар 29 2016, 08:46 ]
Заголовок сообщения:  Re: sap .NET connector

Обманул я на счет RepositoryFunctionParamameter.
это диадоковский класс. Вы правы.
Code:
namespace KonturAPI_SAP
{
    using SAP.Middleware.Connector;
    using System;
    using System.Runtime.CompilerServices;

    [AttributeUsage(AttributeTargets.All, AllowMultiple = true)]
    public class RepositoryFunctionParamameter : Attribute
    {
        public RepositoryFunctionParamameter(string name, RfcDataType type, RfcDirection direction)
        {
            this.Name = name;
            this.Type = type;
            this.Direction = direction;
        }

        public RfcDirection Direction { get; set; }

        public string Name { get; set; }

        public RfcDataType Type { get; set; }
    }
}

Автор:  _garycor_ [ Ср, мар 30 2016, 13:48 ]
Заголовок сообщения:  Re: sap .NET connector

Добрый день, коллеги!
к чему пришел.
как писал AFH, действительно, нужно прописывать хард-кодом репозитарий на стороне сервера.
для этого как раз и создан был класс RepositoryFunctionParamameter.
я от него избавился.
формирую репозитарий руками. делаю так.
Code:
    public class CustomRepository
    {
        public static RfcCustomRepository create()
        {
            RfcCustomRepository repository = new RfcCustomRepository();
            foreach (MethodInfo info in typeof(Handler).GetMethods())
            {
                MethodBase methodFromHandle = MethodBase.GetMethodFromHandle(info.MethodHandle);
                RfcServerFunctionAttribute customAttribute = (RfcServerFunctionAttribute) methodFromHandle.GetCustomAttribute(typeof(RfcServerFunctionAttribute));
                if (customAttribute != null)
                {
                    RfcFunctionMetadata fmd = new RfcFunctionMetadata(customAttribute.Name);
                    switch (fmd.Name)
                    {
                        case "ZDDK_DIADOC_TEST":
                            fmd.AddParameter(new RfcParameterMetadata("I_LOGIN", RfcDataType.STRING, RfcDirection.IMPORT, false));
                            fmd.AddParameter(new RfcParameterMetadata("I_PASSWORD", RfcDataType.STRING, RfcDirection.IMPORT, false));
                            fmd.AddParameter(new RfcParameterMetadata("E_TOKEN", RfcDataType.STRING, RfcDirection.EXPORT, false));
                            fmd.AddParameter(new RfcParameterMetadata("E_ORGS", RfcDataType.STRING, RfcDirection.EXPORT, false));
                           
                            RfcStructureMetadata stru = new RfcStructureMetadata("TLINE");
                            stru.AddField(new RfcFieldMetadata("TDFORMAT", RfcDataType.CHAR, 2, 0, 0, 0));
                            stru.AddField(new RfcFieldMetadata("TDLINE", RfcDataType.CHAR, 132, 0, 0, 0));
                            RfcTableMetadata t = new RfcTableMetadata("ET_ORGS", stru);
                            fmd.AddParameter(new RfcParameterMetadata("ET_ORGS", t, RfcDirection.TABLES, false));
                           
                            break;
                    }
                    repository.AddFunctionMetadata(fmd);
                }
            }
            return repository;
        }
    }

как видно я тут пытаюсь добавить таблицу.

сама функция сервера
Code:
        [RfcServerFunction(Name = "ZDDK_DIADOC_TEST")]
        public void diadoc_login(RfcServerContext context, IRfcFunction function)
        {
            String i_login = function.GetValue("I_LOGIN").ToString();
            String i_password = function.GetValue("I_PASSWORD").ToString();

            IRfcTable tab = function.GetTable("ET_ORGS").Metadata.CreateTable();
            IRfcStructure stru = tab.Metadata.LineType.CreateStructure();
            stru.SetValue("TDFORMAT", "11");
            stru.SetValue("TDLINE", "111111111");
            tab.Append(stru);

            function.SetValue("E_ORGS", "E_ORGS");
            function.SetValue("E_TOKEN", "E_TOKEN");
            function.SetValue("ET_ORGS", tab);
        }

при вызове RFC-функции, получаю ошибку "Индекс находился вне границ массива.".
как только убираю строчку
Code:
            function.SetValue("ET_ORGS", tab);

RFC-функция отрабатывает, но, естественно, не возвращается таблица. только строковые параметры E_ORGS и E_TOKEN.
что я тут не так делаю?

Автор:  _garycor_ [ Ср, мар 30 2016, 15:01 ]
Заголовок сообщения:  Re: sap .NET connector

_garycor_ написал:
Добрый день, коллеги!
к чему пришел.
как писал AFH, действительно, нужно прописывать хард-кодом репозитарий на стороне сервера.
для этого как раз и создан был класс RepositoryFunctionParamameter.
я от него избавился.
формирую репозитарий руками. делаю так.
Code:
    public class CustomRepository
    {
        public static RfcCustomRepository create()
        {
            RfcCustomRepository repository = new RfcCustomRepository();
            foreach (MethodInfo info in typeof(Handler).GetMethods())
            {
                MethodBase methodFromHandle = MethodBase.GetMethodFromHandle(info.MethodHandle);
                RfcServerFunctionAttribute customAttribute = (RfcServerFunctionAttribute) methodFromHandle.GetCustomAttribute(typeof(RfcServerFunctionAttribute));
                if (customAttribute != null)
                {
                    RfcFunctionMetadata fmd = new RfcFunctionMetadata(customAttribute.Name);
                    switch (fmd.Name)
                    {
                        case "ZDDK_DIADOC_TEST":
                            fmd.AddParameter(new RfcParameterMetadata("I_LOGIN", RfcDataType.STRING, RfcDirection.IMPORT, false));
                            fmd.AddParameter(new RfcParameterMetadata("I_PASSWORD", RfcDataType.STRING, RfcDirection.IMPORT, false));
                            fmd.AddParameter(new RfcParameterMetadata("E_TOKEN", RfcDataType.STRING, RfcDirection.EXPORT, false));
                            fmd.AddParameter(new RfcParameterMetadata("E_ORGS", RfcDataType.STRING, RfcDirection.EXPORT, false));
                           
                            RfcStructureMetadata stru = new RfcStructureMetadata("TLINE");
                            stru.AddField(new RfcFieldMetadata("TDFORMAT", RfcDataType.CHAR, 2, 0, 0, 0));
                            stru.AddField(new RfcFieldMetadata("TDLINE", RfcDataType.CHAR, 132, 0, 0, 0));
                            RfcTableMetadata t = new RfcTableMetadata("ET_ORGS", stru);
                            fmd.AddParameter(new RfcParameterMetadata("ET_ORGS", t, RfcDirection.TABLES, false));
                           
                            break;
                    }
                    repository.AddFunctionMetadata(fmd);
                }
            }
            return repository;
        }
    }

как видно я тут пытаюсь добавить таблицу.

сама функция сервера
Code:
        [RfcServerFunction(Name = "ZDDK_DIADOC_TEST")]
        public void diadoc_login(RfcServerContext context, IRfcFunction function)
        {
            String i_login = function.GetValue("I_LOGIN").ToString();
            String i_password = function.GetValue("I_PASSWORD").ToString();

            IRfcTable tab = function.GetTable("ET_ORGS").Metadata.CreateTable();
            IRfcStructure stru = tab.Metadata.LineType.CreateStructure();
            stru.SetValue("TDFORMAT", "11");
            stru.SetValue("TDLINE", "111111111");
            tab.Append(stru);

            function.SetValue("E_ORGS", "E_ORGS");
            function.SetValue("E_TOKEN", "E_TOKEN");
            function.SetValue("ET_ORGS", tab);
        }

при вызове RFC-функции, получаю ошибку "Индекс находился вне границ массива.".
как только убираю строчку
Code:
            function.SetValue("ET_ORGS", tab);

RFC-функция отрабатывает, но, естественно, не возвращается таблица. только строковые параметры E_ORGS и E_TOKEN.
что я тут не так делаю?


еще попробовал вот так
Code:
        [RfcServerFunction(Name = "ZDDK_DIADOC_TEST")]
        public void diadoc_login(RfcServerContext context, IRfcFunction function)
        {
            String i_login = function.GetValue("I_LOGIN").ToString();
            String i_password = function.GetValue("I_PASSWORD").ToString();

            IRfcTable tab = function.GetTable("ET_ORGS");

            FileStream fs = new FileStream("c:\\1\\1.txt", FileMode.Append);
            StreamWriter sw = new StreamWriter(fs);
           
            sw.WriteLine("---------------------------------------");
            tab.Append();
            tab.SetValue("TDLINE", "dsfsdfsdf");
            sw.WriteLine("GetTable_Append_" + tab.CurrentRow.ToString());
            sw.WriteLine("GetTable_RowCount_" + tab.RowCount.ToString());
            sw.WriteLine("GetTable_FieldCount" + tab.Metadata.LineType.FieldCount.ToString());           

            sw.Close(); fs.Close();
        }

вызываю RFC получаю дамп с той же ошибкой. хотя смотрю сформированный файлик и в нем вижу
Code:
---------------------------------------
GetTable_Append_STRUCTURE TLINE { FIELD TDFORMAT= FIELD TDLINE=dsfsdfsdf }
GetTable_RowCount_1
GetTable_FieldCount2

Страница 1 из 3 Часовой пояс: UTC + 3 часа
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/