Форум программистов, компьютерный форум, киберфорум
АСУ ТП, промэлектроника
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  
 
 
Рейтинг 4.61/144: Рейтинг темы: голосов - 144, средняя оценка - 4.61
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382

Работа с Modbus RTU

09.04.2015, 11:18. Показов 35113. Ответов 169
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть некий модуль ввода/вывода, со своим адресом, дискретный и аналоговый сигнал поддерживает. RS-485 интерфейс, протокол MODBUS.

По СОМ порту передает данные на ПК.
Нужно написать приложеньеце, которое будет подключать с СОМ порту определенному, находить по адресу модуль, соединяться с ним и считывать с него и посылать на него данные.

Хочу понять с чего мне начать. Немного владею Delphi и хочу в ней написать простую программку. Подключение к модулю, считывать и посылать на него сигнал.

Горит.

Добавлено через 3 часа 2 минуты
Кто нибудь, подскажите алгоритм хотя бы, пожалуйста?!
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
09.04.2015, 11:18
Ответы с готовыми решениями:

Работа с контроллером производительности нории КПН-4М по Modbus RTU over RS-485
Здравствуйте товарищи. Попалось под руку, это чудо российской мысли, и понадобилось снять с него данные на ЭВМ. Вроде подключил, но...

Получить измеренное значение датчика давления (АИР-20/М2-МВ) по Modbus RTU
По работе понадобилось написать модуль для опроса показаний датчика давления АИР-20/М2-МВ. Читал руководство к датчику, читал интернет...

ТРМ-138-ModBus RTU. Не могу считать показания с термопары на втором канале
Здравствуйте уважаемые форумчане! Возникла проблема с чтением значения температуры на втором канале прибора ТРМ-138 (тип термопары ТХК(L))....

169
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
22.05.2015, 14:11  [ТС]
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от raxp Посмотреть сообщение
по флагу отслеживать прошедшее время со времени отправки команды, по GetTickCount например. При приходу ответа, признак сбрасывать, счетчик обнулять.
Допустим у меня GetTickCount от отправки до приема насчитывает 47, это значит потом это время надо будет сравнивать, если больше этого, значит ответа нет??? Типа такого алгоритма?
0
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
22.05.2015, 14:51
raxp, не горячитесь. Все не так уж "запущенно" . Конкретно проблема "подвисания" порта (у меня, да и не только) состояла в том, что поток, разбирающий сообщения от com-порта (одного или нескольких - не важно) - работал по принципу "спросить данные у порта, если нету - подождать NN мс", в бесконечном цикле, без каких-либо сообщений. Да, он обращался к внешним данным путем изменения свойство объектов и т.д. Проблема же и состояла в том, что если использовать прямые обращения к объекту порта из этого потока (то есть вычитка RX-буфера и запрос его размера), то уарт работает в течение от часа до восьми, при интенсивном опросе. А затем просто виснет - все события по отправке данных происходят без ошибок, а вот приема нет. Совсем. И переинит порта из приложения хоть и помогает, но сами понимаете, не является адекватным решением проблемы, особенно если речь идет о системах управления при интенсивном обмене.
Поэтому, как только я перешел к варианту, который вскользь описал Витальич, все стало на свои места - порты перестали вешаться.

Добавлено через 38 минут
...попробую примером, чтоле:

parser - будет у нас классом от Tthread, со своим Execute. так вот:
com - будет у нас классом порта, скажем, имеющего RxCount, как количество данных в RX-буфере. Имеет также процедуры отправки и приема буферов.

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
procedure parser.Execute;
var
  counter : integer;
  buf : string;
begin
  while(not Terminated) do
  begin
    com.SendStr('$01M'#13#10); // Send some command to remote, for example - send DSC command for ADAM/ICPCON modules
    counter = 5;
    while (com.RxCount=0) and (counter>0) do
    begin
      sleep(50); // wait untill we've got the answer
      dec(counter);
    end;
    if (counter>0) then // we've got the answer, parse
    begin
       buf := com.ReceiveString; // receive the incoming string here from COM-port
       if (pos('4517',buf)>0) then inc(some_ext_counter); // indicate that we've received the answer
    end;
  end;
end;
так вот такой поток загнется, но не сразу.
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
22.05.2015, 17:53
...повторю еще раз: нет никакого опроса в отдельном потоке и не надо, нет никакого смысла топик-стартеру его создавать, особенно с корявой реализацией слипами, оно уже есть в обертке готовое и реализовано как положено.

у меня GetTickCount от отправки до приема насчитывает 47, это значит потом это время надо будет сравнивать, если больше этого, значит ответа нет?
допустим 47 мс это слишком уж часто. Все зависит от объема данных, количеств устройств на шине. Но да, алгоритм такой.
0
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
27.05.2015, 08:43  [ТС]
Цитата Сообщение от raxp Посмотреть сообщение
По кнопкам лишь добавление команд в очередь последним в списке, динамический массив для этого подойдет.
Еще такой вопрос, 1. если я посылаю срочную команду в устройство, а у меня 20 адресов перед этим опрашивается через буфер, тогда получается запаздывание? или как?
2. А буфер лучше простой - массив или через класс и новый тип?

Кликните здесь для просмотра всего текста
raxp, Как к тебе в личку сообщение отправить, надо?
0
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
27.05.2015, 10:40
Цитата Сообщение от PierL Посмотреть сообщение
Еще такой вопрос, 1. если я посылаю срочную команду в устройство, а у меня 20 адресов перед этим опрашивается через буфер, тогда получается запаздывание? или как?
Создавайте не "процедуру опроса всех датчиков", а СПИСОК, который периодически пополняется например, из таймера. И есть функция, которая следит за списком и выполняет запросы из него удаленным устройствам.
Таким образом, опрос 10 удаленных модулей с периодом 500мс должен выглядеть как:
а) Загрузка/добавление 10 сообщений в список.
б) постоянная обработка списка, при его размере >0. То есть всех сообщений.

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

