#pragma once #include "JTerminal.h" #include "ThreadLogging.h" #include "UsbAdcListener.h" #include "UsbDataListener.h" #include "UsbDispatcher.h" #include "SimpleLogger.h" #include "UsbDeviceManager.h" #include "UserAdcChannelsManager.h" #include "RawAdcChannelsManager.h" #include #include #include namespace Incart::Usb { class UsbEventLoopWorker : public QThread { Q_OBJECT const bool MYDEBUG = true; // все сигналы и слоты выполняются в главном потоке // конструктор и деструктор выполняются в главном потоке // метод run() - в собственном потоке // callback - зарершение libusb_fill_bulk_transfer в в потоке из которого вызывается libusb_handle_events // поэтому вся работа непоосредственно с драйвером - в одном потоке -> метод run() // signal - может находиться в произвольном потоке // slot работает исключительно в одном потоке // Реализуем схему - вся работа с Usb отдельном потоке // ответы на команду,визуализацию и файл слушаем непрерывно // внимание - надежность и производительность обусловленна однократным вызовом AllocTransfers() и DeleteTransfers() // обработку сигнала АЦП вынуждены были буферизировать по 20ms, иначе система не успевала обрабатывать заявки // поэтому весь разбор кадров сделали на 20ms буфер // поэтому разделили UsbCmdListener(работает по кадрам, проверяет условие - одина транзакция соответсвует одному кадру) и UsbAdcListener // *1) внимание, для приборов, которые "добивают" usb кадр до 0x40, ОБЯЗАТЕЛЬНО организовать чтение кратно 0x40 const uint maximum_frame_size_for_cmd_parcer = 65535; const uint maximum_frame_size_for_adc_parcer = 0x40 * 2; // *1) эта константа влияет только на порцию чтения данных const uint maximum_frame_size_for_data_parcer = 1024*1024; //const int command_timeout_interval_ms = 250; //const int maximum_waiting_time_for_ep_cmd_ms= 250; const uint time_between_usb_reboots_ms = 1000; const int interval_data_for_adc_listener_ms = 20; const size_t m_maxErrorLogSize = 4*1024*1024; // Максимальный размер лог файла: 4 Мб (можно поменять на другое значение) const size_t m_errorLogFreeBlockSize = 1024 * 1024; // Минимальное кол-во удаляемых байт (т.к. удаление происходит построчно) при уменьшении лог файла protected: QTimer m_TimerLog; std::atomic m_isRun; //std::atomic m_flgConnect; bool m_flgDisConnect; Common::ThreadLogging Log; // пишет логи в буффер (в опреративную память) Common::SimpleLogger m_errorLogger; // пишет логи в файл UsbContext m_usbContext; UsbCmdWriter m_cmd_writer; UsbCmdListener m_cmd_listener; UsbAdcListener m_adc_listener; UsbDataListener m_data_listener; UsbDispatcher m_usbDispatcher; std::shared_ptr m_signalManager; UsbDeviceManager m_deviceManager; public: UsbEventLoopWorker(const std::unordered_map& devicesInfo, const std::vector& cableTypes, ComplexCommandFactory* complexCommandFactory, const std::string& errorLogPath, const std::unordered_set& flags, bool isRawSignals = false, QObject* parent = nullptr) : QThread(parent) , m_isRun(false) , m_flgDisConnect(true) , m_errorLogger(errorLogPath, m_maxErrorLogSize, m_errorLogFreeBlockSize) , m_usbContext(&Log, &m_errorLogger) , m_cmd_writer("cmd", &m_usbContext) , m_cmd_listener("l_cmd", &m_usbContext, maximum_frame_size_for_cmd_parcer) , m_adc_listener("l_adc", &m_usbContext, maximum_frame_size_for_adc_parcer, interval_data_for_adc_listener_ms) , m_data_listener("l_data", &m_usbContext, DATA_INP_ENDPOINT, maximum_frame_size_for_data_parcer, true) , m_usbDispatcher(&m_usbContext, {&m_cmd_writer, &m_cmd_listener, &m_adc_listener, &m_data_listener}) , m_signalManager(isRawSignals ? std::dynamic_pointer_cast(std::make_shared(maximum_frame_size_for_adc_parcer, &m_adc_listener)) : std::dynamic_pointer_cast(std::make_shared(maximum_frame_size_for_adc_parcer, &m_adc_listener))) , m_deviceManager(&m_cmd_writer, &m_cmd_listener, devicesInfo, cableTypes, complexCommandFactory, m_signalManager, flags) { startWork(); } ~UsbEventLoopWorker() override { m_TimerLog.stop(); Common::JTerminal(JCL_YELLOW) << __FUNCTION__ << QThread::currentThreadId(); m_isRun.store(false); wait(); emit slotLog(); } protected: // [Thread: libusb] void run() override { Log << __PRETTY_FUNCTION__ << "start !" << '\n'; m_isRun.store(true); bool isSuccess = m_usbDispatcher.initUsbLib(); if (isSuccess) { isSuccess = m_usbDispatcher.receiveLibusbVersion(); } if (isSuccess) { isSuccess = m_usbDispatcher.allocTransfers(); } if (!isSuccess) { m_isRun.store(false); } int32_t lastTransferStatus; while (m_isRun.load()) { lastTransferStatus = m_usbContext.getLastTransferStatus(); if (lastTransferStatus == LIBUSB_TRANSFER_COMPLETED) { m_usbDispatcher.handleUsbEvents(); m_cmd_writer.exe(); m_cmd_listener.exe(); m_adc_listener.exe(); m_data_listener.exe(); } else { m_cmd_writer.clrData(); m_usbDispatcher.freeTransfers(); // завершаем весь обмен m_usbDispatcher.closeDevice(); // если старое устройство открыто, то закрыть emit signalDisconnect(); std::this_thread::sleep_for(std::chrono::milliseconds(time_between_usb_reboots_ms)); // пауза между циклами опроса готовности устройств if (m_usbDispatcher.openDevice()) { m_signalManager->restart(); m_adc_listener.restart(); m_data_listener.restart(); m_cmd_listener.restart(); m_cmd_writer.restart(); emit signalConnect(); } } } Log << __PRETTY_FUNCTION__ << "stop !" << '\n'; m_usbDispatcher.freeTransfers(); m_usbDispatcher.deleteTransfers(); m_usbDispatcher.closeDevice(); m_usbDispatcher.deinitUsbLib(); } signals: // public signals void deviceIsConnected(); void deviceIsDisconnected(); void cableIsConnected(); void cableIsDisconnected(); void signalError(const std::string& errorType, const std::string& message); // private signals void signalDisconnect(); void signalConnect(); public slots: void slotDisconnect() { if (!m_flgDisConnect) // флаг нужет только для однократного срабатывания DisConnect() { return; } Log << __PRETTY_FUNCTION__ << '\n'; m_flgDisConnect = false; m_deviceManager.handleDeviceDisconnect(); emit deviceIsDisconnected(); } void slotConnect() { Log << __PRETTY_FUNCTION__ << '\n'; //m_flgConnect.store(true); m_flgDisConnect = true; m_deviceManager.handleDeviceConnect(); } void slotLog() { // переодическая выгрузка буфера отладочной печати QString str = Log.get(); QStringList ret = str.split('\n'); for(QString str : ret) { if (str.size() > 0) Common::JTerminal() << str.toStdString(); } } void slotReset() { // имитатор сбоя //Log << __PRETTY_FUNCTION__ << '\n'; //m_usb.SetError(); } public: bool isAdcOn() { return m_adc_listener.isAdcOn(); } UsbDataListener* getDataListener() { return &m_data_listener; } UsbDeviceManager* getDeviceManager() { return &m_deviceManager; } std::shared_ptr getSignalManager() { return m_signalManager; } private slots: void handleDeviceIsConnected() { Common::JTerminal() << __PRETTY_FUNCTION__; emit deviceIsConnected(); } void handleCableIsConnected() { emit cableIsConnected(); } void handleCableIsDisconnected() { emit cableIsDisconnected(); } void handleCommandError(const std::string& errorType, const std::string& message) { emit signalError(errorType, message); } private: void startWork() { Common::JTerminal(JCL_YELLOW) << __PRETTY_FUNCTION__ << QThread::currentThreadId(); //DeviceInfoPrinter::Print(m_devicesInfo); connect(&m_TimerLog,SIGNAL(timeout()), this, SLOT(slotLog())); m_TimerLog.setInterval(10); m_TimerLog.start(); connect(this, &UsbEventLoopWorker::signalConnect, this, &UsbEventLoopWorker::slotConnect, Qt::QueuedConnection); connect(this, &UsbEventLoopWorker::signalDisconnect, this, &UsbEventLoopWorker::slotDisconnect, Qt::QueuedConnection); connect(&m_deviceManager, &UsbDeviceManager::deviceIsConnected, this, &UsbEventLoopWorker::handleDeviceIsConnected); connect(&m_deviceManager, &UsbDeviceManager::cableIsConnected, this, &UsbEventLoopWorker::handleCableIsConnected); connect(&m_deviceManager, &UsbDeviceManager::cableIsDisconnected, this, &UsbEventLoopWorker::handleCableIsDisconnected); connect(&m_deviceManager, &UsbDeviceManager::signalError, this, &UsbEventLoopWorker::handleCommandError); std::cout << __PRETTY_FUNCTION__ << " (0)" << std::endl; start(); } }; } // namespace Incart::Usb