Сохранение и загрузка переменных в SPIFFS

Виктория
Offline
Зарегистрирован: 12.05.2019

Подскажите, как правильно сделать. Хочу загружать данные с встроенной памяти (ESP8266) в переменные(статические настройки сети и пр.). Вижу два варианта:

1.json. Пробовала, но там непонятно с типами данных. Во всех примерах можно загрузить только char, в самом коде библиотеки, можно преобзовывать  json["val-name"].is<int>(); но на практике это не работает.

2. либо читать построково через саму ФС ESP8266. Но ненашла примера... как именно считать строку... Подскажите, будьте добры!

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

Скоко данных?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Виктория пишет:

1.json. ... но на практике это не работает.

2.  Но не нашла примера... как именно считать строку...

1. Да ну?

2. Объект файл унаследован от Stream, со всеми вытекающими.

https://github.com/gmag11/FSBrowserNG Вот это у меня для теста собраное на столе лежит. Исходник нужно переделать под JSON6. Я  сейчас порт к плате пробросил у себя на сервере: mycortez.ru:22280/admin. Утром выключу. Можно насладиться. ;))) Сама плата - то есть объект издевательств - тестовый NodeMCU Amica. с 4 Мегами, два из них отдано FFS.

Сама программа - просто пример от автора кода (поклон ему) по ссылке, только переделанный под новую версию json библиотеки. Так что всё там работает. Просто нужны руки не из жопы.

--------------

ЗЫ: в 10-30 отключил порт

Виктория
Offline
Зарегистрирован: 12.05.2019

sadman41 пишет:

Скоко данных?

Мало. Не более 10-15-ти переменных.

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

Пишите в эмулируемый EEPROM структурой (struct) и читайте так же.

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

sadman41 пишет:

Пишите в эмулируемый EEPROM структурой (struct) и читайте так же.

Тем более, что в случае с ESP,  по сути, это одно и тоже.

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

Вот и я о том же - чего дуремарствовать с JSON-ами из-за простого конфига.

Logik
Offline
Зарегистрирован: 05.08.2014

JSON оно понятно лишнее. Но с SPIFFS есть засада  https://habr.com/ru/post/409911/ . Может это и случилось у ТС.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Ребята! Это ESP, а не AVR. Тут памяти бесконечно. Можно нормально пользоваться и Стрингами и  реально ОЧЕНЬ профессиональной JSON библиотекой. Кто кодит на С++ не в микроконтроллерах может оценить код JSON 6. Наследование темплейты, автокастинг, умные перегрузки... ну вот сам-бы стал писать - не стал бы так аккуратно делать, потому что лень! ;)))

5-ая версия была классика - отдельно аррей и отдельно объект. В 6-ой они слили в единый интерфейс всё. Просто песня. Очень удобно пользоваться.

Еще раз, для доходчивости - это ESP, тут не надо плясать с бубном и экономить байты и такты. И для кода и для рантайма тут более чем достаточно памяти.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

JSON оно понятно лишнее. Но с SPIFFS есть засада  https://habr.com/ru/post/409911/ . Может это и случилось у ТС.

Чудо в перьях! Ты дату публикации на хабре смотрел?

Виктория
Offline
Зарегистрирован: 12.05.2019

Всем спасибо! Нашла вот такую библиотечку https://github.com/bneedhamia/sdconfigfile , может кому понадобится.

Logik
Offline
Зарегистрирован: 05.08.2014

wdrakula пишет:

Logik пишет:

JSON оно понятно лишнее. Но с SPIFFS есть засада  https://habr.com/ru/post/409911/ . Может это и случилось у ТС.

Чудо в перьях! Ты дату публикации на хабре смотрел?

аяяя.. 2018год! Для тебя, додик в стрингах наверно очень старое. Хотя видно попаболь после последнего общения заставляет искать хоть какой повод чтоб тявкнуть ))) Это пройдет.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

бла-бла-бла

Больной? Ты написал векторную "8" на 17 байт, не я. И считаешь себя програмистом? Ну молодец! Мамка похвалит.

Самое смешное, что у тебя хватает тупости хвастать этим, с позволения сказать, кодом.

------------------------

Ну и в том общении я не помню "соревнований", чтобы кто-то победил или проиграл. Ты "наехал" на компилятор - это всегда принак тупости и непрофессионализма. Ровно так Архат уже делал. А я-то причем? Ты пафосно пиздил о "раздолбаях, пишуших компилятор". Вероятно сравнивая их с собой... и 17 байтами на код символа "8".

 

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

 повод чтоб тявкнуть ))) Это пройдет.

У тебя - пройдет желание влезть с "Очень Ценным Мнением" в тему, в которой ты ничего не понимаешь. Странно, но снова напоминает Архата.

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

А тебе нужно было в лужу пернуть... а то ведь позабудут... апидна, да?

-----------------------

=====================

Я, к сожалению, забыл ветку где ты векторные шрифты изобретаешь. Поэтому тут уж добавлю, тема обсуждения все равно закрыта.

С памятью AVR задача не нужна. Ну такая же глупость, как "интернет радио" и половина мастурбации у нас в "проектах". Ты в своем проекте никогда не будешь использовать все 160 символов. Нарисуй вектора с Move,Line и Quad, для тех символов, которые нужны в проекте.

Если тебе нужна продвинутая графика - то это уже не 128х64 дюймовик. И контроллер тогда с нормальной памятью в котором можно рендерить TTF. И да - наезды на СТМ - это уже клоунада... и да снова копия Архата. ;)

(а может ты это он??? ;)))) )

Ты сам раньше выступал за разумность (помню спор "список vs перебор"), а тут тебя заклинило.

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

wdrakula пишет:

Ребята! Это ESP, а не AVR. Тут памяти бесконечно. Можно нормально пользоваться и Стрингами и  реально ОЧЕНЬ профессиональной JSON библиотекой. Кто кодит на С++ не в микроконтроллерах может оценить код JSON 6. Наследование темплейты, автокастинг, умные перегрузки... ну вот сам-бы стал писать - не стал бы так аккуратно делать, потому что лень! ;)))

5-ая версия была классика - отдельно аррей и отдельно объект. В 6-ой они слили в единый интерфейс всё. Просто песня. Очень удобно пользоваться.

Еще раз, для доходчивости - это ESP, тут не надо плясать с бубном и экономить байты и такты. И для кода и для рантайма тут более чем достаточно памяти.

Ну-ну... Памяти как оказалось немножко не бесконечно, нехватило чуток, нужно было бесконечно+10К
На 5-ой версии пытался разбирать форекасты получаемые от openweathermap, текущая погода не очень интересовала, а прогнозы были интересны.
форекасты на 5 суток с 3-х часовым интервалом, весят под 13К, динамик буфер в примерах в два раза больше длины строки указывали, для чего не ясно.
На модуле свободного хипа оставалось чуть больше 30К, строка с джейсоном в 13К + динамик буфер 26К и памяти не осталось.
Зная структуру данных, резал масив и парсил частями.
Памяти много не бывает :)

Logik
Offline
Зарегистрирован: 05.08.2014

Алексей. пишет:

Ну-ну... Памяти как оказалось немножко не бесконечно, нехватило чуток, нужно было бесконечно+10К

..
Памяти много не бывает :)

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

Алексей.
Алексей. аватар
Offline
Зарегистрирован: 02.02.2018

Logik пишет:

Алексей. пишет:

Ну-ну... Памяти как оказалось немножко не бесконечно, нехватило чуток, нужно было бесконечно+10К

..
Памяти много не бывает :)

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

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

Logik
Offline
Зарегистрирован: 05.08.2014

Кстати, и о погоде ;) Я когда ковырял модем A6 в режиме TCP (это здесь - http://arduino.ru/forum/apparatnye-voprosy/gsm-modem-a6-v-rezhime-tcp ), то тоже прогноз тянул с прогнозных сайтов, не по их API (мне интересен именно модем, а не API), а как страницу HTTP, то там и сотни КБ получались. а это атмега 328р. Потому память - сразу решил не пытатся сохранять, парсить с лёту. Так что похоже "парсил частями" - наше все, хоть AVR, хоть ESP.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

а на хрень от wdrakula не обращай внимания

Ты держи перед глазами: "17 байт на символ 8" и радуйся. Програмист, my ass!

Logik
Offline
Зарегистрирован: 05.08.2014

wdrakula пишет:

...my ass!

Болит, понимаю, маж его чемто )))) От жеж недоумок!

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

https://arduinojson.org/
serializeJson(doc, myFile.csv);//Запись
...
deserializeJson(doc, myFile.csv);//Чтение

С этим выяснили.
А как структуру в файл записать?

file.write(myStruct, len);
file.read( myStruct, len);
Так будет работать?

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

Кастануть её к байтовому массиву?

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

del

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Не вариант. Согласен.
Нагуглил следующее: https://www.jarutex.com/index.php/2022/01/10/9366/

lfs_ssize_t lfs_file_write(lfs_t *lfs, lfs_file_t *file, const void *buffer, lfs_size_t size);

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

Еще из идей, пока не знаю как, узнать адресс начала файла и функцией mencpy скопировать структуру.

Подскажите как записать структуру в littleFS?
Несколько часов гуглю безрезультатно(.

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Вариант с граблями разыскал.
Суть такая:
1. Имеем структуру.
2. byte tmp;
3. Цикл побайтного копирования и побайтной записи в файл. memcopy(&struct, &tmp,1) и file.print(tmp) или file.write.
4. Файл сохранен.
5. Читаем в обратной последовательности. Сначала в байт, потом memcpy в структуру.

Вместо байта можно создать массив байтов[sizeof(struct)]. Но тут ОЗУ съедим sizeof(struct)

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

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

(byte*) myStruct

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Спасибо. Получилось. Но осталось несколько вопросов:

  while (file.available()) {
    byte tmp = file.read();
    memcpy((byte*)outStruct + file.position()-1, &tmp, sizeof(byte));
  }

​1. Как обойтись без tmp?

2. Какие типы нельзя использовать в структуре при записи в ФС? То что вычитал: нельзя использовать типы данных в виде ссылок, т.е. маcсивы данных и char[].

Собственно результат:

#include <FS.h>
#include <LittleFS.h>
#define settingsfile "/settings.bin" //путь к файлу для хранения данных
typedef struct Data_struct { // Тип структуры которую будем сохранять
  uint8_t x;
  uint16_t y;
  uint32_t z;
  float a;
  String str;
  char dd[10];
} Data_struct;

Data_struct WriteStruct = {255, 12, 12345, 3.14, "Hallo world"}; //Создаем и наполняем исходную структуру
Data_struct ReadStruct;//Создаем структуру, куда будем записывать данные из файловой системы. Можно писать в исходную.

bool WriteLFS(const char * path, Data_struct * inStruct)  {
  File file = LittleFS.open(path, "w");
  if (!file) return false;
  file.write((byte*)inStruct, sizeof(Data_struct) / sizeof(byte));
  file.close();
  return true;
}

bool ReadLFS(const char * path, Data_struct * outStruct)  {
  File file = LittleFS.open(path, "r");
  if (!file) return 0;
  while (file.available()) {
    byte tmp = file.read();
    memcpy((byte*)outStruct + file.position()-1, &tmp, sizeof(byte));
  }
  file.close();
  return true;
}

void setup() {
  Serial.begin(115200);
  if (!LittleFS.begin()) {
    Serial.println("LittleFS mount failed. Formating...");
    LittleFS.format();
    ESP.restart();
  }
  delay(3000); // Задержка на открытие com порта на ПК.

  if (WriteLFS(settingsfile, &WriteStruct)) { // передаю имя файла и адресс в памяти где хранится исходная структура с данными
    Serial.println("WriteLFS Ok");
  } else {
    Serial.println("WriteLFS failed");
  }

  if (ReadLFS(settingsfile, &ReadStruct)) {
    Serial.println("ReadLFS Ok");
  } else {
    Serial.println("ReadLFS failed");
  }

  Serial.printf("Result: %2d, %2d, %2d, %2.2f, %",
                ReadStruct.x, ReadStruct.y, ReadStruct.z, ReadStruct.a, ReadStruct.str); //Result: 255, 12, 12345, 3.14, Hallo world
  ESP.deepSleep(0);
}

void loop() {
}

 

 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

p-a-h-a пишет:

2. Какие типы нельзя использовать в структуре при записи в ФС? То что вычитал: нельзя использовать типы данных в виде ссылок, т.е. маcсивы данных и char[].

Чойта? Когда в структуре - там пофиг какие данные и какие типы (на сколько я знаю). Все равно по-байтово читается/пишется.

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

BOOM, Протестировал. Все работает включая массивы. Опробовано на ESP8266.

Код сохраняет структуру с разными типами данных в файловую систему микроконтроллера и потом считывает эти данные в другую структуру:

#include <FS.h>
#include <LittleFS.h>
#define settingsfile "/settings.bin" //путь к файлу для хранения данных
typedef struct Data_struct { // Тип структуры которую будем сохранять
  uint8_t x;
  uint16_t y;
  uint32_t z;
  float a;
  uint8_t intArr[10];
  char charArr[8];
  String str;
} Data_struct;

Data_struct WriteStruct = {255, 12, 12345, 3.14, {1,2,3,4,5,6,7,8,9,10}, "charArr", "String"}; //Создаем и наполняем исходную структуру
Data_struct ReadStruct;//Создаем структуру, куда будем записывать данные из файловой системы. Можно писать в исходную.

bool WriteLFS(const char * path, Data_struct * inStruct)  {
  File file = LittleFS.open(path, "w");
  if (!file) return false;
  file.write((byte*)inStruct, sizeof(Data_struct) / sizeof(byte));
  file.close();
  return true;
}

bool ReadLFS(const char * path, Data_struct * outStruct)  {
  File file = LittleFS.open(path, "r");
  if (!file) return false;
  file.read((byte*)outStruct, file.size());
  file.close();
  return true;
}

void setup() {
  Serial.begin(115200);
  if (!LittleFS.begin()) {
    Serial.println("LittleFS mount failed. Formating...");
    LittleFS.format();
    ESP.restart();
  }
  delay(3000); // Задержка на открытие com порта на ПК.
  if (WriteLFS(settingsfile, &WriteStruct)) { // передаю имя файла и адресс в памяти где хранится исходная структура с данными
    Serial.println("WriteLFS Ok");
  } else {
    Serial.println("WriteLFS failed");
  }

  if (ReadLFS(settingsfile, &ReadStruct)) {
    Serial.println("ReadLFS Ok");
  } else {
    Serial.println("ReadLFS failed");
  }

  Serial.printf("Result: %2d, %2d, %2d, %2.2f, %2d, %2s, %2s",
                ReadStruct.x, ReadStruct.y, ReadStruct.z, ReadStruct.a, ReadStruct.intArr[0], ReadStruct.charArr, ReadStruct.str); //Result: 255, 12, 12345, 3.14,  1, charArr, String
 
  ESP.deepSleep(0);
}

void loop() {}

 

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

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

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

всего-то надо поменять пару символов в твоей функции:

bool WriteLFS(const char * path, byte* buf, uint16_t len)  {
  File file = LittleFS.open(path, "w");
  if (!file) return false;
  file.write(buf, len);
  file.close();
  return true;
}

 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

b707 пишет:

писать отдельную функцию для записи и чтения конкретной структуры - это глупость. 

Нужно делать как удобнее в конкретном случае. И незачем усложнять на ровном месте.

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

Никакого усложнения нет в этом случае. Вызов функции рихтуем примерно под такую конфигурацию: WriteLFS(..., (byte*) &myStruct, sizeof(myStruct)) и всё.

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Переписал функции чтения/записи во флеш разных типов данных.

template<class T1, class T2>
bool WriteLFS(const char * path, T1* const &DATA, T2 const &lenght)  {//Запись в файл
  File file = LittleFS.open(path, "w");
  if (!file or lenght < 1) {
    file.close();
    return false;
  }
  file.write((uint8_t*)DATA, sizeof(T1) * lenght);
  file.close();
  return true;
}

template<class T1, class T2>
bool ReadLFS(const char * path, T1* &DATA, T2 &lenght)  {//Чтение из файла с заполнением DATA и lenght

  File file = LittleFS.open(path, "r");
  if (!file or !file.size() or (file.size() % sizeof(T1))) {
    file.close();
    return false;
  }
  lenght = file.size() / sizeof(T1);
  delete[] DATA;
  DATA  = new T1[lenght];//Создаем массив необходимого размера
  file.read((uint8_t*)DATA, file.size());
  file.close();
  return true;
}

Сохранение одной переменной:

  int lenght =sizeof(int);
  int *a  = new int;
  *a=5;
  WriteLFS(settingsfile, a, lenght);//Сохраняем массив В ФС
  int *b;
  ReadLFS(settingsfile, b, lenght);// Тут входящий массив удаляется и создается по новой из ФС, меняется lenght
Serial.println(*b); //5

Сохранение динамического массива из 10 ячеек:

  int lenght = 10;
  int *a  = new int[lenght];
  a[1] = 5;
  WriteLFS(settingsfile, a, lenght);//Сохраняем массив В ФС
  int *b;
  ReadLFS(settingsfile, b, lenght);// Тут входящий массив удаляется и создается по новой из ФС, меняется lenght
  Serial.println(b[1]);//5

Сохранение динамического массива структур:

 typedef  struct data { //Тип структуры пользовательский
  uint32_t var1 = NULL;
  uint16_t var2 = NULL;
} data;

  int lenght = 10;
  data *a  = new data[lenght];
  a[1].var1 = 5;
  WriteLFS(settingsfile, a, lenght);//Сохраняем массив В ФС
  data *b;
  ReadLFS(settingsfile, b, lenght);// Тут входящий массив удаляется и создается по новой из ФС, меняется lenght
  Serial.println(b[1].var1);//5
}

Сохранение структуры (также можно объект класса сохранить)

 typedef  struct data { //Тип структуры пользовательский
  uint32_t var1 = NULL;
  uint16_t var2 = NULL;
} data;
  data *myStruct = new data;
  (*myStruct).var1=5;
  int lenght = sizeof(data);
 
  WriteLFS(settingsfile, myStruct, lenght);//Сохраняем В ФС
  data *newStruct;
  ReadLFS(settingsfile, newStruct, lenght);
  Serial.println((*newStruct).var1);//5
}

И само собой необходимые заголовки

#include <FS.h>
#include <LittleFS.h>
const char* settingsfile PROGMEM = "/settings.bin";

void setup() {
  Serial.begin(115200);
  LittleFS.begin();
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017
 
 
10   data *b;
11   ReadLFS(settingsfile, b, lenght);// Тут входящий массив удаляется и создается по новой из ФС, меняется lenght
 

ты уверен? Памойму, ты просто срёшь за указатель sizeof(int)*(lenght-1) байт.  С виду то оно всё целое, но дьявол кроеца в деталях

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

Где ошибка? Какие пути исправления и избавления от говнокода? Разбираюсь потихоньку. Задача построить всеядные функции для сохранения и чтения любых объектов/переменных в ФС, желательно без перегрузок. len использую для динамических массивов, размер которых просто так не узнать. При этом не понимаю откуда оператор delete [] знает сколько ячеек памяти удалять.

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

p-a-h-a пишет:

Где ошибка?

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

Вот это вот все лишнее

len = file.size() / sizeof(T1);
  delete[] DATA;//Необходимость этой строки под вопросом. Если входящий массив нулевой длинны то не нужно.
  DATA  = new T1[len];//Создаем массив необходимого размера

Кроме того, подумайте над тем, что будет, если len =1. Вообще-то T1 и T1[1] - это разные типы переменных.

 

ЗЫ а еще интересно - вы постоянно хаете здешний форум и тут и на гайвере... но продолжаете сюда ходить. Это как у вас в мозгу - сочетаетеся?

p-a-h-a
Offline
Зарегистрирован: 17.01.2019

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

Если len == 1 создаcться массив на одну ячейку int *a=new int[1]; что равнозначно int *a=new int; В обоих случаях a[0] = 5; будет работать. 

С T и T[1] внимательно разобрался (дальше в коде понятно станет). Хорошо что обратили внимание. Понял некоторые вещи. Правильно у меня в коде было т.к. sizeof(int[0]) == NULL:

{
  uint8_t *a  = new uint8_t[5];
  Serial.println(sizeof(a));// == 4;
  Serial.println(sizeof(a[0]));// ==1;
  foo(a);
  foo2(a);
}
 template<class T>
  void foo(T* &a){ // разименованная ссылка на указатель. как раз первый элемент массива с типом uint8_t 
  Serial.println(sizeof(T)); // ==1     //sizeof(uint8_t)
  Serial.println(sizeof(T[0])); // ==0  //sizeof(uint8_t[0])
  Serial.println(sizeof(a[0])); // ==1
  Serial.println(sizeof(a)); // ==4
  }

 template<class T>
  void foo2(T &a){ // ссылка на указатель uint32_t хранящая адрес памяти первой ячейки дин. массива 
  Serial.println(sizeof(T)); // ==4
  Serial.println(sizeof(T[0])); // ==0
  Serial.println(sizeof(a[0])); // ==1
  Serial.println(sizeof(a)); // ==4
  }

PS Я не форум хаю, а отношение конкретных людей. По началу было непривычно что мешают с калом начинания и непонимание простых вещей. Сейчас просто пропускаю брань мимо себя. Давно это было.

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

p-a-h-a пишет:

PS Я не форум хаю, а отношение конкретных людей. По началу было непривычно что мешают с калом начинания и непонимание простых вещей. Сейчас просто пропускаю брань мимо себя. Давно это было.

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

Советую задуматься об этом феномене.