Внешние компоненты мобильной платформы 1С для ОС Андроид




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2024-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

18 Comments

  1. user623969_dusa

    почему си на андроиде? потому что внешние компоненты на си легче писать??

    Reply
  2. Xershi

    Возьму на заметку.

    Как раз подхожу к этапу разработки:

    https://forum.infostart.ru/forum15/topic202825/

    Reply
  3. ni032mas

    Отличная статья, спасибо. Я все время думаю, зачем это все? Получается, чтобы написать хорошее мобильное бизнес приложение на платформе 1С нужны: C++, Java и 1C разработчики, это как минимум дорого. А найти три в одном не простая задача для HR. Плюс ко всему приложение на 1С проигрывает нативным по скорости работы, по отзывчивости, по возможности кастомизации интерфейса. А еще есть куча багов от 1С, порой которые ставят крест на всем проекте. Сам уже несколько лет забил на мобильную платформу и начал писать нативно под Android.

    Reply
  4. KostyaBu

    Уважаемые коллеги Прошу помощи пишу внешнюю компоненту которая достает из буфера обмена скопированные данные.

    В разработке компонента более менее разобрался добавил свою процедуру которая успешно возвращает в 1с » Привет мир».

    Однако при инициализации ClipboardManager(а) Сервис с помощью которого можно обратится к буферу: m_ClipboardManager = (ClipboardManager)

    m_Activity.getSystemService(Context.CLIPBOARD_SERVICE) Компонента отваливается вместе с 1с молча падает. Однако подобный пример только с датчиками движения, как на итс работает.

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

    Благодарен любой подсказке.

    Reply
  5. IgorKissil

    (4) Реализовал буфер в своей компоненте: https://infostart.ru/public/779912/ С получением сервиса и операциями copy/paste не нашел никаких проблем — вы что-то делаете не так. Трудности будут с получением callback’а об изменении буфера, потребуются знания многопоточности и одна хитрость. Но тоже, как видите, преодолимо.

    Reply
  6. KostyaBu

    (5) Прежде всего хотел выразить благодарность что вы откликнулись. Увидел что вы уже все реализовали и я скачал. спасибо! Но очень хочется разобраться времени много потрачено нужно разобраться.

    напишу кусок из Вашего примера дописка в инициализации класса:

    private long m_V8Object; // 1C application context

    private Activity m_Activity; // custom activity of 1C:Enterprise

    private BroadcastReceiver m_Receiver;

    private String text;

    private ClipboardManager m_ClipboardManager = null;

    // private ClipData.Item item= null;;

    public LockState(Activity activity, long v8Object)

    {

    m_Activity = activity;

    m_V8Object = v8Object;

    m_Context = activity.getBaseContext();

    m_ClipboardManager =(ClipboardManager) m_Context.getSystemService(Context.CLIPBOARD_SERVICE); (Здесь падает)

    }

    хотя сервис доступа к сенсорам получаю без проблем как в примере Степ каунтер.

    m_SensorManager = (SensorManager) m_Context.getSystemService(Context.SENSOR_SERVICE);

    Заранее благодарен за ответ!

    Reply
  7. IgorKissil

    (6) Зачем вам getBaseContext? Используйте контекст активности или приложения

    Reply
  8. KostyaBu

    (7)

    я его написал от безисходности т.к

    m_Activity.getSystemService(Context.CLIPBOARD_SERVICE);

    тоже падает (вся система вместе с 1с закрывается)

    Reply
  9. KostyaBu

    (7)

    такое чувство. что на CLIPBOARD_SERVICE есть какое то ограничение. а с m_SensorManager = (SensorManager) m_Context.getSystemService(Context.SENSOR_SERVICE); как в примере от 1с проходит все гладко но Вы в своей компоненте доказали что я где- то что то не понимаю ((. Осталось понять что.

    Reply
  10. user1169242

    Коллеги. Прошу совета. Задолбался уже искать причину.

    void connect() {
    Initialize();
    StartAPKMethod();
    }
    
    void disconnect() {
    StopAPKMethod();
    }
    
    void StartAPKMethod(){
    if (obj){
    JNIEnv* env = getJniEnv();
    jmethodID methID = env->GetMethodID(cc, «start», «()V»);
    env->CallVoidMethod(obj, methID);
    }
    }
    void StopAPKMethod(){
    if (obj) {
    JNIEnv* env = getJniEnv();
    jmethodID methID = env->GetMethodID(cc, «stop», «()V»);
    env->CallVoidMethod(obj, methID);
    env->DeleteGlobalRef(obj);
    env->DeleteGlobalRef(cc);
    obj = nullptr;
    cc = nullptr;
    }
    }

    Показать

    Если сделать так:

    for(int i=0; i<100; ++i){
    connect();
    disconnect();
    }

    То приложение стабильно падает. Собственно пример синтетический. На самом деле приложение периодически падает при закрытии мобильного приложения 1с. Очевидно все дело в уничтожении объектов, но куда копать и почему оно падает не всегда — понять не могу

    Reply
  11. ineshyk

    Скачал Демку 1С, залил на телефон. При запуске пишет ошибка подключения внешней компоненты

    Reply
  12. Ignat87

    Ребята, помогите. В VS 2019 собрал пустое приложение (i386 b ARM) из шаблона ИТС для мобильных платформ, но на участке кода ПодключитьВнешнююКомпоненту(«ОбщийМакет.TestVK», «TestVK», ТипВнешнейКомпоненты.Native) платформа просто падает без описания ошибок.

    Я новичек в этом деле, в чем проблема — не пойму. Сделал вроде все правильно. Прикреплю файл с проектом, может кто подскажет, в чем проблема.

    Reply
  13. refostart

    Хотелось бы уточнить, есть ли какой-то способ что-то логировать в Java-коде? Написание почти с нуля в лоб внешних компонент весьма проблематичная задача.

    Reply
  14. IgorKissil

    (13)Посмотрите шагомер от 1С, файл jnienv.cpp. Логирование делается как описано здесь: https://developer.android.com/ndk/reference/group/logging Ну и почитайте https://developer.android.com/studio/debug/am-logcat

    Reply
  15. ripreal1

    Столкнулись с проблемой, что компонента не может вызывать java классы, если она была инициализирована на сервере. Расследование показало, что причина кроется в методе IAndroidComponentHelper->FindClass(), который возвращает null, При вызове компоненты с клиента все работает отлично. Отрывок кода на C++:

    Код использую типовой:

    IAndroidComponentHelper* helper = (IAndroidComponentHelper*)cnn->GetInterface(eIAndroidComponentHelper);
    if (helper)
    {
    WCHAR_T* className = nullptr;
    convToShortWchar(&className, L»org.biterp.prmqmobile.MainApp»);
    jclass ccloc = helper->FindClass(className); // Метод возвращает NULL
    delete[] className;
    className = nullptr;
    if (ccloc)
    {
    JNIEnv* env = getJniEnv();
    cc = static_cast<jclass>(env->NewGlobalRef(ccloc));
    env->DeleteLocalRef(ccloc);
    jobject activity = helper->GetActivity();
    // call of constructor for java class
    jmethodID methID = env->GetMethodID(cc, «<init>», «(Landroid/app/Activity;J)V»);
    jobject objloc = env->NewObject(cc, methID, activity, (jlong)cnn);
    obj = static_cast<jobject>(env->NewGlobalRef(objloc));
    env->DeleteLocalRef(objloc);
    methID = env->GetMethodID(cc, «show», «()V»);
    env->CallVoidMethod(obj, methID);
    env->DeleteLocalRef(activity);
    }
    }
    

    Показать

    Кому-нибудь удалось выывать Java код для компоненты с сервера?

    Reply
  16. IgorKissil

    (15)Верно. Мобильные компоненты работают только на клиенте. Потому что есть мобильный клиент. Как вы думаете, где выполняются его Функции и процедуры с директивой &НаСервере? Правильно, на сервере предприятия с среде windows или Linux.

    Reply
  17. ripreal1

    (16) Я пытаюсь запустить не на мобильном клиенте, а на автономном мобильном приложении. Вроде сами разработчики платформы говорили, что аннотации &НаСервере оставлены там только ради обратной совместимости и весь код исполняется в рамках одного приложения. Как-то странно, что «На Сервере» не работает.

    Reply
  18. lokli

    Добрый день. Разбираю ваши исходники, пытаюсь разобраться в данном вопросе. Подскажите, в каком направлении копать, что бы передавать в 1С данные из бродкаста (например, как при работе со сканером или с буфером обмена)?

    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *