Нужен код парсинга сообщений от цифрового протокола

FredP
Offline
Зарегистрирован: 18.02.2019

Разрабатываю ПО для работы с купюроприемником/модулем выдачи денег. Чувствую, потрачу много времени на разработку обработчика одной из команд. Поэтому, задача вам:

1. Получаю сообщение от устройства, записанное в массиве RX_buffer[]

Общая структура сообщения:

 

здесь интерес представляет только количество байт данных, то есть длина сообщения за минусом 4 (заголовок)

Мне нужен код, обрабатывающий ответ купюрника о своем статусе. Это команда 29.

Пример:

(dispensing)

здесь после первых 3х байт идет код статуса а дальше данные. Проблема в том, что в этом сообщении может быть несколько событий подряд. Длина данных события указана только в даташите (таблица ниже), она переменная.

В результате код должен:

1. Прочитать события из массива RX_buffer[] по очереди. В каждом событии код должен печатать в serial текст события (первая колонка таблицы).

2. При повторном запуске, если код события и данные в нем не изменилось - пропустить вывод в serial и перейти к следующему событию. Если события кончились, опять парсим  RX_buffer[].

Жду ваши предложения  с ценой и условиями приемки\оплаты на fprotasov gmail.com

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Как то странно все это выглядит. Вы уточните после трех байт или после четырех. Если пример сообщения - диспенсинг, то номер сообщения в 4-ом байте. А вот расчет контрольной суммы следует уточнить алгоритм расчета. И что значит несколько команд подряд ? Прям несколько статусов или один статус с различным содержимым ?

FredP
Offline
Зарегистрирован: 18.02.2019

Событие это код события + данные. Итого в ответе приходит заголовок, код события 1, данные события 1, код события 2, данные события 2, код события 3, данные события 3. Контрольную сумму трогать не нужно, я ее сам проверяю, тут нет проблемы.
Заголовок состоит все же из 4х байт, дальше идут события.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

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

FredP
Offline
Зарегистрирован: 18.02.2019

Вторым байтом идёт длина данных, включая коды и данных всех событий. КС будет находиться в позиции "значение байта 1 +4" Так что оставьте ее.

Размер буфера 3-5 событий. То есть от нескольких байт до например, 64.
Я запускаю код периодически. Например раз в секунду. За эту секунду может случиться несколько событий, а может длиться одно и то же. К примеру событие опустошения модуля может занимать 10-50 секунд. Вот в этом случае мне не нужно повторно писать в терминал что идет опустошение. Но за эти 50 секунд могут появиться другие события, их нужно обработать.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Действительно, есть же размер... В течении какого срока нужно выполнить задание ? 

karamzin01
Offline
Зарегистрирован: 08.03.2018

интересно, взялся бы- maslachenko767@mail.ru , консультации, подбор компонентов бесплатно, гарантии

b707
Offline
Зарегистрирован: 26.05.2017

если предыдущие откажутся - пишите ded собака cur-ex.ru

FredP
Offline
Зарегистрирован: 18.02.2019

Утром на свежую голову уточняю: помимо текста события в терминал нужно писать его данные (если они есть). 

Время не горит особо. Думаю, тут на 2-3 часа работы. До конца недели хотелось бы получить результат.

sadman41
Offline
Зарегистрирован: 19.10.2016

А я думаю, что на день работы. Откуда взяты 2-3 часа?

FredP
Offline
Зарегистрирован: 18.02.2019

Ни откуда. Субъективное мнение. Просто для масштаба с неделей.

man9913
Offline
Зарегистрирован: 19.03.2016

Ты бы ещё масштаб бюджета озвучил. А то есть подозрение что тебе сроки не к неделе, а к бесконечности надо масштабировать.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Возьму в работу от 3500. Проверка на тестовом массиве , предоставоенном заказчиком.

FredP
Offline
Зарегистрирован: 18.02.2019

brokly пишет:
Возьму в работу от 3500. Проверка на тестовом массиве , предоставоенном заказчиком.

Ну вот и первое предложение. Цена нормальная. День оплаты работы высококвалифицированного специалиста. Жду еще предложений.

sadman41
Offline
Зарегистрирован: 19.10.2016

Без налогов, амортизации оборудования, производственных рисков и пр. затрат ))

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

FredP
Offline
Зарегистрирован: 18.02.2019

Пример буфера и необходмый результат состряпал:

номер байта Результат парсинга: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
пример сообщения 1 idle 3 1 1 0                      
пример сообщения 2 jammed (4321) 3 5 1 5 1 2 3 4              
пример сообщения 3 floating(100) 3 10 1 7 100 0 0 0 5 1 2 3 4    
пример сообщения 4 Cashbox removed
coins low
floated (4321)
3 12 1 5 1 2 3 4 31 3 8 1 2 3 4
пример сообщения 5 idle 3 1 1 0                      

 

bwn
Offline
Зарегистрирован: 25.08.2014

"А выводы тут простые - На каждом российском профильном интернетовском форуме, сегодня правят маленькте кучки завсегдатаев.  Эти завсегдатаи, в своей реальной профессиональной деятельности, мало чего достигли,  но в виртуальном пространстве интернета мнят себя суппер профессионалами. Они не терпят другого мнения,  и всеми доступными им путями ( а они часто имеют административные полномочия на форуме за выслугу лет) либо банят своего оппонента, либо чистят темы так,  что у любого стороннего человека,  случайно заглянувшего в тему,  создается впечатление что он насчтупил в какашки."

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

P/S Вычистил весь флуд.

p.masyukov
p.masyukov аватар
Offline
Зарегистрирован: 14.11.2015

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

Green
Offline
Зарегистрирован: 01.10.2015

Так ТС же сам пишет. И 3 часа уже прошло...

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

p.masyukov пишет:

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

Да это фейковый заказчик. Тему уже почистили. ТС сюда приходил алегура побесить :) Выяснилось, что ТС супер-пупер, прости господи, недопрограммер, мастерски владеет лабвью, может из квадратиков nакую картинку накропать - закачаешься. И задача эта ему на 15 минут. А все ардуино писатели, которые тутусюся здесь, просто недоумки :)

bwn
Offline
Зарегистрирован: 25.08.2014

brokly пишет:

Да это фейковый заказчик. Тему уже почистили. ТС сюда приходил алегура побесить :) Выяснилось, что ТС супер-пупер, прости господи, недопрограммер, мастерски владеет лабвью, может из квадратиков nакую картинку накропать - закачаешься. И задача эта ему на 15 минут. А все ардуино писатели, которые тутусюся здесь, просто недоумки :)

Не, если хотите повторно прослушать курс ценообразования, я не против, я верну, ток скажите.

b707
Offline
Зарегистрирован: 26.05.2017

bwn пишет:

Не, если хотите повторно прослушать курс ценообразования, я не против, я верну, ток скажите.

ни в коем случае :)

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Спасибо, уже вчера предлагали :) Я его наизусть выучил.

FredP
Offline
Зарегистрирован: 18.02.2019

Ого вы тут нафлудили! Особенно брокля. Ты же за заказы меньше 10 тысяч не берешься, в профильной теме написано, чего тут забыл?

Исполнителю - спасибо! За один день и 1500 рублей выполнил задачу. Даже лучше, чем я думал. Приятно работать с такими людьми.

Может, кому пригодится.

#include "events.h"

// test message arrays

byte msg0[] = { 3, 1, 1, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
byte msg1[] = { 3, 5, 1, 29, 5, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0 };
byte msg2[] = { 3, 10, 1, 29, 7, 100, 0, 0, 0, 5, 1, 2, 3, 4, 0, 0 };
byte msg3[] = { 3, 12, 1, 29, 5, 1, 2, 3, 4, 31, 3, 8, 18, 2, 0, 0 };
byte msg4[] = { 3, 1, 1, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

byte* messages[] = { msg0 , msg1 , msg2 , msg3 , msg4 };


// Число тестовых сообщений (только для тестирования)
#define MESS_COUNT 5

// Переключение показа дублей событий
// 0 - скрываем повторяющиеся события, 1 - показываем все
#define SHOW_DUBLES 0


// Полный размер события (1 байт код + от 0 до 8 байт данных) для данного кода
// Для кодов, отсутвующих в таблице - возвращает 0
uint8_t get_event_data_len(uint8_t e_code) {
if (e_code > MAX_EVENT_CODE) return 0;
return (pgm_read_byte(event_len + e_code));
}


// Поиск текущего обрабатываемого события в предыдущем сообщении
// event_ptr - указатель на первый байт события в текущем сообщении
// old_message - указатель на начало предыдущего сообщения
// Возвращает :
// 0 - если событие присутсвует в предыдущем сообщении
// 1 - если события нет или ссылка на предыдущее сообщения пустая
// 2 - если в предыдущем сообщении есть ошибка синтаксиса

uint8_t compare(uint8_t* event_ptr, uint8_t* old_message) {
	if (old_message == NULL) return 1;
	uint8_t a_code = *event_ptr;
	uint8_t a_len = get_event_data_len(a_code);

	uint8_t old_mess_len = *(old_message + DATA_LEN_ADDR);
	uint8_t* old_mess_ptr = old_message + START_DATA_ADDR;
	uint8_t data_len;

	while (old_mess_len > 0) {
		uint8_t code = *old_mess_ptr;
		data_len = get_event_data_len(code);
		if ((data_len == 0) || (data_len > old_mess_len)) return 2;
		if (a_code == code) {
			if (a_len == 1) return 0;
			if (0 == memcmp(event_ptr, old_mess_ptr, a_len)) return 0;
		}

		old_mess_len -= data_len;
		old_mess_ptr += data_len;

	}
	return 1;
}

// Печать 4 байтового целого как суммы 
// (страница 6 мануала)
// NOTE! -для хранения суммы НЕЛЬЗЯ использовать тип float 
// из-за ошибок округления
void print_cash_amount(unsigned long amount) {
	Serial.print(" ");
	Serial.print(amount / 100);
	Serial.print(".");
	uint8_t cents = amount % 100;
	if (cents < 10) Serial.print("0");
	Serial.print(cents);
}

// Вывод в Сериал информации о событии
// cod	-	код события
// dlen -	размер события в байтах (код + данные)
// ptr -	указатель на первый байт события в текущем сообщении
void print_event(uint8_t cod, uint8_t dlen, uint8_t* ptr) {
	uint8_t bb;
	unsigned long fl;
	Serial.print(" ");
	Serial.print(cod);
	Serial.print(" ");
	if (cod > MAX_EVENT_CODE) cod = MAX_EVENT_CODE + 1;
	Serial.print(PGM_CHR pgm_read_word(&(event_names[cod])));
	ptr++;
	switch (dlen) {
	case 9:
		fl = *((long*)((uint8_t*)(ptr)));
		print_cash_amount(fl);
		ptr += 4;
	case 5:
		fl = *((long*)((uint8_t*)(ptr)));
		print_cash_amount(fl);
		Serial.println();
		break;
	case 3:
		bb = *((uint8_t*)(ptr));
		Serial.print(" ");
		Serial.print(bb);
		ptr++;
	case 2:
		bb = *((uint8_t*)(ptr));
		Serial.print(" ");
		Serial.println(bb);
		break;
	case 1:
		Serial.println();
	}

}

// Парсинг текущего сообщения
// message - указатель на начало обрабатываемого сообщения
// prev_message - указатель на начало предыдущего
void parse_message(uint8_t* message, uint8_t* prev_message) {
	uint8_t* buf_ptr = message;
	uint8_t message_len = *(buf_ptr + DATA_LEN_ADDR);
	buf_ptr += START_DATA_ADDR;
	while (message_len > 0) {
		uint8_t code = *(buf_ptr);
		uint8_t data_len = get_event_data_len(code);
		
		if (data_len > message_len) data_len = 0;
		if (SHOW_DUBLES || (0 != compare(buf_ptr, prev_message))) print_event(code, data_len, buf_ptr);
		if (data_len == 0) { Serial.println(F(" <<<<< Syntax error! "));break; }
		message_len -= data_len;
		buf_ptr += data_len;
	}
}



void setup() {
	

	Serial.begin(9600);
	uint8_t* curr_mess;
	uint8_t* prev_mess = 0;
	for (byte i = 0; i < MESS_COUNT; i++) {
		curr_mess = messages[i];
		parse_message(curr_mess, prev_mess);
		prev_mess = curr_mess;
	}
	

}

void loop() {
	

}
#pragma once
#include <avr/pgmspace.h>
#include <Arduino.h>

#define PGM_CHR     (const __FlashStringHelper *) 

// event length in bytes (code + data)
// 0 for undefined events
const byte event_len[] PROGMEM = {
	1,5,5,1,1,5,5,5,    //00 - 07
	5,5,9,9,5,5,1,1,    //08 - 0F
	5,1,1,1,5,5,1,1,    //10 - 17
	1,1,1,1,5,5,1,1,    //18 - 1F
	1,1,1,0,2,1,1,5,    //20 - 27
	5,0,0,0,0,0,0,0,    //28 - 2F
	0,0,0,0,1,1,5,3,    //30 - 37
	2,5,0                 //38 - 39
};

// event titles named by event code
const char event_00[] PROGMEM = "Idle";
const char event_01[] PROGMEM = "Dispensing";
const char event_02[] PROGMEM = "Dispensed";
const char event_03[] PROGMEM = "Coins Low";
const char event_04[] PROGMEM = "Empty";
const char event_05[] PROGMEM = "Jammed";
const char event_06[] PROGMEM = "Halted";
const char event_07[] PROGMEM = "Floating";

const char event_08[] PROGMEM = "Floated";
const char event_09[] PROGMEM = "Timeout";
const char event_0A[] PROGMEM = "Incomplete payout";
const char event_0B[] PROGMEM = "Incomplete float";
const char event_0C[] PROGMEM = "Cashbox paid";
const char event_0D[] PROGMEM = "Coin credit";
const char event_0E[] PROGMEM = "Emptying";
const char event_0F[] PROGMEM = "Emptied";

const char event_10[] PROGMEM = "Fraud attempt";
const char event_11[] PROGMEM = "Disabled";
const char event_12[] PROGMEM = "Note stored";
const char event_13[] PROGMEM = "Slave reset";
const char event_14[] PROGMEM = "Note read";
const char event_15[] PROGMEM = "Note credit";
const char event_16[] PROGMEM = "Note rejecting";
const char event_17[] PROGMEM = "Note rejected";

const char event_18[] PROGMEM = "Note stacking";
const char event_19[] PROGMEM = "Note stacked";
const char event_1A[] PROGMEM = "Note path jam";
const char event_1B[] PROGMEM = "Note stack jam";
const char event_1C[] PROGMEM = "Note from front at start";
const char event_1D[] PROGMEM = "Note stacked at start";
const char event_1E[] PROGMEM = "Cashbox full";
const char event_1F[] PROGMEM = "Cashbox removed";

const char event_20[] PROGMEM = "Cashbox replaced";
const char event_21[] PROGMEM = "Lid open";
const char event_22[] PROGMEM = "Lid closed";
//const char event_23[] PROGMEM = "		no event for code 0x23
const char event_24[] PROGMEM = "Calibration fault";
const char event_25[] PROGMEM = "Attached mech jam";
const char event_26[] PROGMEM = "Attached mech open";
const char event_27[] PROGMEM = "Smart emptying";
const char event_28[] PROGMEM = "Smart empytied";

// no events for codes 0x29 - 0x33

const char event_34[] PROGMEM = "Barcode escrow";
const char event_35[] PROGMEM = "Barcode stacked";
const char event_36[] PROGMEM = "Multiple coins added";
const char event_37[] PROGMEM = "Peripheral error";
const char event_38[] PROGMEM = "Peripheral device disabled";
const char event_39[] PROGMEM = "Note held in bezel";

//virtual event for undefined code
const char event_UNKNOWN[] PROGMEM = "Unknown event";

// array of event names (titles) indexed by event code
const char *const event_names[] PROGMEM = {
	event_00, event_01, event_02, event_03, event_04, event_05, event_06, event_07,
	event_08, event_09, event_0A, event_0B, event_0C, event_0D, event_0E, event_0F,
	event_10, event_11, event_12, event_13, event_14, event_15, event_16, event_17,
	event_18, event_19, event_1A, event_1B, event_1C, event_1D, event_1E, event_1F,
	event_20, event_21, event_22, event_UNKNOWN, event_24, event_25, event_26, event_27,
	event_28, event_UNKNOWN,event_UNKNOWN,event_UNKNOWN,event_UNKNOWN,event_UNKNOWN,event_UNKNOWN,event_UNKNOWN,
	event_UNKNOWN,event_UNKNOWN,event_UNKNOWN,event_UNKNOWN, event_34, event_35, event_36, event_37,
	event_38, event_39,event_UNKNOWN,
};

// last defined event
#define MAX_EVENT_CODE   0x39

// position of data length in message
#define DATA_LEN_ADDR    1

// position of first data byte in message 
#define START_DATA_ADDR  4

 

ALEGYR
Offline
Зарегистрирован: 20.09.2018

FredP пишет:

Исполнителю - спасибо! За один день и 1500 рублей выполнил задачу. Даже лучше, чем я думал. Приятно работать с такими людьми.

А чего ник героя не указал? 

Если он сделал лучше чем ты думал, мог бы и объективную рекламку ему сделать! И ему бы было бы приятно, и будущим заказчикам стало бы известно к кому можно обращаться,  чтобы было недорого, быстро  и лучше чем хочется!

А за выложенный скетч спасибо! Теперь можно будет и его продать!!!

FredP
Offline
Зарегистрирован: 18.02.2019

Продавайте, дарю.

ALEGYR
Offline
Зарегистрирован: 20.09.2018

FredP пишет:

Продавайте, дарю.

Спасибо!

А  что там с  ником героя? Он так и останется неизвестным, или все-таки будущие заказчики узнают к кому надо обращаться?

ALEGYR
Offline
Зарегистрирован: 20.09.2018

ALEGYR пишет:

А за выложенный скетч спасибо! Теперь можно будет и его продать!!!

Извини! Беру свои слова обратно!

Скетч оказался нерабочим!!!

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Бывает, чо. 

FredP
Offline
Зарегистрирован: 18.02.2019

Второй код это заголовочный файл headers.h Положить рядом со основным скетчем ino. Тогда все заработает. 

ALEGYR
Offline
Зарегистрирован: 20.09.2018

FredP пишет:

Второй код это заголовочный файл headers.h Положить рядом со основным скетчем ino. Тогда все заработает. 

Это ты своему заказчику рассказывай,  когда он все это проверит на реальном банкомате!

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Код и правда не работает :) Фуфел :)

 

byte msg0[] = { 3, 1, 1, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
byte msg1[] = { 3, 5, 1, 29, 5, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0 };
byte msg2[] = { 3, 10, 1, 29, 7, 100, 0, 0, 0, 5, 1, 2, 3, 4, 0, 0 };
byte msg3[] = { 3, 5, 1, 29, 5, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0 };
byte msg4[] = { 3, 10, 1, 29, 7, 100, 0, 0, 0, 7, 1, 2, 3, 4, 0, 0 };
 0 Idle
 5 Jammed 673059.85
 7 Floating 1.00
 7 Floating 1.00
 7 Floating 673059.85
 
ALEGYR
Offline
Зарегистрирован: 20.09.2018

FredP пишет:

Второй код это заголовочный файл headers.h Положить рядом со основным скетчем ino. Тогда все заработает. 

Ты можешь даже два хедерса положить рядом с основным скетчем ino,  но пока в твоем ino будет написана пустая "loop",  вся твоя  ino выдаст 5 сообщений parse_message и замолчит!

-NMi-
Offline
Зарегистрирован: 20.08.2018

Интересно деФфки кодят по четыре штуки в ряд)))