mafulechka
mafulechka30 сентября 2019 г. 6:43

Облачные провайдеры и телеметрия через Qt MQTT

MQTT является важным стандартом для телеметрии, особенно в сценарии IoT.


В Qt Company часто обращаются клиенты и пользователи Qt, чтобы узнать, как подключиться к различным облачным провайдерам и, желательно, чтобы список требований был коротким.

В этой статье хотелось бы предоставить больше информации о том, как создать соединение, просто используя Qt, без каких-либо сторонних зависимостей. Для этого сравнения выбраны следующие облачные провайдеры:

• Amazon IoT Core
• Microsoft Azure IoT Hub
• Google Cloud IoT Core
• Alibaba Cloud IoT Platform

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

Предисловие

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

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

Кроме того, идея состоит в том, чтобы использовать только Qt и/или Qt MQTT для установления соединения. Большинство, если не все, поставщики предоставляют SDK либо для устройств, либо для приложений мониторинга (веб и нативных). Однако использование этих SDK расширяет количество дополнительных зависимостей, что приводит к повышению требований к хранилищу и памяти.

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

На связи

Самые первые шаги для отправки сообщений - создать решение для каждого поставщика, а затем установить TCP-соединение.

Amazon IoT Core

Предполагаем, что вы создали учетную запись AWS и службу IoT Core с консоли AWS в браузере.

Панель управления этого сервиса выглядит так:

Кнопка создания открывает мастер, который поможет настроить первое устройство.

Единственная необходимая информация - это название устройства. Все остальные элементы можно оставить пустыми.

Сервис позволяет автоматически создавать сертификат для последующего подключения.

Храните сертификаты (включая корневой CA) и оставляйте их доступными для использования в приложении.

На данный момент никакой политики не требуется. Этим займемся на более позднем этапе.

Последним отсутствующим элементом, который должен начать реализацию примера, является имя хоста, к которому нужно подключиться. Обратите внимание, что для MQTT вы должны использовать префикс для конкретной учетной записи. Кроме того, вы можете найти информацию на странице настроек информационной панели AWS IOT.

Используя Qt MQTT устанавливается соединение с этими несколькими строками:

const QString host = QStringLiteral("<your-endpoint>.amazonaws.com");

const QString rootCA = QStringLiteral("root-CA.crt");

const QString local = QStringLiteral("<device>.cert.pem");

const QString key = QStringLiteral("<device>.private.key");

QMqttClient client;

client.setKeepAlive(10000);

client.setHostname(host);

client.setPort(8883);

client.setClientId("basicPubSub");

QSslConfiguration conf;

conf.setCaCertificates(QSslCertificate::fromPath(rootCA));

conf.setLocalCertificateChain(QSslCertificate::fromPath(local));

QSslKey sslkey(readKey(key), QSsl::Rsa);
conf.setPrivateKey(sslkey);

client.connectToHostEncrypted(conf);

Несколько деталей важны для успешного соединения:

• Значение keepalive должно быть в пределах определенного порога. 10 секунд кажутся хорошим показателем.
• Порт 8883 является стандартизированным портом для зашифрованных MQTT-соединений.
• ClientID должен быть basicPubSub . Это действительный идентификатор (ID), автоматически сгенерированный во время создания экземпляра IoT Core.

Microsoft Azure IoT Hub

Сначала необходимо создать учетную запись для портала Azure. На панели инструментов необходимо создать новый ресурс “Iot Hub”.

Изначально панель управления может показаться ошеломляющей и непреодолимой, поскольку Microsoft выдвигает на передний план множество облачных сервисов и функций. Поскольку основное внимание уделяется подключению первого устройства, самый простой способ - перейти к Shared access policies (Политике общего доступа) и создать новую политику доступа со всеми включенными правами.

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

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

Далее будет использоваться приложение Azure Device Explorer. Это приложение идеально подходит для тестирования. После запуска введите Сonnection string (Строку подключения) сверху в редактирование теста подключения и нажмите Update (Обновить) .

Вкладка управления позволяет создавать новые тестовые устройства с указанием аутентификации через X509 или Security Keys (Ключи безопасности). Security Keys - это заранее выбранный стандартный метод, к которому стремимся.

Наконец, Device Explorer позволяет создать SAS token, который потребуется для настройки клиента MQTT. Token имеет следующую форму:

HostName=<yourIoTHub>.azure-devices.net;DeviceId=<yourDeviceName>;SharedAccessSignature=SharedAccessSignature sr==<yourIoTHub>.azure-devices.net%2F…..

Требуется только эта часть для аутентификации:

SharedAccessSignature sr==<yourIoTHub>.azure-devices.net%2F…..

Azure IoT Hub также использует TLS для подключения. Чтобы получить root CA (корневой центр сертификации), вы можете клонировать Azure IoT C SDK или получить DigiCert Baltimore Root Certificate вручную. Ни веб-интерфейс, ни Device Explorer (Обозреватель устройств) не предоставляют его.

Чтобы установить соединение из приложения Qt с использованием Qt MQTT, код выглядит следующим образом:

const QString iotHubName = QStringLiteral("<yourIoTHub>");
const QString iotHubHostName = iotHubName + QStringLiteral(".azure-devices.net");
const QString deviceId = QStringLiteral("<yourDeviceName>");

QMqttClient client;
client.setPort(8883);
client.setHostname(iotHubHostName);
client.setClientId(deviceId);
client.setUsername(iotHubHostName + QStringLiteral("/") + deviceId + QStringLiteral("/?api-version=2018-06-30"));
client.setPassword(QLatin1String("SharedAccessSignature sr=<yourIoTHub>.azure-devices.net%2Fdevices…"));

auto caCerts = QSslCertificate::fromData(QByteArray(certificates));
QSslConfiguration sslConf;
sslConf.setCaCertificates(caCerts);

client.connectToHostEncryped(sslConf);

Google Cloud IoT Core

После того, как вы создали учетную запись для Google Cloud Platform, веб-интерфейс предоставляет мастер для запуска вашего первого проекта с использованием Cloud IoT Core.

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

Как и в Microsoft Azure, все доступные службы размещены на панели инструментов. Вы найдете элемент IoT Core в разделе Big Data (Важные данные) с левой стороны.

После некоторого использования Google Cloud Platform, поиск окажется очень полезным для перехода на целевую страницу.

Из самого реестра теперь можно добавлять новые устройства.

Интерфейс запрашивает у вас ключи/сертификаты для вашего устройства. Но у него нет возможности создать что-то из самого сервиса. Существует документация о том, как их создать. И на стадии производства эти этапы, вероятно, будут автоматизированы другим способом. Однако для начала работы необходимы дополнительные шаги, которые могут вызвать затруднение.

Как только ваше устройство внесено в реестр, вы можете начать с реализации на стороне клиента.

В отличие от других провайдеров, Google Cloud IoT Core не использует сертификат устройства при создании соединения. Вместо этого закрытый ключ используется для создания пароля. Сам пароль должен быть сгенерирован, как JSON Web Token. Хотя JSON Web Token являются открытым отраслевым стандартом, это добавляет еще одну зависимость вашему проекту. Что-то должно быть в состоянии создать эти Token’ы. Google предоставляет пример кода, но требуется адаптация для включения его в приложение.

Идентификатор (ID) клиента для соединения MQTT состоит из нескольких параметров и имеет следующую форму:

projects/PROJECT_ID/locations/REGION/registries/REGISTRY_ID/devices/DEVICE_ID

Исходя из личного опыта, следует учитывать чувствительность к регистру. Всё, кроме идентификатора проекта (Project ID), сохраняет ту же прописную букву, что и ваш проект, реестр и устройство. Тем не менее, идентификатор проекта будет храниться в нижнем регистре.

Учитывая все это, простейшая реализация для установления соединения выглядит следующим образом:

const QString rootCAPath = QStringLiteral("root_ca.pem");
const QString deviceKeyPath = QStringLiteral("rsa_private.pem");
const QString clientId = QStringLiteral("projects/PROJECT_ID/locations/REGION/registries/REGISTRY_ID/devices/DEVICE_ID");
const QString googleiotHostName = QStringLiteral("mqtt.googleapis.com");
const QString password = QByteArray(CreateJwt(deviceKeyPath, "<yourprojectID>", "RS256"););

QMqttClient client;
client.setKeepAlive(60);
client.setPort(8883);
client.setHostname(googleiotHostName);
client.setClientId(clientId);
client.setPassword(password);

QSslConfiguration sslConf;
sslConf.setCaCertificates(QSslCertificate::fromPath(rootCAPath));

client.connectToHostEncrypted(sslConf);

Alibaba Cloud IoT Platform

Alibaba Cloud IoT Platform является единственным продуктом, имеющим несколько вариантов: базовую и профессиональную версии . На момент написания этой статьи структура этого продукта, похоже, претерпела изменения. Но это не влияет на исследуемые здесь вопросы, связанные с MQTT.

После создания учетной записи для Alibaba Cloud веб-панель позволяет создать новый экземпляр IoT Platform.

После создания экземпляра интерфейс мастера позволяет создавать продукт и устройство.

Из них понадобится пара деталей для установления MQTT-соединения.

• Product Key (Ключ продукта)
• Product Secret (Секрет продукта)
• Device Name (Имя устройства)
• Device Secret (Секрет устройства)

Реализация требует пару дополнительных шагов. Чтобы получить все специфические свойства MQTT, идентификатор клиента, имя пользователя и пароль создаются путем объединения и подписывания.

Для подключения экземпляра клиента QMqtt этого достаточно.

iotx_dev_meta_info_t deviceInfo;
qstrcpy(deviceInfo.product_key, "<yourproductkey>");
qstrcpy(deviceInfo.product_secret, "<yourproductsecret>");
qstrcpy(deviceInfo.device_name, "<yourdeviceID>");
qstrcpy(deviceInfo.device_secret, "<yourdeviceSecret>");

iotx_sign_mqtt_t signInfo;
int32_t result = IOT_Sign_MQTT(IOTX_CLOUD_REGION_GERMANY, &deviceInfo, &signInfo);

QMqttClient client;
client.setKeepAlive(10000);
client.setHostname(QString::fromLocal8Bit(signInfo.hostname));
client.setPort(signInfo.port);
client.setClientId(QString::fromLocal8Bit(signInfo.clientid));
client.setUsername(QString::fromLocal8Bit(signInfo.username));
client.setPassword(QString::fromLocal8Bit(signInfo.password));

client.connectToHost();

Qt Company не использует QMqttClient::connectToHostEncrypted() , как все другие провайдеры. Alibaba Cloud IoT Platform является единственным поставщиком, который по умолчанию использует не-TLS-соединение. Документировано, что можно использовать один, а также получить rootCA. Однако тот факт, что это возможно, все-таки удивляет.

Стандартный вывод (ограничения)

До сих пор мы устанавливали MQTT-соединение с каждым из поставщиков IoT. Каждый поставщик использует немного отличный от других подход для идентификации и аутентификации устройства, но все эти сервисы соответствуют стандарту MQTT 3.1.1.

Однако, для последующих шагов разработчики должны знать об определенных ограничениях или вариациях стандарта. Это будет обсуждаться далее.

Ни один из провайдеров не имеет встроенной поддержки quality-of-service (QoS) level 2. В некоторой степени это имеет смысл, поскольку для телеметрической информации не требуется многократных шагов для проверки доставки сообщения. Будет ли сообщение обработано и проверено, не представляет особого интереса в данном случае, хотя, разработчик должен знать об этом ограничении.

Чтобы освежить память по терминологии, давайте кратко резюмируем, что такое сохранённые сообщениям и «сообщениям воли».

Сохраненные сообщения хранятся на сервере для будущих подписчиков, чтобы получать последнюю информацию по доступным темам. «Сообщения воли» встроены в запрос на подключение и будут распространяться только в случае неожиданного отключения от клиента.

Amazon IoT Core

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

Сохраненные сообщения не поддерживаются AWS, и попытка отправить оставленное сообщение приведет к закрытию соединения.

AWS IoT Core поддерживает «сообщения воли» в рамках разрешенных тем.

Microsoft Azure IoT Hub

Идентификатор клиента используется для идентификации устройства. Поведение двух устройств с одинаковым идентификатором такое же, как и для Amazon IoT Core.

Сохраненные сообщения не поддерживаются в IoT Hub. Однако в документации говорится, что Hub внутренне добавит флажок, чтобы сервер знал, что сообщения были заданы, как сохраненные.

«Сообщения воли» разрешены и поддерживаться, учитывая ограничения темы, которые будут обсуждаться ниже.

Google Cloud IoT Core

Этот провайдер использует идентификатор клиента и пароль для успешной идентификации устройства.

Сообщения, помеченные, как сохраненные, теряют эту опцию во время доставки. Согласно журналам отладки, они передаются, как обычные сообщения. Не найдено никакой документации о том, может ли он работать аналогично Azure IoT Hub, который перенаправляет этот запрос во внутреннюю очередь сообщений.

«Сообщения воли», кажется, не поддерживаются. Несмотря на то, что можно сохранить «сообщение воли» в операторе подключения, оно игнорируется в случае нерегулярного отключения.

Alibaba Cloud IoT Platform

Тройка идентификатора клиента, имени пользователя и пароля используется для идентификации устройства в продукте.

И «флаг хранения», и «сообщения воли» игнорируются со стороны сервера. Сообщение с указанным сохранением пересылается, как обычное сообщение и теряется после доставки. Сообщения воли» игнорируются и нигде не хранятся во время соединения.

Доступные (пользовательские) темы

MQTT использует иерархию тем для создания детального контекста для сообщений. Темы схожи со структурой каталогов, начиная от общего до конкретного устройства. Одним из примеров иерархии тем будет
Sensors (Датчики)/Europe (Европа)/Germany (Германия)/Berlin (Берлин)/device_xyz/temperature (температура).

Каждый провайдер IoT обрабатывает темы по-своему, поэтому разработчики должны быть очень осторожны с этим разделом.

Amazon IoT Core

Во-первых, необходимо проверить, какие темы можно использовать по умолчанию. На панели инструментов перейдите в раздел Secure (Безопасность)-> Policies (Политики) и выберите созданную по умолчанию policy. Это должно выглядеть так:

AWS IoT Core определяет политики в формате JSON, и вы найдете некоторые из предыдущих деталей, указанных в этом документе. Например, доступные идентификаторы клиента указываются в ресурсе Connect. Это также позволяет объявить, какие темы действительны для публикации, подписки и получения. Можно иметь несколько политик, и они должны быть привязаны к устройствам. Таким образом, он допускает детализированную модель безопасности, при которой определенные типы групп имеют разные права доступа.

Обратите внимание, что описание темы также допускает использование подстановочных знаков. Их не следует путать с подстановочными знаками в стандарте MQTT. Это означает, что вы должны использовать «*» вместо «#», чтобы включить все подтемы.

После того, как вы создали иерархию тем, основанную на ваших потребностях, сам код прост и выглядит так:

client.publish(QStringLiteral("topic_1"), "{\"message\":\"Somecontent\"}", 1);
client.subscribe(QStringLiteral("topic_1"), 1);

Microsoft Azure IoT Hub

IoT Hub просто выступает в качестве интерфейса для подключения существующих решений MQTT к Hub. Пользователю не разрешается указывать какую-либо пользовательскую тему, а также нельзя вводить иерархию тем.

Сообщение может быть опубликовано только в следующей форме:

const QString topic = QStringLiteral("devices/") + deviceId + QStringLiteral("/messages/events/");
client.publish(topic, "{id=123}", 1);

Для подписок существуют аналогичные ограничения.

client.subscribe(QStringLiteral("devices/") + deviceId + QStringLiteral("/messages/devicebound/#"), 1);

Подстановочный знак для подписки используется для получения дополнительной информации, которую IoT Hub может добавить к сообщению. Например, это может быть идентификатор сообщения. Для объединения нескольких свойств, сама же подтема кодируется, как URL. Пример сообщения, отправленного из IoT Hub, включает тему:

devices/TestDevice01/messages/devicebound/%24.mid=7493c5cc-d783-4ecd-8129-d3c87590b544&%24.to=%2Fdevices%2FTestDevice01%2Fmessages%2FdeviceBound&iothub-ack=full

Google Cloud IoT Core

По умолчанию клиент MQTT должен использовать эту тему для публикации:

/devices/<deviceID>/events

Но также можно добавить дополнительные темы, используя Google Cloud Shell или другие API.

В этом случае была создана тема customCross . Дополнительные темы отражены, как подтемы на стороне MQTT, если опубликовать сообщение на эту тему, то это будет выглядеть так:

/devices/<deviceID>/events/customCross

Для подписок пользовательские темы недоступны, и есть только две доступные темы, на которые клиент может подписаться:

/devices/<deviceID>/commands/#
/devices/<deviceID>/config/

Конфиг-сообщения - это сохраненные сообщения из облака. Они будут отправляться при каждом подключении клиента для синхронизации устройства.

Alibaba Cloud IoT Platform

Темами можно легко управлять во вкладке «Topic Categories» (Категории тем) на панели инструментов продукта.

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

Обратите внимание, что тема всегда содержит идентификатор устройства. Это имеет значение для маршрутов связи, как указано ниже.

Маршруты связи

Общение в контексте IoT можно разделить на три категории:

  1. Device to Cloud (D2C)
  2. Cloud to Device (C2D)
  3. Device to Device (D2D)

Первая категория является наиболее распространенной. Устройства предоставляют информацию об их состоянии, данные датчика или любую другую информацию. Разговор в другом направлении происходит в случае предоставления инструкций поведения, управления уровнями отладки или любых общих инструкций.

Что касается связи между устройствами, то нужно быть более многословным в определении в этом контексте. Типичный пример можно взять из домашней автоматизации. При определенной интенсивности света датчик распространяет информацию, и жалюзи реагируют на это автоматически, опускаясь. Здесь все алгоритмы обрабатываются на устройствах и облачный интеллект не требуется. Кроме того, не нужно создавать дополнительные правила или фильтры в самом облачном экземпляре. Также все протестированные провайдеры могут создать экземпляр метода, работающего в облаке, и затем перенаправить команду на другое устройство.

В предыдущем разделе мы уже рассмотрели случаи D2C и C2D. После определения иерархии тем клиент может опубликовать эти темы, а также подписаться на них.

Чтобы убедиться, что соединение C2D работает, выберите вкладку «Test» (Тест) в левой части панели инструментов. Браузер покажет минимальный интерфейс, который позволяет отправлять сообщения с указанной темой.

Кроме того, случай device-to-device (от устройства к устройству) хорошо обрабатывается путем подписки и публикации в теме, как указано в политике.

Microsoft Azure IoT Hub

Можно отправлять сообщения с устройства в облако и наоборот. Тем не менее, пользователь не может свободно выбирать тему.

Для отправки Device Explorer - хорошая утилита, особенно для тестирования свойства пакета свойств.

Связь между устройствами, невозможна при использовании Azure IoT Hub.

Google Cloud IoT Core

Возможна отправка сообщений с устройства в облако, что обеспечивает дополнительную детализацию с подтемами для публикации. Сообщения принимаются по двум доступным темам, как описано в разделе выше.

Поскольку пользовательские разделы по-прежнему содержат идентификатор устройства, невозможно использовать экземпляр Google Cloud IoT Core в качестве стандартного посредника для распространения сообщений между устройствами (D2D).

Панель инструментов для устройства позволяет отправлять команду, а также конфигурацию из облачного интерфейса на само устройство.

Alibaba Cloud IoT Platform

Публикация и Подписка могут осуществляться гибким образом с использованием платформы IoT. (Под)Темы могут быть сгенерированы, чтобы обеспечить больше структуры.

Чтобы проверить отправку сообщения из облака на устройство, Topic List (Список тем) на панели инструментов устройства содержит диалоговое окно.

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

/broadcast/<yourProductName>/

Тема на этом подуровне может быть выбрана свободно.

Рекомендации и другое

Amazon IoT Core

• Особенности MQTT для AWS.
• Доступные темы по умолчанию.
• Руководство по проектированию для иерархий тем, также очень хороший справочник для проектов, не связанных с AWS.

Microsoft Azure IoT Hub

• Особенности MQTT для Azure.

Google Cloud IoT Core

• Особенности MQTT для Cloud IoT Core.
• Генерация ключей для устройств.
• Команды.

Alibaba Cloud IoT Platform

• Особенности MQTT для облачной платформы IoT.
• Руководство по подключению.

Дополнительные примечания

Версия 5 MQTT (MQTT version 5) , кажется, слишком молодой, чтобы ее могли принять крупнейшие поставщики. Это очень прискорбно, учитывая, что последний стандарт добавляет пару функций, особенно полезных в мире IoT. Совместно используемые подписки обеспечивают автоматическую балансировку задач, новая команда аутентификации обеспечивает большую гибкость регистрации устройств, свойств подключения и сообщений, что делает облачные подключения более производительными, их проще ограничивать/настраивать и т. д. Но на данный момент придется ждать его принятия.

Опять же, хотелось бы подчеркнуть, что разработчики не рассматривали ни одну из функций, которые вышеупомянутые решения IoT предоставляют для обработки сообщений после их получения. Это часть совершенно другого исследования.

Кроме того, не было рассмотрено использование RPC провайдерами. У некоторых есть жестко закодированные темы для обработки RPC, такие как Google, различающие команды и конфигурацию. Alibaba даже использует темы по умолчанию для обработки уведомлений об обновлении прошивки через MQTT. TrendMicro опубликовал исследование проблем безопасности в MQTT, и RPC занимает там заметное место, которое обязательно нужно прочитать всем, кто настраивает архитектуру MQTT с нуля.

Как я могу проверить это сам?

Разработчиками создан образец приложения, которое позволяет подключаться к любому из перечисленных поставщиков облачных услуг, когда доступны необходимые сведения. Сам интерфейс довольно прост:

Заключительные слова

Для любого более широкого провайдера IoT и облачных услуг можно подключить приложение на основе телеметрии, используя MQTT (и Qt MQTT). Каждый из них имеет различные варианты деталей подключения, в том числе и стандарт, который полностью доступен для разработчиков.

Разработчики Qt Company с нетерпением ждут принятие MQTT версии 5. Команда AUTH обеспечивает лучшую интеграцию методов аутентификации, а другие функции, такие, как псевдонимы тем и свойства, вносят дополнительные сценарии использования в мир IoT. Кроме того, разделяемая подписка полезна для создания отношений между данными и устройствами. Этот последний пункт может наступить на ноги облачным поставщикам, поскольку их цель - справиться с нагрузкой внутри облака.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Вам это нравится? Поделитесь в социальных сетях!

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:50баллов,
  • Очки рейтинга-4
m
  • molni99
  • 26 октября 2024 г. 1:37

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:80баллов,
  • Очки рейтинга4
m
  • molni99
  • 26 октября 2024 г. 1:29

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20баллов,
  • Очки рейтинга-10
Последние комментарии
ИМ
Игорь Максимов22 ноября 2024 г. 11:51
Django - Урок 017. Кастомизированная страница авторизации на Django Добрый вечер Евгений! Я сделал себе авторизацию аналогичную вашей, все работает, кроме возврата к предидущей странице. Редеректит всегда на главную, хотя в логах сервера вижу запросы на правильн…
Evgenii Legotckoi
Evgenii Legotckoi31 октября 2024 г. 14:37
Django - Урок 064. Как написать расширение для Python Markdown Добрый день. Да, можно. Либо через такие же плагины, либо с постобработкой через python библиотеку Beautiful Soup
A
ALO1ZE19 октября 2024 г. 8:19
Читалка fb3-файлов на Qt Creator Подскажите как это запустить? Я не шарю в программировании и кодинге. Скачал и установаил Qt, но куча ошибок выдается и не запустить. А очень надо fb3 переконвертировать в html
ИМ
Игорь Максимов5 октября 2024 г. 7:51
Django - Урок 064. Как написать расширение для Python Markdown Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55 июля 2024 г. 11:02
QML - Урок 016. База данных SQLite и работа с ней в QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
Сейчас обсуждают на форуме
Evgenii Legotckoi
Evgenii Legotckoi24 июня 2024 г. 15:11
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
t
tonypeachey115 ноября 2024 г. 6:04
google domain [url=https://google.com/]domain[/url] domain [http://www.example.com link title]
NSProject
NSProject4 июня 2022 г. 3:49
Всё ещё разбираюсь с кешем. В следствии прочтения данной статьи. Я принял для себя решение сделать кеширование свойств менеджера модели LikeDislike. И так как установка evileg_core для меня не была возможна, ибо он писался…
9
9Anonim25 октября 2024 г. 9:10
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Следите за нами в социальных сетях