Таким образом, ваша задача с включением "срочной" команды сводится к ее вставке в список под нумером 1. "1", потому что сообщение "0" находится уже в обработчике и вот-вот удалится по завершению.
Список прекрасно себя чувствует в классе TStrings, или на базе его старших предков TCollection/TCollectionItem, если хотите сделать красиво.
2
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
27.05.2015, 15:39  [ТС]
Цитата Сообщение от Voland_ Посмотреть сообщение
а СПИСОК, который периодически пополняется например, из таймера
У меня такая ситуация получается: список команд, который загружается при включении, этот список постоянен, опрашивается по кругу (команда1, команда2, команда3, команда4). тут я решил послать команду, список стал таким
(командаСрочная, команда1, команда2, команда3, команда4) командаСрочная отправилась (соответственно ответ получил) и удалилась из списка и снова список стал (команда1, команда2, команда3, команда4). А таймер же нужен, чтобы избавиться от Sleep()? Что-то я вообще запутался! Извините, что такой баобаб.
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
27.05.2015, 17:32
...хоть таймер, хоть поток, без разницы, только в потоке отслеживайте нужное количество тиков или времени.
0
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
29.05.2015, 10:17  [ТС]
Цитата Сообщение от raxp Посмотреть сообщение
...хоть таймер, хоть поток, без разницы, только в потоке отслеживайте нужное количество тиков или времени.
Я тут приблизительно сделал, но у меня чего-то не получается чтобы после "Срочной команды1" шла "команда1", идет "команда2", потом восстанавливается.

{Программа "Пример буфера команд и опроса устройства". В Edit1 по очереди из
списка TSringList передаются команды по порядку. При нажатии кнопки1, в утройство
посылается команда, которая должа по приоритету выполнятся сразу.}
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
unit Unit1;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;
 
type
  TForm1 = class(TForm)
    Button1: TButton;
    Edit1: TEdit;
    Button2: TButton;
    Button3: TButton;
    Timer1: TTimer;
    Memo1: TMemo;
    Edit2: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button2Click(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
 
var
  Form1: TForm1;
  count1:word;//глобальная переменная для счета опроса
  StrList: TStringList;
implementation
 
{$R *.dfm}
 
procedure TForm1.FormCreate(Sender: TObject);
begin
  StrList:=TStringList.Create;
  StrList.Duplicates:=dupIgnore;
  //StrList.Sorted:=True;
  count1:=0;
  //Создается список, который стационарный, т.е. неизменный
  StrList.Add('команда 1');
  StrList.Add('команда 2');
  StrList.Add('команда 3');
  StrList.Add('команда 4');  
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
timer1.Enabled:=not timer1.Enabled;
count1:=0;
end;
 
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
StrList.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
 StrList.Insert(0, 'СрочнаяКоманда1');
 count1:=0;
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
  StrList.Insert(0, 'СрочнаяКоманда2');
  //count1:=0;
  
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Memo1.Lines.Add(StrList[count1]);//индикация переменных
  //Edit2.Text:=inttostr(count1);//индикация переменных
  Edit1.Text:=StrList[count1];
  count1:=count1+1;
  
   if StrList.Count>4 then
    begin StrList.Delete(0)  end;
   if count1>StrList.Count-1 then count1:=0;
end;
end.
Вложения
Тип файла: rar КомандыTStringList.rar (168.8 Кб, 6 просмотров)
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
29.05.2015, 12:07
...ловите:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
var StrList: TStringList;
    count1: integer = 0;
    maxc: integer = 4;
    perv: integer;
 
procedure TForm1.Button1Click(Sender: TObject);
begin
 timer1.Enabled:= false;
 
 count1:= 0;
 maxc:= maxc+1;
 StrList.Insert(0, 'срочная команда-' + inttostr(maxc-perv));
 memo1.Lines.Assign(strlist);
 
 
 timer1.Enabled:= true
end;
 
procedure TForm1.FormCreate(Sender: TObject);
begin
 StrList:=TStringList.Create;
 StrList.Duplicates:=dupIgnore;
 
  //Создается список, который стационарный, т.е. неизменный
  StrList.Add('команда 1');
  StrList.Add('команда 2');
  StrList.Add('команда 3');
  StrList.Add('команда 4');
  perv:= strlist.Count;
 
  memo1.Lines.Assign(strlist);
end;
 
 
 
procedure SelLine(Memo: TMemo; Index: integer);
begin
  with Memo do
    begin
      SelStart := Perform(EM_LINEINDEX, Index, 0);
      SelLength := Length(Lines[Index]);
      SetFocus;
    end;
end;
 
procedure TForm1.Timer1Timer(Sender: TObject);
begin
 SelLine(Memo1, count1);
 inc(count1);
 if count1>maxc-1 then count1:= 0;
 
 {
 выполняем извращения
 }
 
 // если выполнили 0-команду и кол-во команд > изначального списка
 if (count1=1) and (maxc>perv) then begin
  strlist.Delete(0);  memo1.Lines.Assign(strlist);
  maxc:= maxc-1;
  count1:= 0;
 end;
end;
1
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
29.05.2015, 13:46  [ТС]
Цитата Сообщение от raxp Посмотреть сообщение
{
*выполняем извращения
*}
По другому не выходит. Ты сможешь мне в личку свой mail сбросить?
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
29.05.2015, 15:02

Не по теме:

...приглашение в личку в тематических разделах запрещено.



Я исправил ваш код и теперь он выполняется верно, просто скопируйте 1 к 1.
1
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
22.06.2015, 12:12  [ТС]
raxp, Здравствуй!
Подскажи пожалуйста. У меня идет опрос через таймер (Прибор1, Прибор2).
Если в событие нет ответа от прибора, то снова посылается запрос, так до 3х раз.
В таймере отсылаю команду set_smh(p1,p2,p3,p4);

При опросе, если прибор1 и 2 отключены, в мемо выдает "нет ответа 1" , "нет ответа 2"

При опросе прибор 1 подключен, прибор 2 нет. при этой комбинации не получается правильно принимать данные. Принимает только от прибора 1, а "нет ответа 2" не пишет.
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
procedure TForm1.Timer1Timer(Sender: TObject);
var
S2:String; //строковая переменная для
p1,p2,p3,p4:integer;//переменные для формирования полного запроса при отправке
begin
  .....  
  set_smh(p1,p2,p3,p4); //Сформировавшийся запрос
   if (Otvet=0) and (zapros<3) then //в событии ответа Otvet:=1
  begin
  inc(zapros);  
  if zapros=3 then Form1.list.Lines.Add('----нет ответа от '+IntToStr(p1)+'----');
  exit;
  end else  if (Otvet=1)or (zapros=3) then
  begin
  transfer();  //процедура перехода на др.команду в списке команд
  end;
end;
0
 Аватар для PierL
4 / 4 / 0
Регистрация: 18.07.2013
Сообщений: 382
03.09.2015, 11:03  [ТС]
Voland_, raxp, такой вопрос к вам!
Модуль дискретный В/В (Modbus RTU) подключен к ПК. Если у меня возник обрыв линии, я как нибудь могу сгенирировать сигнал, чтобы у меня некоторые биты допустим сбросились при обрыве (или установились)?

Что можно придумать?
Пример: работающий двигатель, при обрыве линии связи с ПК сгенерировать СТОП, двигатель остановлен.

Спасибо!
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
03.09.2015, 11:22
...если двигатель контролируется по той же линии связи, то останов его при обрыве линии связи может осуществить только контроллер, управляющий им. ПК в этом никак не поможет.

Контролировать линию связи можно постоянными циклическими посылками или опросом. Если в течении определенного времени никаких пакетов в линии нет - признак обрыва линии связи. Пакеты/опрос может осуществлять сам ПК, как мастер-устройство.
1
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
03.09.2015, 15:53
Цитата Сообщение от raxp Посмотреть сообщение
Контролировать линию связи можно постоянными циклическими посылками или опросом. Если в течении определенного времени никаких пакетов в линии нет - признак обрыва линии связи. Пакеты/опрос может осуществлять сам ПК, как мастер-устройство.
Контроль-то он сможет осуществлять, а вот отправить команду "СТОП" - уже не сможет, а точнее, она не будет воспринята контроллером, т.к. связи уже нет.
Если бы стояла такая задача, и другого ничего нельзя было бы сделать (аппаратно, например, или электрически), я бы городил второе устройство- "мастер", висящий на другом конце ветки и контролирующий запросы первого мастера. То есть если запросы есть - все ок, мы висим и ничего не делаем. Как только пропадает связь, по таймауту запросов ждущий мастер отправит команды "СТОП" всем слейвам в оторванной ветке.
Да, это извращенно, да, кастомно выглядит. Но вполне может сойти как за решение.

Добавлено через 1 минуту
...и да, в этом случае мастер должен постоянно поллить ветку, чтобы таймаут второго мастера не происходил.
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
03.09.2015, 16:20
а вот отправить команду "СТОП" - уже не сможет, а точнее, она не будет воспринята контроллером, т.к. связи уже нет.
а у меня что написано в первом абзаце? Сам двигатель по себе не работает, есть силовой драйвер и его контроллер - подчиненное устройство в сети, есть мастер-устройство в сети - ПК.

Либо мы в уме представили разные структуры связи. У меня контроллер движка рядом с ним, линия связи между этим контроллером и мастером (ПК).

второго мастера не происходил.
нечего там делать второму мастеру в модбасе.
0
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
03.09.2015, 17:01
raxp,
Цитата Сообщение от PierL Посмотреть сообщение
Модуль дискретный В/В (Modbus RTU) подключен к ПК.
причем тут ПЛК?

Цитата Сообщение от raxp Посмотреть сообщение
нечего там делать второму мастеру в модбасе.
ну, так делается дублирование на контроллерном уровне... Один мастер "подглядывает" за вторым и получает актуальную инфу с модулей. Как только первый слейв "отпадает" по какой-то причине, второй подхватывает его работу.
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
03.09.2015, 17:17
ПЛК при том, что двигатель со своим контроллером. Полагаю линия связи одна (допустим RS-485) для ПК, модуля I/O и контроллера двигателя. Иначе я не вижу смысла во фразе:
Модуль дискретный В/В (Modbus RTU) подключен к ПК.
Если сам модуль только к ПК, то тогда для связи с контроллером движка нужен еще и второй конвертор и вторая линия связи. В общем нужно определиться какую топологию задумал TC.

Один мастер "подглядывает"
при этом он уже не мастер, он подчиненный с возможностью перевода в мастер.
0
 Аватар для Voland_
1983 / 1276 / 131
Регистрация: 04.01.2010
Сообщений: 4,607
03.09.2015, 17:59
ну я так понял, что структура такая: ПК ---> rs485 ---> IO --[DO]-> MotorControl.
То есть выходами I/O модуля мы "нажимаем" на кнопки СТАРТ/СТОП/ПЕРЕМОТАТЬ контроллера двигателя.

ЗЫ: PierL, вы не могли бы яснее расписать задачку вашу? а то мы сча тут напридумаем...
0
 Аватар для raxper
10237 / 6615 / 498
Регистрация: 28.12.2010
Сообщений: 21,154
Записей в блоге: 1
03.09.2015, 18:28
Если модуль ввода-вывода отвалился от общей сети, ничего извне ему не поможет остановить движок, кроме как контроллер движка сам будет контролировать постоянный приход пакетов. Об этом я написал в первом абзаце поста #114.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
03.09.2015, 18:28

Modbus TCP. Работа с регистратором РМТ-59
Всем добрый вечер, ребят опять надеюсь на вашу помощь, такая проблема : кто может поделится исходником Modbus TCP на Builder 6, либо...

Работа с Thermal Inkjet Printer G-Series по Modbus TCP
Помогите пожалуйста передо мной стоит срочная задача научиться работать с производственным принтером подключенным через Ethernet. C COM...

ADAM-4017+. 8-канальный модуль аналогового ввода с поддержкой протокола Modbus (подключение и работа)
Здравствуйте, столкнулся с необходимостью подключения оборудования через АЦП (сам я только начинаю изучать электротехнику, первая...

Работа по Modbus RTU с ПЛК Intellisys из своих приложений
Подскажите, плиз. Есть контроллер Intellisys с протоколом modbus rtu и девайс с modicon modbus protocol pi-mbus-300 rev.e . В описании на...

Работа с модулем ICP DAS I-8831 по Modbus RTU
Доброго времени суток, господа. Прошу Вас подкинуть и указать путь к заголовочным файлам на &quot;C&quot;, описывающим протокол передачи...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
120
Ответ Создать тему
Новые блоги и статьи
Саморегулирующийся социальный контракт для сервера cross-section.
Hrethgir 14.08.2026
С кодом конечно таких глубоких размышлений пока не было, впрочем я уже привык к алгоритмизации. Суть предмета записи: снова в диалоге с нейросетью (я взял пока себе ник для учётки админа - Rector). . . .
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru