mirror of https://github.com/ARMmbed/mbed-os.git
				
				
				
			ATCmdParser: unittests implementation
							parent
							
								
									15eb2a6e4f
								
							
						
					
					
						commit
						1d3a1b7c6c
					
				| 
						 | 
				
			
			@ -0,0 +1,409 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) 2019, Arm Limited and affiliates.
 | 
			
		||||
 * SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "gtest/gtest.h"
 | 
			
		||||
#include "ATCmdParser.h"
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdarg.h>
 | 
			
		||||
#include "FileHandle_stub.h"
 | 
			
		||||
#include "mbed_poll_stub.h"
 | 
			
		||||
#include "Timer_stub.h"
 | 
			
		||||
 | 
			
		||||
using namespace mbed;
 | 
			
		||||
 | 
			
		||||
static bool expected_oob_callback = false;
 | 
			
		||||
 | 
			
		||||
void urc_callback()
 | 
			
		||||
{
 | 
			
		||||
    EXPECT_TRUE(expected_oob_callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// AStyle ignored as the definition is not clear due to preprocessor usage
 | 
			
		||||
// *INDENT-OFF*
 | 
			
		||||
class test_ATCmdParser : public testing::Test {
 | 
			
		||||
protected:
 | 
			
		||||
 | 
			
		||||
    void SetUp()
 | 
			
		||||
    {
 | 
			
		||||
        filehandle_stub_short_value_counter = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void TearDown()
 | 
			
		||||
    {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
// *INDENT-ON*
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_create)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser *at = new ATCmdParser(&fh1, ",");
 | 
			
		||||
    at->set_delimiter("\r");
 | 
			
		||||
    at->set_timeout(5000);
 | 
			
		||||
    at->debug_on(1);
 | 
			
		||||
 | 
			
		||||
    delete at;
 | 
			
		||||
 | 
			
		||||
    at = new ATCmdParser(&fh1, "\r");
 | 
			
		||||
 | 
			
		||||
    EXPECT_TRUE(at != NULL);
 | 
			
		||||
    delete at;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_set_timeout)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    at.set_timeout(8);
 | 
			
		||||
    at.set_timeout(80);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_process_oob)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1);
 | 
			
		||||
    at.set_timeout(10);
 | 
			
		||||
 | 
			
		||||
    at.process_oob();
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_short_value_counter = 1;
 | 
			
		||||
    fh1.short_value = POLLIN;
 | 
			
		||||
    at.oob("s", &urc_callback);
 | 
			
		||||
    at.process_oob();
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_short_value_counter = 2;
 | 
			
		||||
    at.process_oob();
 | 
			
		||||
 | 
			
		||||
    char buf[5];
 | 
			
		||||
    char table[] = "ssssssssssssssssssssssssssssssss\0";
 | 
			
		||||
    filehandle_stub_table = table;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    at.read(buf, 5);
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_short_value_counter = 2;
 | 
			
		||||
    expected_oob_callback = true;
 | 
			
		||||
    at.process_oob();
 | 
			
		||||
    expected_oob_callback = false;
 | 
			
		||||
 | 
			
		||||
    timer_stub_value = 0;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    at.read(buf, 5);
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_short_value_counter = 1;
 | 
			
		||||
    expected_oob_callback = true;
 | 
			
		||||
    at.process_oob();
 | 
			
		||||
    expected_oob_callback = false;
 | 
			
		||||
 | 
			
		||||
    char table2[5];
 | 
			
		||||
    table2[0] = '\r';
 | 
			
		||||
    table2[1] = '\r';
 | 
			
		||||
    table2[2] = '\n';
 | 
			
		||||
    table2[3] = '\n';
 | 
			
		||||
    table2[4] = 0;
 | 
			
		||||
    filehandle_stub_table = table2;
 | 
			
		||||
 | 
			
		||||
    timer_stub_value = 0;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    at.read(buf, 1);
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_short_value_counter = 1;
 | 
			
		||||
    at.process_oob();
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_table = table;
 | 
			
		||||
 | 
			
		||||
    filehandle_stub_short_value_counter = 0;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    filehandle_stub_table = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_flush)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    filehandle_stub_short_value_counter = 1;
 | 
			
		||||
    fh1.short_value = POLLIN;
 | 
			
		||||
    at.flush();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_write)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    fh1.size_value = -1;
 | 
			
		||||
    EXPECT_TRUE(-1 == at.write("help", 4));
 | 
			
		||||
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLOUT;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    fh1.size_value = -1;
 | 
			
		||||
    EXPECT_TRUE(-1 == at.write("help", 4));
 | 
			
		||||
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLOUT;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    fh1.size_value = 7;
 | 
			
		||||
    EXPECT_EQ(4, at.write("help", 4));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_set_delimiter)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    at.set_delimiter("+");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_read)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
    filehandle_stub_table = NULL;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    char buf[6];
 | 
			
		||||
    memset(buf, 0, 6);
 | 
			
		||||
 | 
			
		||||
    // TEST EMPTY BUFFER
 | 
			
		||||
    // Shouldn't read any byte since buffer is empty
 | 
			
		||||
    EXPECT_TRUE(-1 == at.read(buf, 1));
 | 
			
		||||
    // Return error due to error set to at handler by the above call on empty buffer
 | 
			
		||||
    EXPECT_TRUE(-1 == at.read(buf, 1));
 | 
			
		||||
 | 
			
		||||
    // TEST DATA IN BUFFER
 | 
			
		||||
    char table1[] = "1234512345678OK\r\n\0";
 | 
			
		||||
    filehandle_stub_table = table1;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
 | 
			
		||||
    // Read 5 bytes
 | 
			
		||||
    EXPECT_EQ(5, at.read(buf, 5));
 | 
			
		||||
    EXPECT_TRUE(!memcmp(buf, table1, 5));
 | 
			
		||||
    // get_char triggered above should have filled in the whole reading buffer
 | 
			
		||||
    EXPECT_EQ(filehandle_stub_table_pos, (strlen(buf)));
 | 
			
		||||
    // Read another 8 bytes
 | 
			
		||||
    EXPECT_TRUE(8 == at.read(buf, 8) && !memcmp(buf, table1 + 5, 8));
 | 
			
		||||
    // Reading more than the 4 bytes left -> ERROR
 | 
			
		||||
    EXPECT_EQ(-1, at.read(buf, 5));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_debug_on)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    at.debug_on(true);
 | 
			
		||||
    at.debug_on(false);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_abort)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1, ",");
 | 
			
		||||
    at.abort();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_printf)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1);
 | 
			
		||||
    at.flush();
 | 
			
		||||
    at.debug_on(true);
 | 
			
		||||
 | 
			
		||||
    const char * format = "TEST %d %s %x %c TEST \r\r\n\n";
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLOUT;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    fh1.size_value = 64;
 | 
			
		||||
 | 
			
		||||
    EXPECT_EQ(22, at.printf(format, 5, "t", 0x5, 't'));
 | 
			
		||||
 | 
			
		||||
    fh1.size_value = -1;
 | 
			
		||||
    EXPECT_EQ(-1, at.printf(format, 5, "t", 0x5, 't'));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_send)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
 | 
			
		||||
    ATCmdParser at(&fh1);
 | 
			
		||||
    at.flush();
 | 
			
		||||
    at.debug_on(true);
 | 
			
		||||
 | 
			
		||||
    const char * format = "TEST %d %s %x %c TEST \r\r\n\n";
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLOUT;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    fh1.size_value = 64;
 | 
			
		||||
 | 
			
		||||
    //VALID printf
 | 
			
		||||
    EXPECT_TRUE(at.send(format, 5, "t", 0x5, 't'));
 | 
			
		||||
    EXPECT_TRUE(at.send(""));
 | 
			
		||||
 | 
			
		||||
    fh1.size_value = -1;
 | 
			
		||||
    EXPECT_FALSE(at.send(format, 5, "t", 0x5, 't'));
 | 
			
		||||
    EXPECT_FALSE(at.send(""));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_recv)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
    ATCmdParser at(&fh1, "\r", 8);
 | 
			
		||||
 | 
			
		||||
    //parse valid char
 | 
			
		||||
    char table1[] = "s";
 | 
			
		||||
    at.flush();
 | 
			
		||||
    filehandle_stub_table = table1;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    char c;
 | 
			
		||||
    EXPECT_TRUE(at.recv("%c", &c));
 | 
			
		||||
    EXPECT_EQ(c, 's');
 | 
			
		||||
 | 
			
		||||
    //too large response
 | 
			
		||||
    char table2[] = "1234567890\n";
 | 
			
		||||
    at.flush();
 | 
			
		||||
    filehandle_stub_table = table2;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_FALSE(at.recv("123456789%c\n", &c));
 | 
			
		||||
 | 
			
		||||
    //back to normal buffer
 | 
			
		||||
    ATCmdParser at1(&fh1);
 | 
			
		||||
    at1.flush();
 | 
			
		||||
    filehandle_stub_table = table2;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_TRUE(at1.recv("123456789%c\n", &c));
 | 
			
		||||
 | 
			
		||||
    char table3[] = "s\r\n\0";
 | 
			
		||||
    at1.flush();
 | 
			
		||||
    filehandle_stub_table = table3;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_TRUE(at.recv("%c", &c));
 | 
			
		||||
    EXPECT_EQ(c, 's');
 | 
			
		||||
 | 
			
		||||
    char table4[] = "s 1 E test\r\n";
 | 
			
		||||
    char text[5];
 | 
			
		||||
    int hexval;
 | 
			
		||||
    int intval;
 | 
			
		||||
    at1.flush();
 | 
			
		||||
    filehandle_stub_table = table4;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_TRUE(at1.recv("%c %d %x %s", &c, &intval, &hexval, &text));
 | 
			
		||||
    EXPECT_EQ(c, 's');
 | 
			
		||||
    EXPECT_EQ(intval, 1);
 | 
			
		||||
    EXPECT_EQ(hexval, 0xE);
 | 
			
		||||
    EXPECT_EQ(!memcmp(text, "test", 4), 0);
 | 
			
		||||
 | 
			
		||||
    char table5[] = "s 1 E test\r\nt 2 F tes2\r\n";
 | 
			
		||||
    char c1;
 | 
			
		||||
    char text1[5];
 | 
			
		||||
    int hexval1;
 | 
			
		||||
    int intval1;
 | 
			
		||||
    at1.flush();
 | 
			
		||||
    filehandle_stub_table = table5;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
 | 
			
		||||
    expected_oob_callback = true;
 | 
			
		||||
    at1.oob("s", &urc_callback);
 | 
			
		||||
    EXPECT_TRUE(at1.recv("%c %d %x %s\r\n%c %d %x %s\r\n", &c, &intval, &hexval, &text));
 | 
			
		||||
    expected_oob_callback = false;
 | 
			
		||||
    EXPECT_EQ(c, 't');
 | 
			
		||||
    EXPECT_EQ(intval, 2);
 | 
			
		||||
    EXPECT_EQ(hexval, 0xF);
 | 
			
		||||
    EXPECT_EQ(memcmp(text, "tes2", 4), 0);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST_F(test_ATCmdParser, test_ATCmdParser_scanf)
 | 
			
		||||
{
 | 
			
		||||
    FileHandle_stub fh1;
 | 
			
		||||
    ATCmdParser at(&fh1, "\r");
 | 
			
		||||
 | 
			
		||||
    //parse valid char
 | 
			
		||||
    char table1[] = "s";
 | 
			
		||||
    at.flush();
 | 
			
		||||
    filehandle_stub_table = table1;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    char c;
 | 
			
		||||
    EXPECT_TRUE(at.scanf("%c", &c));
 | 
			
		||||
    EXPECT_EQ(c, 's');
 | 
			
		||||
 | 
			
		||||
    //back to normal buffer
 | 
			
		||||
    char table2[] = "1234567890\n";
 | 
			
		||||
    at.flush();
 | 
			
		||||
    filehandle_stub_table = table2;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_TRUE(at.scanf("123456789%c\n", &c));
 | 
			
		||||
 | 
			
		||||
    char table3[] = "s\r\n\0";
 | 
			
		||||
    at.flush();
 | 
			
		||||
    filehandle_stub_table = table3;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_TRUE(at.scanf("%c", &c));
 | 
			
		||||
    EXPECT_EQ(c, 's');
 | 
			
		||||
 | 
			
		||||
    char table4[] = "s 1 E test\r\n";
 | 
			
		||||
    char text[5];
 | 
			
		||||
    memset(text, 0, 5);
 | 
			
		||||
    int hexval;
 | 
			
		||||
    int intval;
 | 
			
		||||
    at.flush();
 | 
			
		||||
    filehandle_stub_table = table4;
 | 
			
		||||
    filehandle_stub_table_pos = 0;
 | 
			
		||||
    mbed_poll_stub::revents_value = POLLIN;
 | 
			
		||||
    mbed_poll_stub::int_value = 1;
 | 
			
		||||
    EXPECT_TRUE(at.scanf("%c %d %x %s", &c, &intval, &hexval, &text));
 | 
			
		||||
    EXPECT_EQ(c, 's');
 | 
			
		||||
    EXPECT_EQ(intval, 1);
 | 
			
		||||
    EXPECT_EQ(hexval, 0xE);
 | 
			
		||||
    EXPECT_EQ(!memcmp(text, "test", 4), 0);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,16 @@
 | 
			
		|||
####################
 | 
			
		||||
# UNIT TESTS
 | 
			
		||||
####################
 | 
			
		||||
 | 
			
		||||
# Source files
 | 
			
		||||
set(unittest-sources
 | 
			
		||||
  ../platform/source/ATCmdParser.cpp
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
# Test files
 | 
			
		||||
set(unittest-test-sources
 | 
			
		||||
  platform/ATCmdParser/test_ATCmdParser.cpp
 | 
			
		||||
  stubs/FileHandle_stub.cpp
 | 
			
		||||
  stubs/mbed_assert_stub.c
 | 
			
		||||
  stubs/mbed_poll_stub.cpp
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -1,420 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) , Arm Limited and affiliates.
 | 
			
		||||
 * SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ATCmdParser.h"
 | 
			
		||||
#include "mbed_poll.h"
 | 
			
		||||
#include "mbed_debug.h"
 | 
			
		||||
 | 
			
		||||
#ifdef LF
 | 
			
		||||
#undef LF
 | 
			
		||||
#define LF  10
 | 
			
		||||
#else
 | 
			
		||||
#define LF  10
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#ifdef CR
 | 
			
		||||
#undef CR
 | 
			
		||||
#define CR  13
 | 
			
		||||
#else
 | 
			
		||||
#define CR  13
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
// getc/putc handling with timeouts
 | 
			
		||||
int ATCmdParser::putc(char c)
 | 
			
		||||
{
 | 
			
		||||
    pollfh fhs;
 | 
			
		||||
    fhs.fh = _fh;
 | 
			
		||||
    fhs.events = POLLOUT;
 | 
			
		||||
 | 
			
		||||
    int count = poll(&fhs, 1, _timeout);
 | 
			
		||||
    if (count > 0 && (fhs.revents & POLLOUT)) {
 | 
			
		||||
        return _fh->write(&c, 1) == 1 ? 0 : -1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ATCmdParser::getc()
 | 
			
		||||
{
 | 
			
		||||
    pollfh fhs;
 | 
			
		||||
    fhs.fh = _fh;
 | 
			
		||||
    fhs.events = POLLIN;
 | 
			
		||||
 | 
			
		||||
    int count = poll(&fhs, 1, _timeout);
 | 
			
		||||
    if (count > 0 && (fhs.revents & POLLIN)) {
 | 
			
		||||
        unsigned char ch;
 | 
			
		||||
        return _fh->read(&ch, 1) == 1 ? ch : -1;
 | 
			
		||||
    } else {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ATCmdParser::flush()
 | 
			
		||||
{
 | 
			
		||||
    while (_fh->readable()) {
 | 
			
		||||
        unsigned char ch;
 | 
			
		||||
        _fh->read(&ch, 1);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// read/write handling with timeouts
 | 
			
		||||
int ATCmdParser::write(const char *data, int size)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i < size; i++) {
 | 
			
		||||
        if (putc(data[i]) < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ATCmdParser::read(char *data, int size)
 | 
			
		||||
{
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; i < size; i++) {
 | 
			
		||||
        int c = getc();
 | 
			
		||||
        if (c < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        data[i] = c;
 | 
			
		||||
    }
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// printf/scanf handling
 | 
			
		||||
int ATCmdParser::vprintf(const char *format, va_list args)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
    if (vsprintf(_buffer, format, args) < 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    for (; _buffer[i]; i++) {
 | 
			
		||||
        if (putc(_buffer[i]) < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ATCmdParser::vscanf(const char *format, va_list args)
 | 
			
		||||
{
 | 
			
		||||
    // Since format is const, we need to copy it into our buffer to
 | 
			
		||||
    // add the line's null terminator and clobber value-matches with asterisks.
 | 
			
		||||
    //
 | 
			
		||||
    // We just use the beginning of the buffer to avoid unnecessary allocations.
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    int offset = 0;
 | 
			
		||||
 | 
			
		||||
    while (format[i]) {
 | 
			
		||||
        if (format[i] == '%' && format[i + 1] != '%' && format[i + 1] != '*') {
 | 
			
		||||
            _buffer[offset++] = '%';
 | 
			
		||||
            _buffer[offset++] = '*';
 | 
			
		||||
            i++;
 | 
			
		||||
        } else {
 | 
			
		||||
            _buffer[offset++] = format[i++];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Scanf has very poor support for catching errors
 | 
			
		||||
    // fortunately, we can abuse the %n specifier to determine
 | 
			
		||||
    // if the entire string was matched.
 | 
			
		||||
    _buffer[offset++] = '%';
 | 
			
		||||
    _buffer[offset++] = 'n';
 | 
			
		||||
    _buffer[offset++] = 0;
 | 
			
		||||
 | 
			
		||||
    // To workaround scanf's lack of error reporting, we actually
 | 
			
		||||
    // make two passes. One checks the validity with the modified
 | 
			
		||||
    // format string that only stores the matched characters (%n).
 | 
			
		||||
    // The other reads in the actual matched values.
 | 
			
		||||
    //
 | 
			
		||||
    // We keep trying the match until we succeed or some other error
 | 
			
		||||
    // derails us.
 | 
			
		||||
    int j = 0;
 | 
			
		||||
 | 
			
		||||
    while (true) {
 | 
			
		||||
        // Ran out of space
 | 
			
		||||
        if (j + 1 >= _buffer_size - offset) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        // Recieve next character
 | 
			
		||||
        int c = getc();
 | 
			
		||||
        if (c < 0) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        _buffer[offset + j++] = c;
 | 
			
		||||
        _buffer[offset + j] = 0;
 | 
			
		||||
 | 
			
		||||
        // Check for match
 | 
			
		||||
        int count = -1;
 | 
			
		||||
        sscanf(_buffer + offset, _buffer, &count);
 | 
			
		||||
 | 
			
		||||
        // We only succeed if all characters in the response are matched
 | 
			
		||||
        if (count == j) {
 | 
			
		||||
            // Store the found results
 | 
			
		||||
            vsscanf(_buffer + offset, format, args);
 | 
			
		||||
            return j;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// Command parsing with line handling
 | 
			
		||||
bool ATCmdParser::vsend(const char *command, va_list args)
 | 
			
		||||
{
 | 
			
		||||
    // Create and send command
 | 
			
		||||
    if (vsprintf(_buffer, command, args) < 0) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (int i = 0; _buffer[i]; i++) {
 | 
			
		||||
        if (putc(_buffer[i]) < 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Finish with newline
 | 
			
		||||
    for (size_t i = 0; _output_delimiter[i]; i++) {
 | 
			
		||||
        if (putc(_output_delimiter[i]) < 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    debug_if(_dbg_on, "AT> %s\n", _buffer);
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ATCmdParser::vrecv(const char *response, va_list args)
 | 
			
		||||
{
 | 
			
		||||
restart:
 | 
			
		||||
    _aborted = false;
 | 
			
		||||
    // Iterate through each line in the expected response
 | 
			
		||||
    while (response[0]) {
 | 
			
		||||
        // Since response is const, we need to copy it into our buffer to
 | 
			
		||||
        // add the line's null terminator and clobber value-matches with asterisks.
 | 
			
		||||
        //
 | 
			
		||||
        // We just use the beginning of the buffer to avoid unnecessary allocations.
 | 
			
		||||
        int i = 0;
 | 
			
		||||
        int offset = 0;
 | 
			
		||||
        bool whole_line_wanted = false;
 | 
			
		||||
 | 
			
		||||
        while (response[i]) {
 | 
			
		||||
            if (response[i] == '%' && response[i + 1] != '%' && response[i + 1] != '*') {
 | 
			
		||||
                _buffer[offset++] = '%';
 | 
			
		||||
                _buffer[offset++] = '*';
 | 
			
		||||
                i++;
 | 
			
		||||
            } else {
 | 
			
		||||
                _buffer[offset++] = response[i++];
 | 
			
		||||
                // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
 | 
			
		||||
                if (response[i - 1] == '\n' && !(i >= 3 && response[i - 3] == '[' && response[i - 2] == '^')) {
 | 
			
		||||
                    whole_line_wanted = true;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Scanf has very poor support for catching errors
 | 
			
		||||
        // fortunately, we can abuse the %n specifier to determine
 | 
			
		||||
        // if the entire string was matched.
 | 
			
		||||
        _buffer[offset++] = '%';
 | 
			
		||||
        _buffer[offset++] = 'n';
 | 
			
		||||
        _buffer[offset++] = 0;
 | 
			
		||||
 | 
			
		||||
        debug_if(_dbg_on, "AT? %s\n", _buffer);
 | 
			
		||||
        // To workaround scanf's lack of error reporting, we actually
 | 
			
		||||
        // make two passes. One checks the validity with the modified
 | 
			
		||||
        // format string that only stores the matched characters (%n).
 | 
			
		||||
        // The other reads in the actual matched values.
 | 
			
		||||
        //
 | 
			
		||||
        // We keep trying the match until we succeed or some other error
 | 
			
		||||
        // derails us.
 | 
			
		||||
        int j = 0;
 | 
			
		||||
 | 
			
		||||
        while (true) {
 | 
			
		||||
            // Receive next character
 | 
			
		||||
            int c = getc();
 | 
			
		||||
            if (c < 0) {
 | 
			
		||||
                debug_if(_dbg_on, "AT(Timeout)\n");
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            // Simplify newlines (borrowed from retarget.cpp)
 | 
			
		||||
            if ((c == CR && _in_prev != LF) ||
 | 
			
		||||
                    (c == LF && _in_prev != CR)) {
 | 
			
		||||
                _in_prev = c;
 | 
			
		||||
                c = '\n';
 | 
			
		||||
            } else if ((c == CR && _in_prev == LF) ||
 | 
			
		||||
                       (c == LF && _in_prev == CR)) {
 | 
			
		||||
                _in_prev = c;
 | 
			
		||||
                // onto next character
 | 
			
		||||
                continue;
 | 
			
		||||
            } else {
 | 
			
		||||
                _in_prev = c;
 | 
			
		||||
            }
 | 
			
		||||
            _buffer[offset + j++] = c;
 | 
			
		||||
            _buffer[offset + j] = 0;
 | 
			
		||||
 | 
			
		||||
            // Check for oob data
 | 
			
		||||
            for (struct oob *oob = _oobs; oob; oob = oob->next) {
 | 
			
		||||
                if ((unsigned)j == oob->len && memcmp(
 | 
			
		||||
                            oob->prefix, _buffer + offset, oob->len) == 0) {
 | 
			
		||||
                    debug_if(_dbg_on, "AT! %s\n", oob->prefix);
 | 
			
		||||
                    oob->cb();
 | 
			
		||||
 | 
			
		||||
                    if (_aborted) {
 | 
			
		||||
                        debug_if(_dbg_on, "AT(Aborted)\n");
 | 
			
		||||
                        return false;
 | 
			
		||||
                    }
 | 
			
		||||
                    // oob may have corrupted non-reentrant buffer,
 | 
			
		||||
                    // so we need to set it up again
 | 
			
		||||
                    goto restart;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Check for match
 | 
			
		||||
            int count = -1;
 | 
			
		||||
            if (whole_line_wanted && c != '\n') {
 | 
			
		||||
                // Don't attempt scanning until we get delimiter if they included it in format
 | 
			
		||||
                // This allows recv("Foo: %s\n") to work, and not match with just the first character of a string
 | 
			
		||||
                // (scanf does not itself match whitespace in its format string, so \n is not significant to it)
 | 
			
		||||
            } else {
 | 
			
		||||
                sscanf(_buffer + offset, _buffer, &count);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // We only succeed if all characters in the response are matched
 | 
			
		||||
            if (count == j) {
 | 
			
		||||
                debug_if(_dbg_on, "AT= %s\n", _buffer + offset);
 | 
			
		||||
                // Reuse the front end of the buffer
 | 
			
		||||
                memcpy(_buffer, response, i);
 | 
			
		||||
                _buffer[i] = 0;
 | 
			
		||||
 | 
			
		||||
                // Store the found results
 | 
			
		||||
                vsscanf(_buffer + offset, _buffer, args);
 | 
			
		||||
 | 
			
		||||
                // Jump to next line and continue parsing
 | 
			
		||||
                response += i;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Clear the buffer when we hit a newline or ran out of space
 | 
			
		||||
            // running out of space usually means we ran into binary data
 | 
			
		||||
            if (c == '\n' || j + 1 >= _buffer_size - offset) {
 | 
			
		||||
                debug_if(_dbg_on, "AT< %s", _buffer + offset);
 | 
			
		||||
                j = 0;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Mapping to vararg functions
 | 
			
		||||
int ATCmdParser::printf(const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, format);
 | 
			
		||||
    int res = vprintf(format, args);
 | 
			
		||||
    va_end(args);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ATCmdParser::scanf(const char *format, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, format);
 | 
			
		||||
    int res = vscanf(format, args);
 | 
			
		||||
    va_end(args);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ATCmdParser::send(const char *command, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, command);
 | 
			
		||||
    bool res = vsend(command, args);
 | 
			
		||||
    va_end(args);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ATCmdParser::recv(const char *response, ...)
 | 
			
		||||
{
 | 
			
		||||
    va_list args;
 | 
			
		||||
    va_start(args, response);
 | 
			
		||||
    bool res = vrecv(response, args);
 | 
			
		||||
    va_end(args);
 | 
			
		||||
    return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// oob registration
 | 
			
		||||
void ATCmdParser::oob(const char *prefix, Callback<void()> cb)
 | 
			
		||||
{
 | 
			
		||||
    struct oob *oob = new struct oob;
 | 
			
		||||
    oob->len = strlen(prefix);
 | 
			
		||||
    oob->prefix = prefix;
 | 
			
		||||
    oob->cb = cb;
 | 
			
		||||
    oob->next = _oobs;
 | 
			
		||||
    _oobs = oob;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ATCmdParser::abort()
 | 
			
		||||
{
 | 
			
		||||
    _aborted = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool ATCmdParser::process_oob()
 | 
			
		||||
{
 | 
			
		||||
    if (!_fh->readable()) {
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    int i = 0;
 | 
			
		||||
    while (true) {
 | 
			
		||||
        // Receive next character
 | 
			
		||||
        int c = getc();
 | 
			
		||||
        if (c < 0) {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        _buffer[i++] = c;
 | 
			
		||||
        _buffer[i] = 0;
 | 
			
		||||
 | 
			
		||||
        // Check for oob data
 | 
			
		||||
        struct oob *oob = _oobs;
 | 
			
		||||
        while (oob) {
 | 
			
		||||
            if (i == (int)oob->len && memcmp(
 | 
			
		||||
                        oob->prefix, _buffer, oob->len) == 0) {
 | 
			
		||||
                debug_if(_dbg_on, "AT! %s\r\n", oob->prefix);
 | 
			
		||||
                oob->cb();
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            oob = oob->next;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Clear the buffer when we hit a newline or ran out of space
 | 
			
		||||
        // running out of space usually means we ran into binary data
 | 
			
		||||
        if (i + 1 >= _buffer_size ||
 | 
			
		||||
                strcmp(&_buffer[i - _output_delim_size], _output_delimiter) == 0) {
 | 
			
		||||
 | 
			
		||||
            debug_if(_dbg_on, "AT< %s", _buffer);
 | 
			
		||||
            i = 0;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,33 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (c) , Arm Limited and affiliates.
 | 
			
		||||
 * SPDX-License-Identifier: Apache-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
#ifndef __AT_CMD_PARSER_H__
 | 
			
		||||
#define __AT_CMD_PARSER_H__
 | 
			
		||||
 | 
			
		||||
#include "mbed.h"
 | 
			
		||||
#include <cstdarg>
 | 
			
		||||
#include "FileHandle.h"
 | 
			
		||||
 | 
			
		||||
class ATCmdParser {
 | 
			
		||||
public:
 | 
			
		||||
    ATCmdParser(mbed::FileHandle *fh, const char *output_delimiter = "\r",
 | 
			
		||||
                int buffer_size = 256, int timeout = 8000, bool debug = false) {}
 | 
			
		||||
 | 
			
		||||
    ~ATCmdParser() {}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif //__AT_CMD_PARSER_H__
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -166,10 +166,16 @@ restart:
 | 
			
		|||
 | 
			
		||||
        while (response && response[i]) {
 | 
			
		||||
            if (response[i] == '%' && response[i + 1] != '%' && response[i + 1] != '*') {
 | 
			
		||||
                if ((offset + 2) > _buffer_size) {
 | 
			
		||||
                    return -1;
 | 
			
		||||
                }
 | 
			
		||||
                _buffer[offset++] = '%';
 | 
			
		||||
                _buffer[offset++] = '*';
 | 
			
		||||
                i++;
 | 
			
		||||
            } else {
 | 
			
		||||
                if ((offset + 1) > _buffer_size) {
 | 
			
		||||
                    return -1;
 | 
			
		||||
                }
 | 
			
		||||
                _buffer[offset++] = response[i++];
 | 
			
		||||
                // Find linebreaks, taking care not to be fooled if they're in a %[^\n] conversion specification
 | 
			
		||||
                if (response[i - 1] == '\n' && !(i >= 3 && response[i - 3] == '[' && response[i - 2] == '^')) {
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +188,9 @@ restart:
 | 
			
		|||
        // Scanf has very poor support for catching errors
 | 
			
		||||
        // fortunately, we can abuse the %n specifier to determine
 | 
			
		||||
        // if the entire string was matched.
 | 
			
		||||
        if ((offset + 3) > _buffer_size) {
 | 
			
		||||
            return -1;
 | 
			
		||||
        }
 | 
			
		||||
        _buffer[offset++] = '%';
 | 
			
		||||
        _buffer[offset++] = 'n';
 | 
			
		||||
        _buffer[offset++] = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -229,6 +238,9 @@ restart:
 | 
			
		|||
                _in_prev = c;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ((offset + j + 1) > _buffer_size) {
 | 
			
		||||
                return -1;
 | 
			
		||||
            }
 | 
			
		||||
            _buffer[offset + j++] = c;
 | 
			
		||||
            _buffer[offset + j] = 0;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue