diff --git a/.gitignore b/.gitignore index df2110047..96111bd8a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,4 @@ build-rt1051/ src/git-fame compile_commands.json +source/version.hpp diff --git a/module-apps/application-messages/windows/MessagesMainWindow.cpp b/module-apps/application-messages/windows/MessagesMainWindow.cpp index ffad9762e..b6cf74726 100644 --- a/module-apps/application-messages/windows/MessagesMainWindow.cpp +++ b/module-apps/application-messages/windows/MessagesMainWindow.cpp @@ -21,6 +21,7 @@ #include "../MessagesStyle.hpp" #include "service-db/api/DBServiceAPI.hpp" +#include "service-cellular/api/CellularServiceAPI.hpp" #include #include @@ -53,6 +54,7 @@ void MessagesMainWindow::buildInterface() { list->setProvider(threadModel); bottomBar->setActive(BottomBar::Side::LEFT, true); + bottomBar->setActive(BottomBar::Side::CENTER, true); bottomBar->setActive(BottomBar::Side::RIGHT, true); bottomBar->setText(BottomBar::Side::LEFT, @@ -73,6 +75,7 @@ void MessagesMainWindow::buildInterface() { searchImage = new gui::Image(this, 480 - 48 - 26, 55, 0, 0, "search"); // setFocusItem( list ); + } void MessagesMainWindow::destroyInterface() { AppWindow::destroyInterface(); diff --git a/module-apps/application-settings/ApplicationSettings.cpp b/module-apps/application-settings/ApplicationSettings.cpp index 0d735dc75..5c48aa736 100644 --- a/module-apps/application-settings/ApplicationSettings.cpp +++ b/module-apps/application-settings/ApplicationSettings.cpp @@ -12,14 +12,21 @@ #include "windows/SettingsMainWindow.hpp" #include "windows/LanguageWindow.hpp" #include "windows/BtWindow.hpp" + #include "windows/UITestWindow.hpp" +#include "windows/TestMessageWindow.hpp" + + #include "ApplicationSettings.hpp" +#include "service-cellular/ServiceCellular.hpp" + namespace app { ApplicationSettings::ApplicationSettings(std::string name, std::string parent, bool startBackgound) : Application( name, parent, startBackgound ) { + busChannels.push_back(sys::BusChannels::ServiceCellularSMSNotification); } ApplicationSettings::~ApplicationSettings() { @@ -77,6 +84,7 @@ void ApplicationSettings::createUserInterface() { window = new gui::UiTestWindow(this); windows.insert(std::pair(window->getName(), window)); + } void ApplicationSettings::destroyUserInterface() { diff --git a/module-apps/application-settings/CMakeLists.txt b/module-apps/application-settings/CMakeLists.txt index 305059732..fe97bba9c 100644 --- a/module-apps/application-settings/CMakeLists.txt +++ b/module-apps/application-settings/CMakeLists.txt @@ -18,10 +18,14 @@ target_sources( ${PROJECT_NAME} "${CMAKE_CURRENT_LIST_DIR}/windows/SettingsMainWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/LanguageWindow.cpp" "${CMAKE_CURRENT_LIST_DIR}/windows/BtWindow.cpp" + "${CMAKE_CURRENT_LIST_DIR}/windows/UITestWindow.cpp" + + PUBLIC "${CMAKE_CURRENT_LIST_DIR}/ApplicationSettings.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/SettingsMainWindow.hpp" "${CMAKE_CURRENT_LIST_DIR}/windows/LanguageWindow.hpp" + ) diff --git a/module-apps/application-settings/windows/TestMessageWindow.cpp b/module-apps/application-settings/windows/TestMessageWindow.cpp new file mode 100644 index 000000000..a0b883e7f --- /dev/null +++ b/module-apps/application-settings/windows/TestMessageWindow.cpp @@ -0,0 +1,150 @@ +/* + * TestMessageWindow.cpp + * + * Created on: 3 gru 2019 + * Author: kuba + */ + +#include +#include + +#include "service-appmgr/ApplicationManager.hpp" + +#include "../ApplicationSettings.hpp" + +#include "i18/i18.hpp" + +#include "Label.hpp" +#include "Margins.hpp" +#include "TestMessageWindow.hpp" +#include + +#include "service-cellular/api/CellularServiceAPI.hpp" + +namespace gui { + +TestMessageWindow::TestMessageWindow(app::Application *app) : + AppWindow(app, "Message") { + setSize(480, 600); + + buildInterface(); +} + +void TestMessageWindow::rebuild() { + + + destroyInterface(); + buildInterface(); + +} + +void textSetInput(Text * text, bool numeric = true) +{ + /// tu ustawiasz że coś jest edytowalne + text->setEditMode(gui::Text::EditMode::EDIT); + text->setFont(style::window::font::bigbold); + /// tu ustawiasz tak naprawdę przez tą listę InputMode::digit etc jakie kolejne tryby wprowadzania są wybierane + if(numeric) { + text->setInputMode(new InputMode({InputMode::digit})); + } else { + text->setInputMode(new InputMode({InputMode::ABC, InputMode::abc, InputMode::digit})); + } +} + +void TestMessageWindow::buildInterface() { + AppWindow::buildInterface(); + bottomBar->setActive(BottomBar::Side::CENTER, true); + bottomBar->setActive(BottomBar::Side::RIGHT, true); + bottomBar->setText(BottomBar::Side::CENTER, + utils::localize.get("common_select")); + bottomBar->setText(BottomBar::Side::RIGHT, + utils::localize.get("common_back")); + + topBar->setActive(TopBar::Elements::SIGNAL, true); + topBar->setActive(TopBar::Elements::BATTERY, true); + + receivedLabel = new gui::Label(this,10, 50, 480-20, 50, "Received SMS"); + receivedLabel->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + receivedLabel->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES); + + receiveNumber = new gui::Label(this,110, 190, 480-120, 50); + receiveNumber->setFont(style::window::font::small); + + RecNumberLabel = new gui::Label(this,10, 190, 90, 50, "Number"); + RecNumberLabel->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + RecNumberLabel->setFont(style::window::font::small); + RecNumberLabel->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES); + + receiveMessage = new gui::Text(this, 110, 120, 480-120, 50); + textSetInput(receiveMessage); + receiveMessage->setFont(style::window::font::small); + + RecMessageLabel = new gui::Label(this, 10, 120, 90, 50, "Message"); + RecMessageLabel->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + RecMessageLabel->setFont(style::window::font::small); + RecMessageLabel->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES); + + + sendLabel = new gui::Label(this,10, 290, 480-20, 50, "New SMS"); + sendLabel->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + sendLabel->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES); + + sendMessage = new gui::Text(this,110, 360, 480-120, 50); + textSetInput(sendMessage,false); + sendMessage->setText("Siema tu PurePhone :)"); + sendMessage->setFont(style::window::font::small); + + sendNumber = new gui::Text(this,110, 430, 480-120, 50); + textSetInput(sendNumber); + sendNumber->setFont(style::window::font::small); + + SendNumberLabel = new gui::Label(this, 10, 430, 90, 50, "Number"); + SendNumberLabel->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + SendNumberLabel->setFont(style::window::font::small); + SendNumberLabel->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES); + + SendMessageLabel = new gui::Label(this, 10, 360, 90, 50, "Message"); + SendMessageLabel->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + SendMessageLabel->setFont(style::window::font::small); + SendMessageLabel->setEdges(RectangleEdgeFlags::GUI_RECT_EDGE_NO_EDGES); + + sendButton = new gui::Label(this,10, 500, 480-20, 50, "Send SMS"); + sendButton->setAlignement(gui::Alignment( gui::Alignment::ALIGN_VERTICAL_CENTER, gui::Alignment::ALIGN_HORIZONTAL_CENTER)); + sendButton->setPenWidth(1); + sendButton->setPenFocusWidth(3); + sendButton->activatedCallback = [=] (gui::Item& item) { + LOG_INFO("Send SMS callback" ); + CellularServiceAPI::SendSMS(application, sendNumber->getText(), sendMessage->getText()); + return true; }; + + sendButton->setNavigationItem(gui::NavigationDirection::UP, sendNumber); + sendNumber->setNavigationItem(gui::NavigationDirection::UP, sendMessage); + + sendMessage->setNavigationItem(gui::NavigationDirection::DOWN, sendNumber); + sendNumber->setNavigationItem(gui::NavigationDirection::DOWN, sendButton); + + + +} +void TestMessageWindow::destroyInterface() { + AppWindow::destroyInterface(); + + this->focusItem = nullptr; + + children.clear(); +} + +TestMessageWindow::~TestMessageWindow() { +} + +void TestMessageWindow::onBeforeShow(ShowMode mode, SwitchData *data) { + setFocusItem( sendButton ); +} + +void TestMessageWindow::cellularMessageCallback(UTF8& number, UTF8& message) +{ + receiveNumber->setText(number); + receiveMessage->setText(message); + application->refreshWindow( RefreshModes::GUI_REFRESH_FAST ); +} +} /* namespace gui */ diff --git a/module-apps/application-settings/windows/TestMessageWindow.hpp b/module-apps/application-settings/windows/TestMessageWindow.hpp new file mode 100644 index 000000000..103c8c12b --- /dev/null +++ b/module-apps/application-settings/windows/TestMessageWindow.hpp @@ -0,0 +1,61 @@ +/* + * TestMessageWindow.hpp + * + * Created on: 3 gru 2019 + * Author: kuba + */ + +#ifndef MODULE_APPS_APPLICATION_SETTINGS_WINDOWS_TESTMESSAGEWINDOW_HPP_ +#define MODULE_APPS_APPLICATION_SETTINGS_WINDOWS_TESTMESSAGEWINDOW_HPP_ + + +#include +#include + +#include "AppWindow.hpp" +#include "gui/widgets/Label.hpp" +#include "gui/widgets/Text.hpp" +#include "gui/widgets/Image.hpp" +#include "gui/widgets/Window.hpp" +#include "gui/widgets/BottomBar.hpp" +#include "gui/widgets/TopBar.hpp" + +namespace gui { + +/* + * + */ +class TestMessageWindow: public AppWindow { +protected: + gui::Label* sendLabel; + gui::Label* sendButton; + + gui::Label* receivedLabel; + + gui::Label* SendNumberLabel; + gui::Label* SendMessageLabel; + gui::Text* sendNumber; + gui::Text* sendMessage; + + gui::Label* RecNumberLabel; + gui::Label* RecMessageLabel; + gui::Label* receiveNumber; + gui::Text* receiveMessage; + + +public: + TestMessageWindow( app::Application* app ); + virtual ~TestMessageWindow(); + + //virtual methods + void onBeforeShow( ShowMode mode, SwitchData* data ) override; + + void rebuild() override; + void buildInterface() override; + void destroyInterface() override; + void cellularMessageCallback(UTF8& number, UTF8& message); +}; +} /* namespace gui */ + + +#endif /* MODULE_APPS_APPLICATION_SETTINGS_WINDOWS_TESTMESSAGEWINDOW_HPP_ */ diff --git a/module-cellular/Modem/TS0710/DLC_channel.cpp b/module-cellular/Modem/TS0710/DLC_channel.cpp index 16eb63380..a035735d1 100644 --- a/module-cellular/Modem/TS0710/DLC_channel.cpp +++ b/module-cellular/Modem/TS0710/DLC_channel.cpp @@ -85,10 +85,13 @@ ssize_t DLC_channel::ReceiveData(std::vector &data, uint32_t timeout) { } #endif -std::vector DLC_channel::SendCommandResponse(const char *cmd, size_t rxCount, uint32_t timeout) -{ - std::vector tokens; - std::vector data(cmd, cmd + strlen(cmd)); + + +std::vector DLC_channel::SendCommandResponse(const char *cmd, + size_t rxCount, uint32_t timeout) { + + std::vector tokens; + std::vector data(cmd, cmd + strlen(cmd)); // Remove \r and \n for logging purposes std::string cmdStr(cmd); @@ -97,63 +100,168 @@ std::vector DLC_channel::SendCommandResponse(const char *cmd, size_ LOG_INFO("[AT]: %s, timeout value %d", cmdStr.c_str(), timeout); - blockedTaskHandle = xTaskGetCurrentTaskHandle(); - SendData(data); + blockedTaskHandle = xTaskGetCurrentTaskHandle(); + SendData(data); - uint32_t currentTime = cpp_freertos::Ticks::GetTicks(); - uint32_t timeoutNeeded = timeout == UINT32_MAX ? UINT32_MAX : currentTime + timeout; - uint32_t timeElapsed = currentTime; - // wait_for_data: - while (1) - { + uint32_t currentTime = cpp_freertos::Ticks::GetTicks(); + uint32_t timeoutNeeded = + timeout == UINT32_MAX ? UINT32_MAX : currentTime + timeout; + uint32_t timeElapsed = currentTime; + + //wait_for_data: + while (1) { + if (timeElapsed >= timeoutNeeded) { + std::string cmdStr(cmd); LOG_MODEM_TIMEOUT("[AT]: %s, timeout %d - please check the value with Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf", cmdStr.c_str(), timeout); break; } - auto ret = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed); - timeElapsed = cpp_freertos::Ticks::GetTicks(); - if (ret) - { - std::vector strings; + auto ret = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed); + timeElapsed = cpp_freertos::Ticks::GetTicks(); + if (ret) { - cpp_freertos::LockGuard lock(mutex); - TS0710_Frame::frame_t frame; - std::vector v(responseBuffer.begin(), responseBuffer.end()); - responseBuffer.clear(); - frame.deserialize(v); - std::string str(frame.data.begin(), frame.data.end()); - // tokenize responseBuffer - // empty lines are also removed - auto ret = ATParser::Tokenizer(str, rxCount, "\r\n"); - tokens.insert(std::end(tokens), std::begin(ret), std::end(ret)); - if (tokens.size() < rxCount) - { - continue; - } - } - else - { - LOG_MODEM_TIMEOUT("[AT]: %s, timeout %d - please check the value with Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf", cmdStr.c_str(), timeout); - } - - break; - } + std::vector strings; - LOG_INFO("[AT]: %s - returning %i tokens in %d ms", cmdStr.c_str(), tokens.size(), timeElapsed - currentTime); + cpp_freertos::LockGuard lock(mutex); + TS0710_Frame::frame_t frame; + std::vector v(responseBuffer.begin(), + responseBuffer.end()); + + responseBuffer.clear(); + std::vector> mFrames; + std::vector rawBuffer; + + //get frames from buffer + for (int i = 0; i < v.size(); i++) { + rawBuffer.push_back(v[i]); + if (/*TS0710_Frame::isComplete(rawBuffer)*/(rawBuffer.size() > 1) + && (rawBuffer[0] == 0xF9) && (rawBuffer[rawBuffer.size() - 1] == 0xF9)) { + //LOGrawBufferEBUG("Pushing back FRAME"); + mFrames.push_back(rawBuffer); + rawBuffer.clear(); + } + } + + //deseriaise data from received frames + std::string deserialisedData; + for (std::vector vv : mFrames) { + frame.deserialize(vv); + std::string str(frame.data.begin(), frame.data.end()); + //append deserialised buffer + deserialisedData += str; + } + mFrames.clear(); + + //tokenize data + LOG_DEBUG("[Tokenizing] frame"); + auto ret = ATParser::Tokenizer(deserialisedData, rxCount, "\r\n"); + tokens.insert(std::end(tokens), std::begin(ret), std::end(ret)); + if (tokens.size() < rxCount) { + continue; + } + + blockedTaskHandle = nullptr; + + return tokens; + } else { + //timeout + blockedTaskHandle = nullptr; + + return tokens; + } + + ; #if DEBUG_MODEM_OUTPUT_RESPONSE - for (auto s : tokens) - { - LOG_DEBUG("[]%s", s.c_str()); - } -#endif + LOG_INFO("[AT]: %s - returning %i tokens in %d ms", cmdStr.c_str(), tokens.size(), timeElapsed - currentTime) - blockedTaskHandle = nullptr; - return tokens; + for (auto s : tokens) + { + LOG_DEBUG("[]%s", s.c_str()); + } +#endif + //to avoid endless loop + return tokens; + } + +} + +std::vector DLC_channel::SendCommandPrompt(const char *cmd, + size_t rxCount, uint32_t timeout) { + std::vector tokens; + std::vector data(cmd, cmd + strlen(cmd)); + + // Remove \r and \n for logging purposes + std::string cmdStr(cmd); + cmdStr.erase(std::remove(cmdStr.begin(), cmdStr.end(), '\r'), cmdStr.end()); + cmdStr.erase(std::remove(cmdStr.begin(), cmdStr.end(), '\n'), cmdStr.end()); + + LOG_INFO("[AT]: %s, timeout value %d", cmdStr.c_str(), timeout); + + blockedTaskHandle = xTaskGetCurrentTaskHandle(); + SendData(data); + + + uint32_t currentTime = cpp_freertos::Ticks::GetTicks(); + uint32_t timeoutNeeded = + timeout == UINT32_MAX ? UINT32_MAX : currentTime + timeout; + uint32_t timeElapsed = currentTime; + + //wait_for_data: + while (1) { + + if (timeElapsed >= timeoutNeeded) + { + std::string cmdStr(cmd); + LOG_MODEM_TIMEOUT("[AT]: %s, timeout %d - please check the value with Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf", cmdStr.c_str(), timeout); + break; + } + + auto ret = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed); + timeElapsed = cpp_freertos::Ticks::GetTicks(); + if (ret) { + + std::vector strings; + + cpp_freertos::LockGuard lock(mutex); + TS0710_Frame::frame_t frame; + std::vector v(responseBuffer.begin(), + responseBuffer.end()); + responseBuffer.clear(); + frame.deserialize(v); + std::string str(frame.data.begin(), frame.data.end()); + //tokenize responseBuffer + auto pos = str.find(">"); + if (pos != std::string::npos) { + tokens.push_back(str.substr(pos, strlen(">"))); + } + if (tokens.size() < rxCount) { + continue; + } + blockedTaskHandle = nullptr; + + return tokens; + } else { + //timeout + blockedTaskHandle = nullptr; + + return tokens; + } +#if DEBUG_MODEM_OUTPUT_RESPONSE + LOG_INFO("[AT]: %s - returning %i tokens in %d ms", cmdStr.c_str(), tokens.size(), timeElapsed - currentTime) + + for (auto s : tokens) + { + LOG_DEBUG("[]%s", s.c_str()); + } +#endif + //to avoid endless loop + return tokens; + } } int DLC_channel::ParseInputData(std::vector &data) { diff --git a/module-cellular/Modem/TS0710/DLC_channel.h b/module-cellular/Modem/TS0710/DLC_channel.h index 1520b8659..8520bc540 100644 --- a/module-cellular/Modem/TS0710/DLC_channel.h +++ b/module-cellular/Modem/TS0710/DLC_channel.h @@ -49,10 +49,17 @@ public: //ssize_t ReceiveData(std::vector &data, uint32_t timeout); void setCallback(Callback_t callback) { LOG_DEBUG("[%s] Setting up callback for channel", pv_name.c_str()); pv_callback = callback; } - std::vector SendCommandResponse(const char *cmd, size_t rxCount, uint32_t timeout = 300); + + + + std::vector SendCommandResponse(const char *cmd, + size_t rxCount, uint32_t timeout = 300); + std::vector SendCommandPrompt(const char *cmd, size_t rxCount, + uint32_t timeout = 300); + int ParseInputData(std::vector &data); void callback(std::vector &data) { pv_callback( data ); } }; -#endif //_DLC_CHANNEL_H \ No newline at end of file +#endif //_DLC_CHANNEL_H diff --git a/module-cellular/Modem/TS0710/TS0710.cpp b/module-cellular/Modem/TS0710/TS0710.cpp index 8a3d81fe2..8da019c2a 100644 --- a/module-cellular/Modem/TS0710/TS0710.cpp +++ b/module-cellular/Modem/TS0710/TS0710.cpp @@ -195,7 +195,6 @@ TS0710::ConfState TS0710::ConfProcedure() { // Route URCs to second (Notifications) MUX channel CheckATCommandResponse(parser->SendCommand("AT+QCFG=\"cmux/urcport\",1\r", 1)); - // Turn off RI pin for incoming calls CheckATCommandResponse(parser->SendCommand("AT+QCFG=\"urc/ri/ring\",\"off\"\r", 1)); // Turn off RI pin for incoming sms @@ -211,6 +210,14 @@ TS0710::ConfState TS0710::ConfProcedure() { // Turn on caller's number presentation // per Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf timeout should be set to 15s CheckATCommandResponse(parser->SendCommand("AT+CLIP=1\r", 1, 15000)); + // Set Message format to Text + CheckATCommandResponse(parser->SendCommand("AT+CMGF=1\r", 1)); + //Set ucs2 message format + CheckATCommandResponse(parser->SendCommand("AT+CSCS=\"UCS2\"\r", 1)); + //todo remove unneeded command +// CheckATCommandResponse(parser->SendCommand("AT+CSCS=\"GSM\"\r", 1)); + // Set SMS prefferd storage + CheckATCommandResponse(parser->SendCommand("AT+CPMS=“ME”,“ME”,“ME”", 2)); // Enable sleep mode LOG_DEBUG("TODO: determine while this retry loop is necessary"); diff --git a/module-cellular/Modem/TS0710/TS0710.h b/module-cellular/Modem/TS0710/TS0710.h index 9e7f693db..e7bcaea8a 100644 --- a/module-cellular/Modem/TS0710/TS0710.h +++ b/module-cellular/Modem/TS0710/TS0710.h @@ -257,6 +257,13 @@ private: return false; } + bool searchForPrompt(const std::vector &response) { + for (std::string s : response) { + if (s == ">") + return true; + } + return false; + } TS0710_START::START_SystemParameters_t startParams; sys::Service *pv_parent; @@ -348,6 +355,19 @@ public: } } + bool CheckATCommandPrompt(const std::vector &response) { + //if (response.size() == 1 && response[0] == "OK") { + if (searchForPrompt(response)) { + return true; + } else { + std::string resp; + for (std::string s : response) + resp.append(s); + LOG_ERROR("Invalid response: %s", resp.c_str()); + return false; + } + } + TS0710(PortSpeed_e portSpeed, sys::Service *parent); TS0710() = delete; @@ -359,4 +379,4 @@ TS0710() = delete; }; -#endif //_TS0710_H \ No newline at end of file +#endif //_TS0710_H diff --git a/module-cellular/Modem/TS0710/TS0710_Frame.h b/module-cellular/Modem/TS0710/TS0710_Frame.h index c48a3991b..cb3f32aea 100644 --- a/module-cellular/Modem/TS0710/TS0710_Frame.h +++ b/module-cellular/Modem/TS0710/TS0710_Frame.h @@ -118,6 +118,7 @@ public: if (serData[3] == 0xFF) //ugly hack for Quectel misimplementation of the standard Length = myLen - 6; //LOG_DEBUG("[Frame] %s Addr: 0x%02X, Ctrl: 0x%02X, Len: %i", TypeOfFrame_text[static_cast(Control)].c_str(), Address, Control, Length); + data.clear(); data.insert(data.begin(), serData.begin() + 4, serData.begin() + 4 + Length); //get data - amount of data determined by Length value //check FCS diff --git a/module-services/service-cellular/ServiceCellular.cpp b/module-services/service-cellular/ServiceCellular.cpp index aaea8aad3..9c3b7d974 100644 --- a/module-services/service-cellular/ServiceCellular.cpp +++ b/module-services/service-cellular/ServiceCellular.cpp @@ -26,6 +26,8 @@ #include "Common.hpp" #include "log/log.hpp" +#include "ucs2/UCS2.hpp" + const char *ServiceCellular::serviceName = "ServiceCellular"; constexpr int32_t ServiceCellular::signalStrengthToDB[]; @@ -67,7 +69,13 @@ ServiceCellular::ServiceCellular() break; case CellularNotificationMessage::Type::NewIncomingSMS: - //TODO:M.P fill message's fields + { + //find message number + std::string notification(data.begin(), data.end() ); + auto begin = notification.find(","); + auto end = notification.find("\r"); + msg->data = notification.substr(begin + 1, end); + } break; case CellularNotificationMessage::Type::SignalStrengthUpdate: @@ -162,15 +170,23 @@ sys::Message_t ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl, sys: case MessageType::CellularNotification: { CellularNotificationMessage *msg = reinterpret_cast(msgl); - if ((msg->type == CellularNotificationMessage::Type::CallAborted) || (msg->type == CellularNotificationMessage::Type::CallBusy)) - { - stopTimer(callStateTimer); - } - else if (msg->type == CellularNotificationMessage::Type::PowerUpProcedureComplete) - { - sys::Bus::SendUnicast(std::make_shared(MessageType::CellularStartConfProcedure), GetName(), this); - state = State ::ModemConfigurationInProgress; - } + + if ((msg->type == CellularNotificationMessage::Type::CallAborted) || + (msg->type == CellularNotificationMessage::Type::CallBusy)) + { + stopTimer(callStateTimer); + } + else if (msg->type == CellularNotificationMessage::Type::PowerUpProcedureComplete) + { + sys::Bus::SendUnicast(std::make_shared(MessageType::CellularStartConfProcedure), + GetName(), this); + state = State ::ModemConfigurationInProgress; + } + else if(msg->type == CellularNotificationMessage::Type::NewIncomingSMS) + { + LOG_INFO("New incoming sms received"); + receiveSMS(msg->data); + } else { // ignore rest of notifications @@ -230,6 +246,7 @@ sys::Message_t ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl, sys: if (cmux->StartMultiplexer() == TS0710::ConfState::Success) { + LOG_DEBUG("[ServiceCellular] Modem is fully operational"); DLC_channel *notificationsChannel = cmux->GetChannel("Notifications"); // open channel - notifications @@ -240,6 +257,7 @@ sys::Message_t ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl, sys: } state = State::Ready; + } else { @@ -331,26 +349,37 @@ sys::Message_t ServiceCellular::DataReceivedHandler(sys::DataMessage *msgl, sys: } break; - case MessageType::CellularDialNumber: { - CellularRequestMessage *msg = reinterpret_cast(msgl); - auto channel = cmux->GetChannel("Commands"); - if (channel) - { - auto ret = channel->SendCommandResponse(("ATD" + msg->data + ";\r").c_str(), 1, 5000); - if (cmux->CheckATCommandResponse(ret)) - { - responseMsg = std::make_shared(true); - // activate call state timer - ReloadTimer(callStateTimer); - // Propagate "Ringing" notification into system - sys::Bus::SendMulticast(std::make_shared(CellularNotificationMessage::Type::Ringing, msg->data), - sys::BusChannels::ServiceCellularNotifications, this); - break; - } + case MessageType::CellularDialNumber: { + CellularRequestMessage *msg = + reinterpret_cast(msgl); + auto channel = cmux->GetChannel("Commands"); + if (channel) { + auto ret = channel->SendCommandResponse( + ("ATD" + msg->data + ";\r").c_str(), 1, 5000); + if (cmux->CheckATCommandResponse(ret)) { + responseMsg = std::make_shared(true); + // activate call state timer + ReloadTimer(callStateTimer); + // Propagate "Ringing" notification into system + sys::Bus::SendMulticast( + std::make_shared( + CellularNotificationMessage::Type::Ringing, + msg->data), + sys::BusChannels::ServiceCellularNotifications, this); + break; + } + } + responseMsg = std::make_shared(false); + } + break; + case MessageType::CellularSendSMS: { + CellularSMSRequestMessage *msg = reinterpret_cast(msgl); + + auto ret = this->sendSMS(msg->number, msg->message); + + responseMsg = std::make_shared(ret); } - responseMsg = std::make_shared(false); - } - break; + break; default: break; @@ -410,3 +439,142 @@ CellularNotificationMessage::Type ServiceCellular::identifyNotification(std::vec return CellularNotificationMessage::Type::None; } + +bool ServiceCellular::sendSMS(UTF8& number, UTF8& text) { + uint32_t textLen = text.length(); + + const uint32_t singleMessageLen = 30; + + + //if text fit in single message send + if (textLen < singleMessageLen) { + + auto retCommand = cmux->GetChannel("Commands")->SendCommandPrompt( + ("AT+CMGS=\"" + UCS2(number).modemStr() + "\"\r").c_str(), 1, 1000); + + if ((retCommand.size() == 1) && (retCommand[0] == ">")) { + auto retText = cmux->GetChannel("Commands")->SendCommandResponse( + (UCS2(text).modemStr() + "\032").c_str(), 1); + return true; + } + } + //split text, and send concatenated message + else { + const uint32_t maxConcatenatedCount = 7; + uint32_t messagePartsCount = textLen / singleMessageLen; + if( (textLen % singleMessageLen) != 0 ) + { + messagePartsCount++; + } + + if(messagePartsCount > maxConcatenatedCount) + { + LOG_ERROR("Message to long"); + return false; + } + + for(uint32_t i = 0; i < messagePartsCount; i++) + { + + uint32_t partLength = singleMessageLen; + if (i * singleMessageLen + singleMessageLen > text.length()) { + partLength = text.length() - i * singleMessageLen; + } + std::string messagePart = text.substr(i * singleMessageLen, + partLength); + + + std::string command( + "AT+QCMGS=\"" + UCS2(number).modemStr() + "\",120," + + std::to_string(i + 1) + "," + + std::to_string(messagePartsCount) + "\r"); + + auto retCommand = cmux->GetChannel("Commands")->SendCommandPrompt( + command.c_str(), 1, 1000); + + if ((retCommand.size() == 1) && (retCommand[0] == ">")) { + //prompt sign received, send data ended by "Ctrl+Z" + auto sended = + cmux->GetChannel("Commands")->SendCommandResponse( + (UCS2(messagePart).modemStr() + + "\032").c_str(), 2, 2000); + } + } + return true; + } + return false; +} + +bool ServiceCellular::receiveSMS(std::string messageNumber) { + + std::string command("AT+QCMGR=" + messageNumber + "\r"); + + auto ret = cmux->GetChannel("Commands")->SendCommandResponse( + command.c_str(), 2, 2000); + + bool messageParsed = false; + + std::string messageRawBody; + UTF8 receivedNumber; + if (ret.size() != 0) { + for (uint32_t i = 0; i < ret.size(); i++) { + if (ret[i].find("QCMGR") != std::string::npos) { + + std::istringstream ss(ret[i]); + std::string token; + std::vector tokens; + while (std::getline(ss, token, ',')) { + tokens.push_back(token); + } + tokens[1].erase( + std::remove(tokens[1].begin(), tokens[1].end(), '\"'), + tokens[1].end()); + receivedNumber = UCS2(tokens[1]).toUTF8(); + + //if its single message process + if (tokens.size() == 5) { + //todo add message to database + + messageRawBody = ret[i+1]; + messageParsed = true; + } + //if its concatenated message wait for last message + else if (tokens.size() == 8) { + + uint32_t last = std::stoi(tokens[7]); + uint32_t current = std::stoi(tokens[6]); + + if (current == last) { + messageParts.push_back(ret[i + 1]); + + for (uint32_t j = 0; j < messageParts.size(); j++) { + messageRawBody += messageParts[j]; + } + messageParts.clear(); + messageParsed = true; + } else { + messageParts.push_back(ret[i + 1]); + } + } + } + } + if (messageParsed) { + messageParsed = false; + UTF8 decodedMessage = UCS2(messageRawBody).toUTF8(); + + // todo temporary send multicast + auto msg = std::make_shared( + MessageType::CellularSMSMulticast); + + msg->number = receivedNumber; + msg->message = decodedMessage; + + sys::Bus::SendMulticast(msg, + sys::BusChannels::ServiceCellularSMSNotification, this); + } + } + //delete message from modem memory + cmux->GetChannel("Commands")->SendCommandResponse( + ("AT+CMGD=" + messageNumber).c_str(), 1, 150); + return true; +} diff --git a/module-services/service-cellular/ServiceCellular.hpp b/module-services/service-cellular/ServiceCellular.hpp index 872d6acd2..fb5fa055c 100644 --- a/module-services/service-cellular/ServiceCellular.hpp +++ b/module-services/service-cellular/ServiceCellular.hpp @@ -16,7 +16,7 @@ #include "Modem/TS0710/DLC_channel.h" #include "Modem/TS0710/TS0710.h" #include "messages/CellularMessage.hpp" - +#include "utf8/UTF8.hpp" // class MuxDaemon; @@ -55,6 +55,9 @@ public: static int32_t getSignalStrengthDB(int32_t strength) { return signalStrengthToDB[strength]; } static int32_t getSignalStrengthDBRange() { return (sizeof(signalStrengthToDB) / sizeof(signalStrengthToDB[0])); } + bool sendSMS(UTF8& number, UTF8& text); + bool receiveSMS(std::string messageNumber); + private: //std::unique_ptr muxdaemon; @@ -112,6 +115,7 @@ private: return false; } + std::vector messageParts; }; diff --git a/module-services/service-cellular/api/CellularServiceAPI.cpp b/module-services/service-cellular/api/CellularServiceAPI.cpp index 51d49db0a..89736dc1f 100644 --- a/module-services/service-cellular/api/CellularServiceAPI.cpp +++ b/module-services/service-cellular/api/CellularServiceAPI.cpp @@ -12,7 +12,7 @@ #include "CellularServiceAPI.hpp" #include "Service/Bus.hpp" #include "../ServiceCellular.hpp" - +#include "utf8/UTF8.hpp" bool CellularServiceAPI::DialNumber(sys::Service* serv,const std::string& number) { std::shared_ptr msg = std::make_shared(MessageType::CellularDialNumber); @@ -56,4 +56,20 @@ bool CellularServiceAPI::HangupCall(sys::Service* serv){ LOG_ERROR("Failed"); return false; } -} \ No newline at end of file +} + +bool CellularServiceAPI::SendSMS(sys::Service* serv, UTF8 number, UTF8 message) +{ + std::shared_ptr msg = std::make_shared(MessageType::CellularSendSMS); + + msg->number = number; + msg->message = message; + + auto ret = sys::Bus::SendUnicast(msg, ServiceCellular::serviceName, serv, 5000); + CellularResponseMessage* response = reinterpret_cast(ret.second.get()); + if((ret.first == sys::ReturnCodes::Success) && (response->retCode == true)) + { + return true; + } + return false; +} diff --git a/module-services/service-cellular/api/CellularServiceAPI.hpp b/module-services/service-cellular/api/CellularServiceAPI.hpp index b71df0abd..c56263aff 100644 --- a/module-services/service-cellular/api/CellularServiceAPI.hpp +++ b/module-services/service-cellular/api/CellularServiceAPI.hpp @@ -13,6 +13,7 @@ #define PUREPHONE_CELLULARSERVICEAPI_HPP #include "../messages/CellularMessage.hpp" +#include "utf8/UTF8.hpp" class Service; @@ -21,7 +22,7 @@ public: static bool DialNumber(sys::Service* serv,const std::string& number); static bool AnswerIncomingCall(sys::Service* serv); static bool HangupCall(sys::Service* serv); - + static bool SendSMS(sys::Service* Serv, UTF8 number, UTF8 message); }; diff --git a/module-services/service-cellular/messages/CellularMessage.hpp b/module-services/service-cellular/messages/CellularMessage.hpp index 9459e2745..fa3ad094b 100644 --- a/module-services/service-cellular/messages/CellularMessage.hpp +++ b/module-services/service-cellular/messages/CellularMessage.hpp @@ -16,7 +16,7 @@ #include #include "Service/Message.hpp" #include "MessageType.hpp" - +#include "utf8/UTF8.hpp" class CellularMessage : public sys::DataMessage { public: @@ -74,6 +74,17 @@ public: }; +class CellularSMSRequestMessage : public CellularMessage{ +public: + + CellularSMSRequestMessage(MessageType messageType):CellularMessage(messageType){} + ~CellularSMSRequestMessage() {} + + UTF8 number; + UTF8 message; + +}; + class CellularResponseMessage: public sys::ResponseMessage { public: CellularResponseMessage(uint32_t retCode) : sys::ResponseMessage(),retCode(retCode) {}; diff --git a/module-utils/CMakeLists.txt b/module-utils/CMakeLists.txt index 6a9a575aa..c99929e5a 100644 --- a/module-utils/CMakeLists.txt +++ b/module-utils/CMakeLists.txt @@ -52,6 +52,7 @@ set (SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/json/json11.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utf8/UTF8.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time/time_conversion.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ucs2/UCS2.cpp ) add_library(${PROJECT_NAME} STATIC ${SOURCES} ${BOARD_SOURCES}) diff --git a/module-utils/ucs2/UCS2.cpp b/module-utils/ucs2/UCS2.cpp new file mode 100644 index 000000000..d05a61967 --- /dev/null +++ b/module-utils/ucs2/UCS2.cpp @@ -0,0 +1,165 @@ +/* + * UCS2.cpp + * + * Created on: 24 wrz 2019 + * Author: kuba + */ + +#include "UCS2.hpp" +#include +#include +#include + +#include +#include "log/log.hpp" +#include + +const uint32_t UCS2::ucs2bufferExt = 16; + +UCS2::UCS2(void) { + sizeUsed = 0; + sizeAllocated = ucs2bufferExt; + buffer = new uint16_t[ucs2bufferExt]; + length = 0; +} + +UCS2::~UCS2() { + if (buffer != nullptr) + delete[] buffer; +} + +UCS2::UCS2(const UTF8 &string) { + this->clear(); + + for (uint32_t i = 0; i < string.length(); i++) { + uint32_t decodedSize; + uint32_t utfChar = string[i]; + +// LOG_INFO("decoded uft %x", ucs2char); + if (0xffff0000 & utfChar) { + sizeUsed = 0; + length = 0; + LOG_ERROR( + "UCS2::UCS2(const UTF8& string) failed, provided char is out of range"); + break; + } + + append(static_cast(utfChar)); + } +} + +UCS2::UCS2(const std::string &string) { + sizeUsed = 0; + sizeAllocated = ucs2bufferExt; + buffer = new uint16_t[ucs2bufferExt]; + length = 0; + + for (unsigned int i = 0; i < string.size() / 4; i++) { + uint16_t ucs2char; + try { + ucs2char = std::stoi(string.substr(i * 4, 4), 0, 16); + } catch (std::invalid_argument &e) { + LOG_ERROR("UCS2::UCS2(const std::string& string) failed."); + this->clear(); + } catch (std::out_of_range &e) { + this->clear(); + LOG_ERROR("UCS2::UCS2(const std::string& string) failed."); + } + if (0xffff0000 & ucs2char) { + this->clear(); + LOG_ERROR( + "UCS2::UCS2(const UTF8& string) failed, provided char is out of range"); + //break; + } + append(static_cast(ucs2char)); + } + + //terminate ucs2 string by 0 + append(0); + +} +UCS2::UCS2(UCS2 &ucs) { + sizeUsed = 0; + sizeAllocated = ucs2bufferExt; + buffer = new uint16_t[ucs2bufferExt]; + length = 0; + + this->sizeUsed = ucs.getSizeUsed(); + this->sizeAllocated = ucs.getSizeAlocated(); + this->length = ucs.getLength(); + this->buffer = new uint16_t[sizeAllocated]; + memset(buffer, 0, sizeAllocated); + memcpy(buffer, ucs.getData(), sizeUsed); +} + +UTF8 UCS2::toUTF8(void) { + if (this->length == 0) + return UTF8(); + + //create buffer for worst case scenario which is that every char will take 3 bytes in utf8 string + // + 1 for null terminator + + uint8_t *buffer = new uint8_t[3 * this->length + 1]; + memset(buffer, 0, 3 * this->length + 1); + + uint32_t offset = 0; + for (uint32_t i = 0; i < this->length; i++) { + uint32_t c = this->buffer[i]; + + //check if character must occupy 3 bytes + if (c > 0x07ff) { + buffer[offset++] = (0x00E0 | ((c & 0xF000) >> 12)); + buffer[offset++] = (0x0080 | ((c & 0x0FC0) >> 6)); + buffer[offset++] = (0x0080 | (c & 0x003F)); + } + //check if character must occupy 2 bytes + else if (c > 0x07f) { + buffer[offset++] = (0x00C0 | ((c & 0x07C0) >> 6)); + buffer[offset++] = (0x0080 | (c & 0x003F)); + } else { + buffer[offset++] = c; + } + } + UTF8 retString(reinterpret_cast(buffer)); + delete[] buffer; + return retString; + +} + +void UCS2::append(const uint16_t &ucs2char) { + // check if buffer needs to be expanded + if (sizeUsed == sizeAllocated) { + uint16_t *newBuffer = new uint16_t[sizeAllocated + ucs2bufferExt]; + memset(newBuffer, 0, sizeAllocated + ucs2bufferExt); + memcpy(newBuffer, buffer, sizeAllocated); + delete[] buffer; + buffer = newBuffer; + sizeAllocated = sizeAllocated + ucs2bufferExt; + } + //write character to the end of buffer, increment size and add 2 to used bytes ( usc2 character is two byte ) + buffer[length] = ucs2char; + length++; + sizeUsed += 2; +} + +std::string UCS2::modemStr(void) { + std::stringstream ss; + + for (uint32_t i = 0; i < length; i++) { + ss << std::setw(4) << std::setfill('0') << std::hex << std::uppercase; + ss << buffer[i]; + } + + return ss.str(); +} + +void UCS2::clear(void) { + sizeUsed = 0; + sizeAllocated = ucs2bufferExt; + if(buffer != nullptr) + { + delete[] buffer; + } + buffer = new uint16_t[ucs2bufferExt]; + length = 0; +} diff --git a/module-utils/ucs2/UCS2.hpp b/module-utils/ucs2/UCS2.hpp new file mode 100644 index 000000000..f65c6e0d9 --- /dev/null +++ b/module-utils/ucs2/UCS2.hpp @@ -0,0 +1,67 @@ +/* + * UCS2.hpp + * + * Created on: 24 wrz 2019 + * Author: kuba + */ + +#ifndef MODULE_UTILS_UCS2_UCS2_HPP_ +#define MODULE_UTILS_UCS2_UCS2_HPP_ + +#include +#include "utf8/UTF8.hpp" + + +class UCS2{ +private: + //pointer to memory where ucs2 characters are stored. + uint16_t* buffer; + //size in bytes of memory that was allcated to the buffer + uint32_t sizeAllocated; + //size in bytes of memory used in buffer + uint32_t sizeUsed; + //number of characters in the string. its equal to size of allocated memory plus null terminator + uint32_t length; + // + static const uint32_t ucs2bufferExt; + void clear(void); +public: + //default constructor + UCS2(void); + // +// UCS2( uint16_t* text); + /* + * @brief Initializes new ucs2 string from utf8 string. It's used to convert text from + * utf8 to ucs2. + * @param string utf8 string to convert + */ + UCS2(const UTF8& string); + /* + * @brief Initializes new ucs2 string from std::string. It's used to convert text from + * modem message format to ucs2. + * @param string std::string to convert + */ + UCS2(const std::string& string); + UCS2(UCS2& ucs); + ~UCS2(void); + const char* c_str(void){ return reinterpret_cast(buffer);} + /* + * @brief It's converting ucs2 to utf string. + * @return utf8 string + */ + UTF8 toUTF8(void); + void append(const uint16_t& ucs2char); + /* + * @brief It's converting text coded in ucs2 to string. Used to send data to modem. + * @return coded string + */ + std::string modemStr(void); + uint32_t getLength(void) { return length; }; + uint32_t getSizeUsed(void) { return sizeUsed; }; + uint32_t getSizeAlocated(void) { return sizeAllocated; }; + uint16_t* getData(void) {return buffer; }; +}; + + + +#endif /* MODULE_UTILS_UCS2_UCS2_HPP_ */ diff --git a/source/BusChannelsCustom.hpp b/source/BusChannelsCustom.hpp index 6a9c78cfa..8802201b5 100644 --- a/source/BusChannelsCustom.hpp +++ b/source/BusChannelsCustom.hpp @@ -9,9 +9,12 @@ // Custom Bus channels ServiceCellularNotifications, +//todo remove sms notification +ServiceCellularSMSNotification, Test2CustomBusChannel, ServiceDatabaseAlarmNotifications, ServiceAudioNotifications + #endif //MODULE_CORE_BUSCHANNELSCUSTOM_HPP diff --git a/source/MessageType.hpp b/source/MessageType.hpp index 8339ec73e..8031c89b3 100644 --- a/source/MessageType.hpp +++ b/source/MessageType.hpp @@ -72,12 +72,15 @@ enum class MessageType CellularStartPowerUpProcedure, CellularStartConfProcedure, CellularStartAudioConfProcedure, + CellularSendSMS, // request ot send new sms + CellularSMSMulticast, + + DBNotesAdd, // Add new note's record + DBNotesRemove, // Remove selected note's record + DBNotesUpdate, // Update selected note's record + DBNotesGetLimitOffset, // Get notes records by limit,offset + DBNotesGetCount, // Get notes reocrds count - DBNotesAdd, // Add new note's record - DBNotesRemove, // Remove selected note's record - DBNotesUpdate, // Update selected note's record - DBNotesGetLimitOffset, // Get notes records by limit,offset - DBNotesGetCount, // Get notes reocrds count DBCalllogAdd, // Add new note's record DBCalllogRemove, // Remove selected note's record diff --git a/source/main.cpp b/source/main.cpp index 3f8fe0964..3a4c360c4 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -130,6 +130,7 @@ public: }; + int main() { bsp::BoardInit();