gifts2017

1C Messenger для отправки сообщений, файлов и обмена данными между пользователями 1С, вэб страницы, мобильными приложениями а ля Skype, WhatsApp

Опубликовал Сергей Смирнов (Serginio) в раздел Программирование - Практика программирования

Данная разработка позволяет пользователям 1С обмениваться сообщениями, файлами (до 3 мб). Запрашивать данные у клиента как Вэб или HTTP сервисах.
Основано на технологиях ASP.Net SignaR который использует WebSockets и т.д. для двунаправленного обмена данными. Используется обертка над классами .Net

 

В  свое  время  делал  клиента  для  Whats  App,  и  многие  приемы  взяты  оттуда.  Но  постоянно  они  что-то  меняют.  Плюс  нельзя  отправлять  любые  файлы,  использовать  клиента  как  web  сервис  и т.д.

Эта  статья  является  продолжением  статей

  Использование  сборок  .NET  в    7.x  b  8.x.  Создание  внешних  Компонент 

(Здесь же лежат и файлы клиентов и сервера)

И

  .NET(C#)  для  1С.  Динамическая  компиляция  класса  обертки  для  использования  .Net  событий  в    через  ДобавитьОбработчик  или  ОбработкаВнешнегоСобытия 

 

В  этой  разработке  используются  обе  эти  разработки  и  служит  примером  того,  как  можно  легко  использовать  технологии  .Net  в  1С.

В  данной  разработке  можно  обмениваться  сообщениямиайлами,  запрашивать  данные  от  других  клиентов.  Клиенты  могут  быть  как  на  1С,  так  и  с  использованием  вэб  браузера,  WPF  приложения  и  даже  андроид  приложения  и т.д.

Ну,  начнем  по  порядку.  Начну  с  кода  на  1С.

Первое,  что  нам  нужно, это  подключиться.

 

Попытка

                SignalRClietHello=Врап.ПолучитьТипИзСборки("SignalRClient.SignalRClietHello",Объект.КаталогDLL+"SignalRHelloClient.dll");
                //Сборка=врап.загрузитьСборку(Объект.КаталогDLL+"SignalRHelloClient.dll");
                Клиент=Врап.СоздатьОбъект(SignalRClietHello);
                Клиент.Соединиться(Объект.АдресСервера).Wait();


                фрм=ПолучитьФорму("ВнешняяОбработка.SignalRClient.Форма.ФормаОбработки");
                ЗаполнитьЗначенияСвойств(Фрм.Объект,Объект,"АдресСервера,КаталогDLL,КаталогПолученныхФайлов,ИспользоватьСкомпилированнуюСборку");
                Фрм.врап=врап;
                Фрм.Клиент=Клиент;

                фрм.Открыть();
                Закрыть();
        исключение
                Сообщить(ОписаниеОшибки());

                //    ПоказатьПредупреждение(,Врап.ПоследняяОшибка.Message);
                Врап.ВывестиПоследнююОшибку();
        конецПопытки

 

При  удачном  подключении  открываем  форму  обмена  сообщениями

И  добавим  обработчики  событий

 

&НаКлиенте
Процедура  СоздатьОбертку(NetОбъект)
        //  Создаём  обертку  для  событий  и  подключаем  обработчики
        //ОберткаСобытий=врап.СоздатьОберткуДляСобытий(NetОбъект);
        //  Можно  использовать  уже  скомпилированную  сборку,  так  как  при
        //врап.СоздатьОберткуДляСобытий  создается  файл  ВрапперДляSignalRClient_SignalRClietHello.dll
        ПолучитьОбертку(NetОбъект);

        ДобавитьОбработчик  ОберткаСобытий.ОшибкаСобытия,ОшибкаСобытия;


        ДобавитьОбработчик  ОберткаСобытий.ПриОтключении,  ПриОтключении;
        ДобавитьОбработчик  ОберткаСобытий.ОшибкаСоединения,  ОшибкаСоединения;
        ДобавитьОбработчик  ОберткаСобытий.ПриИзмененииСостоянияСоединения,  ПриИзмененииСостоянияСоединения;
        ДобавитьОбработчик  ОберткаСобытий.ПриПолученииСообщения,  ПриПолученииСообщения;
        ДобавитьОбработчик  ОберткаСобытий.ПриАвторизации,  ПриАвторизации;
        ДобавитьОбработчик  ОберткаСобытий.ПриПолученииФайла,  ПриПолученииФайла;
        ДобавитьОбработчик  ОберткаСобытий.ПриПодключенииНовогоПользователя,  ПриПодключенииНовогоПользователя;
        ДобавитьОбработчик  ОберткаСобытий.ПриОтключенииПользователя,  ПриОтключенииПользователя;
        ДобавитьОбработчик  ОберткаСобытий.ПриПолученииКоманды,  ПриПолученииКоманды;
        ДобавитьОбработчик  ОберткаСобытий.ПриПолученииОтветаКоманды,  ПриПолученииОтветаКоманды;
        //NetОбъект==Клиент
        Клиент.Зарегистрироваться(ИмяПользователя());
КонецПроцедуры


 

Не  буду  расписывать  все  обработчики,  покажу  только  некоторые

 

/

/    параметр  Данные:Аннимный  Тип
//  Свойства  параметра
//  id:System.String
 //  ИмяПользователя:System.String
 //  СписокПользователей:System.Collections.Generic.List`1[[SignalRClient.User,  SignalRClient,  Version=1.0.0.0,  Culture=neutral,  PublicKeyToken=null]]
&НаКлиенте

Процедура  ПриАвторизации(Данные)
        пользователи=Элементы.Получатель.СписокВыбора;
        пользователи.Очистить();
        Элем=пользователи.Добавить("Всем","Всем");
        ЭтотОбъект.Получатель="Всем";

        Сообщить("ПриАвторизации  "+Врап.ВСтроку(Данные));
        ИД=  Данные.id;

        Для  каждого  Юзер    Из  Данные.СписокПользователей    Цикл

                ДобавитьНовогоПользователя(Юзер.ConnectionId,  Юзер.Name)


        КонецЦикла;
КонецПроцедуры

//    параметр  Данные:Аннимный  Тип
//  Свойства  параметра
//  ОтКого:System.String
 //  FileName:System.String
 //  id:System.String
 //  Данные:System.Byte[]
&НаКлиенте

Процедура  ПриПолученииФайла(Данные)
        Сообщить("ПриПолученииФайла  "+Врап.ВСтроку(Данные));
        Массив=Данные.Данные;
        имяФайла  =Врап.ПолучитьТип("System.IO.Path").Combine(Объект.КаталогПолученныхФайлов,  Данные.FileName);
        Врап.ПолучитьТип("System.IO.File").WriteAllBytes(имяФайла,Массив);
        ДобавитьСообщение(Данные.ОтКого,  "Отправлен  файл  "+  Данные.FileName,  Данные.id,  имяФайла);

        Клиент.ОтправитьСообщение(СтрШаблон("Получен  файл  %1  размер  %2  байт",  Данные.FileName,Врап.ОбернутьЛюбойОбъект(Данные.Данные).Length),  Данные.id);
КонецПроцедуры

//    параметр  Данные:Аннимный  Тип
//  Свойства  параметра
//  Имя:System.String
 //  сообщение:System.String
 //  IdОтправителя:System.String
&НаКлиенте
Процедура  ПриПолученииСообщения(Данные)
        Сообщить("ПриПолученииСообщения  "+Врап.ВСтроку(Данные));
        ДобавитьСообщение(Данные.Имя,Данные.сообщение,Данные.IdОтправителя,"")
КонецПроцедуры 

 

Здесь  применяются  объекты  и  статические  методы  классов  .Net,  обернутые  через  врап=новый  COMОбъект("NetObjectToIDispatch45");

Отправка  сообщений  происходит  также  через  Net  объект

 

&НаКлиенте
Процедура  КомандаОтправитьСообщение(Команда)
        //  Вставить  содержимое  обработчика.
        Если  ПустаяСтрока(объект.Сообщение)  Тогда
                показатьПредупреждение(,"Сообщение  не  заполнено");
                возврат
        КонецЕсли;

        ИДПолучателя=ЭтотОбъект.Получатель;

        Если  ПустаяСтрока(ИДПолучателя)  Тогда
                показатьПредупреждение(,"Не  выбран  получатель");
                возврат
        КонецЕсли;

        Если  Объект.ПоИмени  и  ИДПолучателя="Всем"  Тогда
                показатьПредупреждение(,"Нельзя  отправлять  по  имени  Всем");
                возврат
        КонецЕсли;

        Сообщение=Объект.Сообщение;
        Попытка
                Если  Объект.ПоИмени  Тогда
                        Пользователи=  Элементы.Получатель.СписокВыбора;
                        Имя=Пользователи.НайтиПоЗначению(ИДПолучателя).Представление;
                        //  Можно  отправлять  ассинхронно  без  Wait
                        //  Но  могут  быть  проблемы  с  отловом  ошибок
                        //  На  всякий  случай  поставим  1  сек  ожидания
                        //  для  предотвращения  взаимных  блокировок
                        Клиент.ОтправитьСообщениеПоИмени(Сообщение,Имя).Wait(1000);
                ИначеЕсли  Получатель="Всем"  Тогда
                        Клиент.ОтправитьСообщение(Сообщение,  "").Wait(1000);
                Иначе
                        Клиент.ОтправитьСообщение(Сообщение,ИДПолучателя).Wait(1000);
                КонецЕсли;

                Объект.Сообщение="";

        исключение
                Сообщить(ОписаниеОшибки());
                Если  Врап.ПоследняяОшибка<>  Неопределено  Тогда
                        ПоказатьПредупреждение(,Врап.ПоследняяОшибка.Message);
                КонецЕсли;
        КонецПопытки;
КонецПроцедуры

 

Реализованы  следующие  команды:  КомандаОтправитьФайл,  КомандаВычислитьСложение  (отправляется  команда  и  параметры  клиенту,  результат  получается  асинхронно).

Перейду  с  серверной  части.  Она  написана  на  Asp.Net  с  использованием  Nancy  в  котором  кроме  сервера  SignalR  реализован  сайт  с  клиентом  SignalR

Вот  реализация  Хаба

 

public  class  HelloHub  :  Hub

{

 static  List<User>  Users  =  new  List<User>();

 

 bool  ПользовательЗарегистрирован(User  user)

      {

       var  curId  =  Context.ConnectionId;

var  имя  =  Users.Where(x  =>  x.ConnectionId  ==  curId).Select (x=>x.Name).SingleOrDefault();

                        user.Name  =  имя;

                        user.ConnectionId  =  curId;

                        return  (!String.IsNullOrWhiteSpace(имя));

 }

 

 public  void  SendEcho(string  str,  string  Кому)

   {

       var  user  =  new  User();

       if  (!ПользовательЗарегистрирован(user))

            return;

 

      if  (string.IsNullOrWhiteSpace(Кому))

             Clients.Others.addMessage(user.Name,  str,  user.ConnectionId);

                  else

             Clients.Client(Кому).addMessage(user.Name,  str,  user.ConnectionId);

   }

 


  public  void  SendByName(string  str,  string  Кому)

        {

           var  user  =  new  User();

           if  (!ПользовательЗарегистрирован(user))

                 return;

 

 foreach(var  клиент  in  Users.Where(x  =>  x.Name.ToUpper()  ==  Кому.ToUpper()).Select(x  =>  x.ConnectionId))

    Clients.Client(клиент).addMessage(user.Name,  str,  user.ConnectionId);

  }


  public  void  Send(string  name2,  string  message)

       {

             var  user  =  new  User();

             if  (!ПользовательЗарегистрирован(user))

                   return;

 

              Clients.All.addMessage(user.Name,  message,  user.ConnectionId);

       }

 

  

  public  void  SendFile(string  Кому,string  FileName,  byte[]  Data,  bool  isCompressed)

     {

           var  user  =  new  User();

           if  (!ПользовательЗарегистрирован(user))

                                return;

 

          if  (Users.Any(x  =>  x.ConnectionId  ==  Кому))

              {

Clients.Client(Кому).GetFile(user.Name,FileName,user.ConnectionId,Data,isCompressed);

 

                }

 

  public  void  EvaluteCommand(string  Кому,string  command,  byte[]  Data,  bool isCompressed)

       {

           var  user  =  new  User();

           if  (!ПользовательЗарегистрирован(user))

                 return;

 

           if  (Users.Any(x  =>  x.ConnectionId  ==  Кому))

           {

 Clients.Client(Кому).EvaluteCommand(user.Name,command,user.ConnectionId,Data,  isCompressed);

                        }

 

                }

 

  public  void  ResultCommand(string  Кому,string  command,byte[]  Data,bool  isCompressed)

                {

                        var  user  =  new  User();

                        if  (!ПользовательЗарегистрирован(user))

                                return;

 

 

  if  (Users.Any(x  =>  x.ConnectionId  ==  Кому))

   {

 Clients.Client(Кому).ResultCommand(user.Name,command,user.ConnectionId,Data,  isCompressed);

                        }

 

                }

                //  Подключение  нового  пользователя

  

  public  void  Connect(string  userName)

  {

    var  id  =  Context.ConnectionId;

    if  (!Users.Any(x  =>  x.ConnectionId  ==  id))

       {

           Users.Add(new  User  {  ConnectionId  =  id,  Name  =  userName  });

           //  Посылаем  сообщение  текущему  пользователю

           Clients.Caller.onConnected(id,  userName,  Users);

           //  Посылаем  сообщение  всем  пользователям,  кроме  текущего

            Clients.AllExcept(id).onNewUserConnected(id,  userName);

                        }

                }

 

                //  Отключение  пользователя

  public  override  System.Threading.Tasks.Task  OnDisconnected(bool  stopCalled)

  {

       var  id  =  Context.ConnectionId;

       var  item  =  Users.FirstOrDefault(x  =>  x.ConnectionId  ==id);

            if  (item  !=  null)

                    {

                        Users.Remove(item);

                        Clients.All.onUserDisconnected(id,  item.Name);

                      }

 

                        return  base.OnDisconnected(stopCalled);

                }

        }

 

Ссылки на SignalR  Работа с SignalR

http://metanit.com/sharp/mvc5/16.1.php

Публикация вэб сервиса

http://professorweb.ru/my/ASP_NET/sites/level3/3_1.php

http://metanit.com/sharp/mvc/13.2.php

Файлы для публикации лежат в ..\SignalR\ФайлыДляПубликации\

Для сервера SignalR требуется сервер должен использовать Windows 8, Windows Server 2012 или более поздней версии, и WebSocket должен быть включен в IIS.

 http://www.asp.net/signalr/overview/getting-started/supported-platforms


Попробовал поработать с Xamarin. В общем использовать можно, но еще сыро. Проще использовать WinMobile 10.

В папке SignalRMobile лежит недоделанный пример. Проблемы с инициализацией IOS модуля, ограничения на события с 4 мя параметрами и отсутствием времени не стал его доделывать.

 

В папке SignalRHelloClient лежит сборка для использования её в 1С

В папке SignalRClient лежит WPF приложение. Там же лежит основной модуль клиента SignalRClietHello.cs используемый обеими клиентами.

 

SignalRNancy это сервер.

 

Все файлы лежат по ссылке   Использование  сборок  .NET  в    7.x  b  8.x.  Создание  внешних  Компонент

Для использования нужна  .NET Framework 4.6

https://msdn.microsoft.com/Ru-ru/library/ms171868(v=vs.110).aspx

Для VS 2015 нужно обновление апдейт

Внешний отчет лежит в ВнешниеОтчеты\SignalRClient.epf

См. также

Подписаться Добавить вознаграждение

Комментарии

1. Сергей Смирнов (Serginio) 25.12.15 16:44
Добавил рабочий вариант для Android на Xamarin. Проверял на эмуляторе. На IOS не проверял.
2. Сергей Смирнов (Serginio) 25.12.15 16:47
Из пакетов убрал Xamarin.Forms.2.0.0.6490 он 20 мб
3. Максим Кузнецов (Makushimo) 06.05.16 05:37
а что нужно прочитать, чтобы понять то, что тут написано?
Nizikov; VVi3ard; zif74; METAL; +4 Ответить 1
4. Сергей Смирнов (Serginio) 06.05.16 08:11
(3) В статье есть ссылки
Ссылки на SignalR Работа с SignalR

http://metanit.com/sharp/mvc5/16.1.php

Публикация вэб сервиса

http://professorweb.ru/my/ASP_NET/sites/level3/3_1.php

http://metanit.com/sharp/mvc/13.2.php

Если нужно только для 1С, то только опубликовать вэб сервис. Файлы здесь

http://infostart.ru/public/238584/

еще можно почитать
http://infostart.ru/public/448668/
http://infostart.ru/public/417830/
Для написания сообщения необходимо авторизоваться
Прикрепить файл
Дополнительные параметры ответа