Forráskód Böngészése

#1 Done. Add _docs, README.md, main.sql.

Danil138 2 hónapja
commit
2a9adcb973

+ 763 - 0
PG_sql_files/main.sql

@@ -0,0 +1,763 @@
+do $$
+begin
+
+/*
+Скрипт создания информационной базы данных
+Согласно технического задания https://git.hostfl.ru/VolovikovAlex/Study2025
+Редакция 2025-02-12
+Edit by valex
+*/
+
+
+/*
+ 1. Удаляем старые элементы
+ ======================================
+ */
+
+raise notice 'Запускаем создание новой структуры базы данных meteo'; 
+begin
+
+	-- Связи
+	alter table if exists public.measurment_input_params
+	drop constraint if exists measurment_type_id_fk;
+
+	alter table if exists public.employees
+	drop constraint if exists military_rank_id_fk;
+
+	alter table if exists public.measurment_baths
+	drop constraint if exists measurment_input_param_id_fk;
+
+	alter table if exists public.measurment_baths
+	drop constraint if exists emploee_id_fk;
+
+	
+	-- Таблицы
+	drop table if exists public.measurment_input_params;
+	drop table if exists public.measurment_baths;
+	drop table if exists public.employees;
+	drop table if exists public.measurment_types;
+	drop table if exists public.military_ranks;
+	drop table if exists public.measurment_settings;
+
+	-- Нумераторы
+	drop sequence if exists public.measurment_input_params_seq;
+	drop sequence if exists public.measurment_baths_seq;
+	drop sequence if exists public.employees_seq;
+	drop sequence if exists public.military_ranks_seq;
+	drop sequence if exists public.measurment_types_seq;
+end;
+
+raise notice 'Удаление старых данных выполнено успешно';
+
+/*
+ 2. Добавляем структуры данных 
+ ================================================
+ */
+
+-- Справочник должностей
+create table military_ranks
+(
+	id integer primary key not null,
+	description character varying(255)
+);
+
+insert into military_ranks(id, description)
+values(1,'Рядовой'),(2,'Лейтенант');
+
+create sequence military_ranks_seq start 3;
+
+alter table military_ranks alter column id set default nextval('public.military_ranks_seq');
+
+-- Пользователя
+create table employees
+(
+    id integer primary key not null,
+	name text,
+	birthday timestamp ,
+	military_rank_id integer
+);
+
+insert into employees(id, name, birthday,military_rank_id )  
+values(1, 'Воловиков Александр Сергеевич','1978-06-24', 2);
+
+create sequence employees_seq start 2;
+
+alter table employees alter column id set default nextval('public.employees_seq');
+
+
+-- Устройства для измерения
+create table measurment_types
+(
+   id integer primary key not null,
+   short_name  character varying(50),
+   description text 
+);
+
+insert into measurment_types(id, short_name, description)
+values(1, 'ДМК', 'Десантный метео комплекс'),
+(2,'ВР','Ветровое ружье');
+
+create sequence measurment_types_seq start 3;
+
+alter table measurment_types alter column id set default nextval('public.measurment_types_seq');
+
+-- Таблица с параметрами
+create table measurment_input_params 
+(
+    id integer primary key not null,
+	measurment_type_id integer not null,
+	height numeric(8,2) default 0,
+	temperature numeric(8,2) default 0,
+	pressure numeric(8,2) default 0,
+	wind_direction numeric(8,2) default 0,
+	wind_speed numeric(8,2) default 0,
+	bullet_demolition_range numeric(8,2) default 0
+);
+
+insert into measurment_input_params(id, measurment_type_id, height, temperature, pressure, wind_direction,wind_speed )
+values(1, 1, 100,12,34,0.2,45);
+
+create sequence measurment_input_params_seq start 2;
+
+alter table measurment_input_params alter column id set default nextval('public.measurment_input_params_seq');
+
+-- Таблица с историей
+create table measurment_baths
+(
+		id integer primary key not null,
+		emploee_id integer not null,
+		measurment_input_param_id integer not null,
+		started timestamp default now()
+);
+
+
+insert into measurment_baths(id, emploee_id, measurment_input_param_id)
+values(1, 1, 1);
+
+create sequence measurment_baths_seq start 2;
+
+alter table measurment_baths alter column id set default nextval('public.measurment_baths_seq');
+
+-- Таблица с настройками
+create table measurment_settings
+(
+	key character varying(100) primary key not null,
+	value  character varying(255) ,
+	description text
+);
+
+
+insert into measurment_settings(key, value, description)
+values('min_temperature', '-10', 'Минимальное значение температуры'), 
+('max_temperature', '50', 'Максимальное значение температуры'),
+('min_pressure','500','Минимальное значение давления'),
+('max_pressure','900','Максимальное значение давления'),
+('min_wind_direction','0','Минимальное значение направления ветра'),
+('max_wind_direction','59','Максимальное значение направления ветра'),
+('calc_table_temperature','15.9','Табличное значение температуры'),
+('calc_table_pressure','750','Табличное значение наземного давления'),
+('min_height','0','Минимальная высота'),
+('max_height','400','Максимальная высота');
+
+
+raise notice 'Создание общих справочников и наполнение выполнено успешно'; 
+
+/*
+ 3. Подготовка расчетных структур
+ ==========================================
+ */
+
+drop table if exists calc_temperatures_correction;
+create table calc_temperatures_correction
+(
+   temperature numeric(8,2) primary key,
+   correction numeric(8,2)
+);
+
+insert into public.calc_temperatures_correction(temperature, correction)
+Values(0, 0.5),(5, 0.5),(10, 1), (20,1), (25, 2), (30, 3.5), (40, 4.5);
+
+drop type  if exists interpolation_type;
+create type interpolation_type as
+(
+	x0 numeric(8,2),
+	x1 numeric(8,2),
+	y0 numeric(8,2),
+	y1 numeric(8,2)
+);
+
+drop type if exists input_params cascade;
+create type input_params as
+(
+	height numeric(8,2),
+	temperature numeric(8,2),
+	pressure numeric(8,2),
+	wind_direction numeric(8,2),
+	wind_speed numeric(8,2),
+	bullet_demolition_range numeric(8,2)
+);
+
+raise notice 'Расчетные структуры сформированы';
+
+/*
+ 4. Создание связей
+ ==========================================
+ */
+
+begin 
+	
+	alter table public.measurment_baths
+	add constraint emploee_id_fk 
+	foreign key (emploee_id)
+	references public.employees (id);
+	
+	alter table public.measurment_baths
+	add constraint measurment_input_param_id_fk 
+	foreign key(measurment_input_param_id)
+	references public.measurment_input_params(id);
+	
+	alter table public.measurment_input_params
+	add constraint measurment_type_id_fk
+	foreign key(measurment_type_id)
+	references public.measurment_types (id);
+	
+	alter table public.employees
+	add constraint military_rank_id_fk
+	foreign key(military_rank_id)
+	references public.military_ranks (id);
+
+end;
+
+raise notice 'Связи сформированы';
+
+/*
+ 4. Создает расчетные и вспомогательные функции
+ ==========================================
+ */
+
+-- Функция для расчета отклонения приземной виртуальной температуры
+drop function if exists   public.fn_calc_header_temperature;
+create function public.fn_calc_header_temperature(
+	par_temperature numeric(8,2))
+    returns numeric(8,2)
+    language 'plpgsql'
+as $BODY$
+declare
+	default_temperature numeric(8,2) default 15.9;
+	default_temperature_key character varying default 'calc_table_temperature' ;
+	virtual_temperature numeric(8,2) default 0;
+	deltaTv numeric(8,2) default 0;
+	var_result numeric(8,2) default 0;
+begin	
+
+	raise notice 'Расчет отклонения приземной виртуальной температуры по температуре %', par_temperature;
+
+	-- Определим табличное значение температуры
+	Select coalesce(value::numeric(8,2), default_temperature) 
+	from public.measurment_settings 
+	into virtual_temperature
+	where 
+		key = default_temperature_key;
+
+    -- Вирутальная поправка
+	deltaTv := par_temperature + 
+		public.fn_calc_temperature_interpolation(par_temperature => par_temperature);
+		
+	-- Отклонение приземной виртуальной температуры
+	var_result := deltaTv - virtual_temperature;
+	
+	return var_result;
+end;
+$BODY$;
+
+
+-- Функция для формирования даты в специальном формате
+drop function if exists public.fn_calc_header_period;
+create function public.fn_calc_header_period(
+	par_period timestamp with time zone)
+    RETURNS text
+    LANGUAGE 'sql'
+    COST 100
+    VOLATILE PARALLEL UNSAFE
+
+RETURN ((((CASE WHEN (EXTRACT(day FROM par_period) < (10)::numeric) THEN '0'::text ELSE ''::text END || (EXTRACT(day FROM par_period))::text) || CASE WHEN (EXTRACT(hour FROM par_period) < (10)::numeric) THEN '0'::text ELSE ''::text END) || (EXTRACT(hour FROM par_period))::text) || "left"(CASE WHEN (EXTRACT(minute FROM par_period) < (10)::numeric) THEN '0'::text ELSE (EXTRACT(minute FROM par_period))::text END, 1));
+
+
+-- Функция для расчета отклонения наземного давления
+drop function if exists public.fn_calc_header_pressure;
+create function public.fn_calc_header_pressure
+(
+	par_pressure numeric(8,2))
+	returns numeric(8,2)
+	language 'plpgsql'
+as $body$
+declare
+	default_pressure numeric(8,2) default 750;
+	table_pressure numeric(8,2) default null;
+	default_pressure_key character varying default 'calc_table_pressure' ;
+begin
+
+	raise notice 'Расчет отклонения наземного давления для %', par_pressure;
+	
+	-- Определяем граничное табличное значение
+	if not exists (select 1 from public.measurment_settings where key = default_pressure_key ) then
+	Begin
+		table_pressure :=  default_pressure;
+	end;
+	else
+	begin
+		select value::numeric(18,2) 
+		into table_pressure
+		from  public.measurment_settings where key = default_pressure_key;
+	end;
+	end if;
+
+	
+	-- Результат
+	return par_pressure - coalesce(table_pressure,table_pressure) ;
+
+end;
+$body$;
+
+
+-- Функция для проверки входных параметров
+drop function if exists public.fn_check_input_params(numeric(8,2), numeric(8,2),   numeric(8,2), numeric(8,2), numeric(8,2), numeric(8,2));
+create function public.fn_check_input_params(
+	par_height numeric(8,2),
+	par_temperature numeric(8,2),
+	par_pressure numeric(8,2),
+	par_wind_direction numeric(8,2),
+	par_wind_speed numeric(8,2),
+	par_bullet_demolition_range numeric(8,2)
+)
+returns public.input_params
+language 'plpgsql'
+as $body$
+declare
+	var_result public.input_params;
+begin
+
+	
+	-- Температура
+	if not exists (
+		select 1 from (
+				select 
+						coalesce(min_temperature , '0')::numeric(8,2) as min_temperature, 
+						coalesce(max_temperature, '0')::numeric(8,2) as max_temperature
+				from 
+				(select 1 ) as t
+					cross join
+					( select value as  min_temperature from public.measurment_settings where key = 'min_temperature' ) as t1
+					cross join 
+					( select value as  max_temperature from public.measurment_settings where key = 'max_temperature' ) as t2
+				) as t	
+			where
+				par_temperature between min_temperature and max_temperature
+			) then
+
+			raise exception 'Температура % не укладывает в диаппазон!', par_temperature;
+	end if;
+
+	var_result.temperature = par_temperature;
+
+	
+	-- Давление
+	if not exists (
+		select 1 from (
+			select 
+					coalesce(min_pressure , '0')::numeric(8,2) as min_pressure, 
+					coalesce(max_pressure, '0')::numeric(8,2) as max_pressure
+			from 
+			(select 1 ) as t
+				cross join
+				( select value as  min_pressure from public.measurment_settings where key = 'min_pressure' ) as t1
+				cross join 
+				( select value as  max_pressure from public.measurment_settings where key = 'max_pressure' ) as t2
+			) as t	
+			where
+				par_pressure between min_pressure and max_pressure
+				) then
+
+			raise exception 'Давление % не укладывает в диаппазон!', par_pressure;
+	end if;
+
+	var_result.pressure = par_pressure;			
+
+		-- Высота
+		if not exists (
+			select 1 from (
+				select 
+						coalesce(min_height , '0')::numeric(8,2) as min_height, 
+						coalesce(max_height, '0')::numeric(8,2) as  max_height
+				from 
+				(select 1 ) as t
+					cross join
+					( select value as  min_height from public.measurment_settings where key = 'min_height' ) as t1
+					cross join 
+					( select value as  max_height from public.measurment_settings where key = 'max_height' ) as t2
+				) as t	
+				where
+				par_height between min_height and max_height
+				) then
+	
+				raise exception 'Высота % не укладывает в диаппазон!', par_height;
+		end if;
+
+		var_result.height = par_height;
+		
+		-- Напрвление ветра
+		if not exists (
+			select 1 from (	
+				select 
+						coalesce(min_wind_direction , '0')::numeric(8,2) as min_wind_direction, 
+						coalesce(max_wind_direction, '0')::numeric(8,2) as max_wind_direction
+				from 
+				(select 1 ) as t
+					cross join
+					( select value as  min_wind_direction from public.measurment_settings where key = 'min_wind_direction' ) as t1
+					cross join 
+					( select value as  max_wind_direction from public.measurment_settings where key = 'max_wind_direction' ) as t2
+			)
+				where
+				par_wind_direction between min_wind_direction and max_wind_direction
+			) then
+
+			raise exception 'Направление ветра % не укладывает в диаппазон!', par_wind_direction;
+	end if;
+			
+	var_result.wind_direction = par_wind_direction;	
+	var_result.wind_speed = par_wind_speed;
+
+	return var_result;
+	
+end;
+$body$;
+
+-- Функция для проверки параметров
+drop function if exists public.fn_check_input_params(input_params);
+create function public.fn_check_input_params(
+	par_param input_params
+)
+returns public.input_params
+language 'plpgsql'
+as $body$
+declare
+	var_result input_params;
+begin
+
+	var_result := fn_check_input_params(
+		par_param.height, par_param.temperature, par_param.pressure, par_param.wind_direction,
+		par_param.wind_speed, par_param.bullet_demolition_range
+	);
+
+	return var_result;
+	
+end ;
+$body$;
+		
+-- Функция для расчета интерполяции
+drop function if exists public.fn_calc_temperature_interpolation;
+create function public.fn_calc_temperature_interpolation(
+		par_temperature numeric(8,2))
+		returns numeric
+		language 'plpgsql'
+as $body$
+	-- Расчет интерполяции 
+	declare 
+			var_interpolation interpolation_type;
+	        var_result numeric(8,2) default 0;
+	        var_min_temparure numeric(8,2) default 0;
+	        var_max_temperature numeric(8,2) default 0;
+	        var_denominator numeric(8,2) default 0;
+	begin
+
+  				raise notice 'Расчет интерполяции для температуры %', par_temperature;
+
+                -- Проверим, возможно температура совпадает со значением в справочнике
+                if exists (select 1 from public.calc_temperatures_correction where temperature = par_temperature ) then
+                begin
+                        select correction 
+                        into  var_result 
+                        from  public.calc_temperatures_correction
+                        where 
+                                temperature = par_temperature;
+                end;
+                else    
+                begin
+                        -- Получим диапазон в котором работают поправки
+                        select min(temperature), max(temperature) 
+                        into var_min_temparure, var_max_temperature
+                        from public.calc_temperatures_correction;
+
+                        if par_temperature < var_min_temparure or   
+                           par_temperature > var_max_temperature then
+
+                                raise exception 'Некорректно передан параметр! Невозможно рассчитать поправку. Значение должно укладываться в диаппазон: %, %',
+                                        var_min_temparure, var_max_temperature;
+                        end if;   
+
+                        -- Получим граничные параметры
+
+                        select x0, y0, x1, y1 
+						 into var_interpolation.x0, var_interpolation.y0, var_interpolation.x1, var_interpolation.y1
+                        from
+                        (
+                                select t1.temperature as x0, t1.correction as y0
+                                from public.calc_temperatures_correction as t1
+                                where t1.temperature <= par_temperature
+                                order by t1.temperature desc
+                                limit 1
+                        ) as leftPart
+                        cross join
+                        (
+                                select t1.temperature as x1, t1.correction as y1
+                                from public.calc_temperatures_correction as t1
+                                where t1.temperature >= par_temperature
+                                order by t1.temperature 
+                                limit 1
+                        ) as rightPart;
+
+                        raise notice 'Граничные значения %', var_interpolation;
+
+                        -- Расчет поправки
+                        var_denominator := var_interpolation.x1 - var_interpolation.x0;
+                        if var_denominator = 0.0 then
+
+                                raise exception 'Деление на нуль. Возможно, некорректные данные в таблице с поправками!';
+
+                        end if;
+
+						var_result := (par_temperature - var_interpolation.x0) * (var_interpolation.y1 - var_interpolation.y0) / var_denominator + var_interpolation.y0;
+
+                end;
+                end if;
+
+				return var_result;
+
+end;
+$body$;
+
+-- Функция для генерации случайной даты
+drop function if exists fn_get_random_timestamp;
+create function fn_get_random_timestamp(
+	par_min_value timestamp,
+	par_max_value timestamp)
+returns timestamp
+language 'plpgsql'
+as $body$
+begin
+	 return random() * (par_max_value - par_min_value) + par_min_value;
+end;
+$body$;
+
+-- Функция для генерации случайного целого числа из диаппазона
+drop function if exists fn_get_randon_integer;
+create function fn_get_randon_integer(
+	par_min_value integer,
+	par_max_value integer
+	)
+returns integer
+language 'plpgsql'
+as $body$
+begin
+	return floor((par_max_value + 1 - par_min_value)*random())::integer + par_min_value;
+end;
+$body$;
+
+-- Функция для гнерации случайного текста
+drop function if exists fn_get_random_text;
+create function fn_get_random_text(
+   par_length int,
+   par_list_of_chars text DEFAULT 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюяABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789'
+)
+returns text
+language 'plpgsql'
+as $body$
+declare
+    var_len_of_list integer default length(par_list_of_chars);
+    var_position integer;
+    var_result text = '';
+	var_random_number integer;
+	var_max_value integer;
+	var_min_value integer;
+begin
+
+	var_min_value := 10;
+	var_max_value := 50;
+	
+    for var_position in 1 .. par_length loop
+        -- добавляем к строке случайный символ
+	    var_random_number := fn_get_randon_integer(var_min_value, var_max_value );
+        var_result := var_result || substr(par_list_of_chars,  var_random_number ,1);
+    end loop;
+	
+    return var_result;
+	
+end;
+$body$;
+
+
+-- Функция для расчета метео приближенный
+drop function if exists fn_calc_header_meteo_avg;
+create function fn_calc_header_meteo_avg(
+	par_params input_params
+)
+returns text
+language 'plpgsql'
+as $body$
+declare
+	var_result text;
+	var_params input_params;
+begin
+
+	-- Проверяю аргументы
+	var_params := public.fn_check_input_params(par_params);
+	
+	select
+		-- Дата
+		public.fn_calc_header_period(now()) ||
+		--Высота расположения метеопоста над уровнем моря.
+	    lpad( 340::text, 4, '0' ) ||
+		-- Отклонение наземного давления атмосферы
+		lpad(
+				case when coalesce(var_params.pressure,0) < 0 then
+					'5' 
+				else ''
+				end ||
+				lpad ( abs(( coalesce(var_params.pressure, 0) )::int)::text,2,'0')
+			, 3, '0') as "БББ",
+		-- Отклонение приземной виртуальной температуры	
+		lpad( 
+				case when coalesce( var_params.temperature, 0) < 0 then
+					'5'
+				else
+					''
+				end ||
+				( coalesce(var_params.temperature,0)::int)::text
+			, 2,'0')
+		into 	var_result;
+	return 	var_result;
+
+end;
+$body$;
+
+
+
+raise notice 'Структура сформирована успешно';
+end $$;
+
+
+-- Проверка расчета
+do $$
+declare
+	var_pressure_value numeric(8,2) default 0;
+	var_temperature_value numeric(8,2) default 0;
+	
+	var_period text;
+	var_pressure text;
+	var_height text;
+	var_temperature text;
+begin
+
+	var_pressure_value :=  public.fn_calc_header_pressure(743);
+	var_temperature_value := public.fn_calc_header_temperature(23);
+	
+	select
+		-- Дата
+		public.fn_calc_header_period(now()) as "ДДЧЧМ",
+		--Высота расположения метеопоста над уровнем моря.
+	    lpad( 340::text, 4, '0' ) as "ВВВВ",
+		-- Отклонение наземного давления атмосферы
+		lpad(
+				case when var_pressure_value < 0 then
+					'5' 
+				else ''
+				end ||
+				lpad ( abs((var_pressure_value)::int)::text,2,'0')
+			, 3, '0') as "БББ",
+		-- Отклонение приземной виртуальной температуры	
+		lpad( 
+				case when var_temperature_value < 0 then
+					'5'
+				else
+					''
+				end ||
+				(var_temperature_value::int)::text
+			, 2,'0') as "TT"
+		into
+			var_period, var_height, var_pressure, var_temperature;
+
+		raise notice '==============================';
+		raise notice 'Пример расчета метео приближенный';
+		raise notice ' ДДЧЧМ %, ВВВВ %,  БББ % , TT %', 	var_period, var_height, var_pressure, var_temperature;
+		
+end $$;
+
+-- Генерация тестовых данных
+
+
+do $$
+declare
+	 var_position integer;
+	 var_emploee_ids integer[];
+	 var_emploee_quantity integer default 5;
+	 var_min_rank integer;
+	 var_max_rank integer;
+	 var_emploee_id integer;
+	 var_current_emploee_id integer;
+	 var_index integer;
+	 var_measure_type_id integer;
+	 var_measure_input_data_id integer;
+begin
+
+	-- Определяем макс дипазон по должностям
+	select min(id), max(id) 
+	into var_min_rank,var_max_rank
+	from public.military_ranks;
+
+	
+	-- Формируем список пользователей
+	for var_position in 1 .. var_emploee_quantity loop
+		insert into public.employees(name, birthday, military_rank_id )
+		select 
+			fn_get_random_text(25),								-- name
+			fn_get_random_timestamp('1978-01-01','2000-01-01'), 				-- birthday
+			fn_get_randon_integer(var_min_rank, var_max_rank)  -- military_rank_id
+			;
+		select id into var_emploee_id from public.employees order by id desc limit 1;	
+		var_emploee_ids := var_emploee_ids || var_emploee_id;
+	end loop;
+
+	raise notice 'Сформированы тестовые пользователи  %', var_emploee_ids;
+
+	-- Формируем для каждого по 100 измерений
+	foreach var_current_emploee_id in ARRAY var_emploee_ids LOOP
+		for var_index in 1 .. 100 loop
+			var_measure_type_id := fn_get_randon_integer(1,2);
+			
+			insert into public.measurment_input_params(measurment_type_id, height, temperature, pressure, wind_direction, wind_speed)
+			select
+				var_measure_type_id,
+				fn_get_randon_integer(0,600)::numeric(8,2), -- height
+				fn_get_randon_integer(0, 50)::numeric(8,2), -- temperature
+				fn_get_randon_integer(500, 850)::numeric(8,2), -- pressure
+				fn_get_randon_integer(0,59)::numeric(8,2), -- ind_direction
+				fn_get_randon_integer(0,59)::numeric(8,2)	-- wind_speed
+				;
+
+			select id into var_measure_input_data_id from 	measurment_input_params order by id desc limit 1;
+				
+			insert into public.measurment_baths( emploee_id, measurment_input_param_id, started)
+			select
+				var_current_emploee_id,
+				var_measure_input_data_id,
+				fn_get_random_timestamp('2025-02-01 00:00', '2025-02-05 00:00')
+			;	
+		end loop;
+	
+	end loop;
+
+	raise notice 'Набор тестовых данных сформирован успешно';
+	
+end $$;
+

+ 2 - 0
REDME.md

@@ -0,0 +1,2 @@
+* Документация: [docs][/_Docs]
+* Основной файл: [main.sql][/PG_sql_files/main.sql]

+ 65 - 0
_Docs/AlgoritmBp.md

@@ -0,0 +1,65 @@
+\usepackage{amsmath}
+
+# Алгоритм расчета 
+Тип оборудования: **Ветровое ружье (`ВР`)**
+
+1. `Отклонение наземного давления атмосферы` $ΔH_{0}$ (мм рт. ст.) и отклонение наземной виртуальной температуры воздуха (град) на уровне метеорологического поста. Отклонение наземного давления атмосферы $ΔH_{0}$, записываемое в бюллетень, определяется как разность измеренного давления Н0 и табличного значения наземного давления, равного 750 мм рт. ст., т. е. $ΔН_{0} = Н_{0} – 750$.
+Если измеренное давление будет больше 750 мм рт. ст., то отклонение ΔН0 имеет знак «плюс», если меньше – знак «минус».
+
+2. `Отклонение приземной виртуальной температуры` ,  записываемое в бюллетень, определяется как разность между приземной виртуальной температурой, измеренной метеорологическим постом, 
+и ее табличным значением, равным **+15,9°С**, т.е.
+
+>
+> $ΔT_{0}^{мп}$ = T0  – 15,9
+>
+>
+>Если приземная  виртуальная температура будет больше +15,9°С, 
+> от отклонение  имеет знак **«плюс»**, если меньше – знак **«минус»**.
+> Приземная виртуальная температура τ0 вычисляется по формуле:
+> 
+> T0 = t0  + $ΔТ_{V}$
+> 
+> где:
+> - t0 - измеренная приземная температура воздуха. Берется из ячейки > `температура`
+> - $ΔТ_{V}$ - виртуальная поправка, определяемая по таблице 1.
+
+**Таблица 1**
+|  $t_{0}$ | Ниже 0 | 0 - 5 | 10 - 15 | 20 | 25 | 30 | 40 |
+|----------|--------|-------|---------|----|----|----|----|
+| $ΔТ_{V}$ | 0      | 0.5   | 1       | 1.5  | 2  | 3.5 | 4.5 |
+
+3. `Средние отклонения температуры воздуха`  $Δt_{Y}$, направление и скорость среднего ветра определяются для стандартных слоев до высоты **4 км**. Среднее отклонение температуры воздуха Y в пределах стандартных высот бюллетеня определяется по **таблице 2**.
+
+**Таблица 2**
+![](./_Images/15.png)
+
+> Входами в таблицы являются стандартные высоты бюллетеня Y 
+> и отклонение приземной виртуальной температуры.
+> 
+> **Среднее отклонение температуры** при отрицательных значениях  указано > в числителе дроби, при положительных – в знаменателе дроби.
+> 
+> Для отклонений $Δt$ между **10 и 20, 20 и 30 и т. д.** среднее отклонение температуры Y определяется путем сложения величины отклонений из столбцов, соответствующих целому числу десятков и числу единиц.
+
+4. `Скорость среднего ветра` $W_{у}$ для всех стандартных высот бюллетеня определяется по **таблице 3**.
+
+**Таблица 3**
+![](./_Images/16.png)
+
+> Входами в таблицы являются стандартные высоты  Y и дальности сноса ветровых пуль Дг (м).
+> При дальности сноса ветровых пуль `Дг  < 40 м` для всех стандартных высот Y скорость среднего ветра принимается равной нулю. В этом случае в бюллетене для всех стандартных высот Y места цифр, отведенные для направления и скорости ветра, заполняются нулями.
+
+
+5. `Направление среднего ветра` $α_{WY}$ для всех стандартных высот определяется по формуле: 
+$α_{WY} = α_{V0} + Δα_{WY}$
+
+где<br>
+$α_{V0}$ – направление приземного ветра;<br>
+$Δα_{WY}$ – приращение среднего ветра относительно направления приземного ветра, определяемое по **таблице 3**
+
+> Для высот более **4000** метров данные просто копируются из значений на 4000 метров.<br>
+> Для высот менее **200** метров данные просто копируются из значений на 200 метров..
+
+
+
+
+

+ 89 - 0
_Docs/AlgoritmDmk.md

@@ -0,0 +1,89 @@
+\usepackage{amsmath}
+
+# Алгоритм расчета 
+Тип оборудования: **Десатный метео комплект (`ДМК`)**
+
+1. `Отклонение наземного давления атмосферы` $ΔH_{0}$ (мм рт. ст.) и отклонение наземной виртуальной температуры воздуха (град) на уровне метеорологического поста. Отклонение наземного давления атмосферы $ΔH_{0}$, записываемое в бюллетень, определяется как разность измеренного давления Н0 и табличного значения наземного давления, равного 750 мм рт. ст., т. е. $ΔН_{0} = Н_{0} – 750$.
+Если измеренное давление будет больше 750 мм рт. ст., то отклонение ΔН0 имеет знак «плюс», если меньше – знак «минус».
+
+2. `Отклонение приземной виртуальной температуры` ,  записываемое в бюллетень, определяется как разность между приземной виртуальной температурой, измеренной метеорологическим постом, 
+и ее табличным значением, равным **+15,9°С**, т.е.
+
+>
+> $ΔT_{0}^{мп}$ = T0  – 15,9
+>
+>
+>Если приземная  виртуальная температура будет больше +15,9°С, 
+> от отклонение  имеет знак **«плюс»**, если меньше – знак **«минус»**.
+> Приземная виртуальная температура τ0 вычисляется по формуле:
+> 
+> T0 = t0  + $ΔТ_{V}$
+> 
+> где:
+> - t0 - измеренная приземная температура воздуха. Берется из ячейки > `температура`
+> - $ΔТ_{V}$ - виртуальная поправка, определяемая по таблице 1.
+
+**Таблица 1**
+|  $t_{0}$ | Ниже 0 | 0 - 5 | 10 - 15 | 20 | 25 | 30 | 40 |
+|----------|--------|-------|---------|----|----|----|----|
+| $ΔТ_{V}$ | 0      | 0.5   | 1       | 1.5  | 2  | 3.5 | 4.5 |
+
+3. `Средние отклонения температуры воздуха`  $Δt_{Y}$, направление и скорость среднего ветра определяются для стандартных слоев до высоты **4 км**. Среднее отклонение температуры воздуха Y в пределах стандартных высот бюллетеня определяется по таблице 2.
+
+**Таблица 2**
+![](./_Images/13.png)
+
+> Входами в таблицы являются стандартные высоты бюллетеня Y 
+> и отклонение приземной виртуальной температуры.
+> 
+> **Среднее отклонение температуры** при отрицательных значениях  указано > в числителе дроби, при положительных – в знаменателе дроби.
+> 
+> Для отклонений $Δt$ между **10 и 20, 20 и 30 и т. д.** среднее отклонение температуры Y определяется путем сложения величины отклонений из столбцов, соответствующих целому числу десятков и числу единиц.
+
+
+#### Пример расчета
+- **Исходные данные**: измеренная приземная температура воздуха $t0$ = +3
+- **Решение**:
+
+T0 = t0  + $ΔТ_{V}$<br>
+T0 = +3 + 0,3 = + 3,3
+
+$ΔT_{0}^{мп}$ = T0  – 15,9<br>
+$ΔT_{0}^{мп}$ = +3,3 - 15,9 =  -12,6 = -13 (округлили)
+
+По **таблице 2**  начинаем считать отклонение для каждой стандартной высоты. 
+
+**200 метров**: `-13` раскладываем на два числа: `-10` и `-3`, в таблице находим соответствующие им значения для 200 метров и складываем их. 
+Для `-10` значение **-9**, для `-3` значение **-3**.  
+
+Отклонение температуры для стандартной высоты 200 метров будет равно:        
+`-9 + (-3) = -12`  
+ 
+Далее из-за того, что полученное значение отрицательное к его модулю необходимо прибавить `50 -> |-12| + 50 = 62`. 
+Число **62** необходимо записать в столбец отклонения температуры для высоты 200 метров (**02**). 
+
+
+4. `Скорость среднего ветра` $W_{у}$ для всех стандартных высот бюллетеня определяется по **таблице 3**
+
+**Таблица 3**
+![](./_Images/14.png)
+
+> Входами в таблицы являются стандартные высоты 
+Y и скорости приземного ветра $V_{0}$.
+> При скорости приземного ветра $V_{0}$  < 3 м/с для всех стандартных высот Y скорость среднего ветра принимается равной нулю. В этом случае в бюллетене для всех стандартных высот Y места цифр, отведенные для направления и скорости ветра, заполняются нулями. Среднее значение скорости округляют до 1 м/с.
+
+
+5. `Направление среднего ветра` $α_{WY}$ для всех стандартных высот определяется по формуле: 
+$α_{WY} = α_{V0} + Δα_{WY}$
+
+где<br>
+$α_{V0}$ – направление приземного ветра;<br>
+$Δα_{WY}$ – приращение среднего ветра относительно направления приземного ветра, определяемое по **таблице 3**
+
+> Для высот более **4000** метров данные просто копируются из значений на 4000 метров.<br>
+> Для высот менее **200** метров данные просто копируются из значений на 200 метров.
+
+
+
+
+

+ 163 - 0
_Docs/TechnicalTask.md

@@ -0,0 +1,163 @@
+\usepackage{amsmath}
+
+# Техническое задание
+
+> В рамках `Гособоронзаказа` необходимо разработать программно-аппаратный комплекс (`ПАК`) для автоматизации расчетных задач поддержки артиллерии. Для проведения расчетно-огневых задач требуется регулярное измерение метрологических характеристик на различных участках фронта. 
+
+В качестве входных данных используются:
+
+- Тип оборудования
+- Высота ДМК
+- Температура воздуха на высоте метеопоста
+- Давление атмосферы на высоте метеопоста
+- Направление ветра в приземном слое воздуха
+- Скорость ветра в приземном слое воздуха
+
+Результат расчета оформляется в виде таблицы:
+
+- Высота (`H`)
+- Среднее отклонение температуры (`TT`)
+- Дирекционные угол направление ветра (`HH`)
+- Скорость среднего ветка (`СС`)
+
+Интерфейс:
+
+|![](./_Images/1.png)
+
+## Описание
+Система должна состоять из двух частей
+1. Метеобюллетень
+2. Журнал измерений
+
+На первой вкладке `метеобюллетень` производится текущий расчет, а так же отображаются текущие `сохраненные` результаты расчета.
+На второй вкладке `журнал измерений` отображается история измерений с набором дополнительного функционала.
+
+1. Переключатель выбора режима расчета приближенного метеобюллетеня предназначен для выбора **оборудования** для проведения измерений. Есть два варианта составления приближенного метеобюллетеня: 
+* С использованием десантный метео комплект (`ДМК`)
+* С использованием ветрового ружья (`ВР`)
+
+Отличаются эти варианты исходными данными для составления приближенного метеобюллетеня и математическими расчтами.
+
+![](./_Images/2.png)
+
+При выборе `ДМК` в области исходных данным последняя строка `Скорость ветра`. 
+
+Пример:
+
+![](./_Images/3.png)
+
+При выборе `ВР` в области исходных данным последняя строка `Дальность сноса пуль`.
+
+Пример
+
+![](./_Images/4.png)
+
+
+2. Ячейки исходных данных данных по умолчанию должны содержать следующие значения:
+
+| Параметр     | Значение по умолчанию   |
+|--------------|-------------------------|
+| Высота метеопоста | **100**       |
+| Температура   | **15** |
+| Давление    | **750** |
+| Направление ветра | **00** |
+| Скорость ветра | **0** |
+| Дальность сноса пуль | **0** |
+
+
+3. `Высота метеопоста`. Значения - целые числа, могут быть отрицательными и положительными и нулем, указывается в метрах над уровнем моря
+
+4. `Температура`. Значения - дробные числа с одним знаком после запятой, могут быть отрицательными и положительными, нулем Минимальное значение `-58`, максимальное  `58`, указывается в цельсиях
+
+5. `Давления`. Значения -  целые числа, могут быть только положительными. Минимальное значение `500`, максимальное  `900`, указывается в мм рт ст
+
+6. `Направление ветра`. Значение всегда стоит из двух разрядов. Минимальное значение `0`,` максимальное значение `59`. Направление ветра указывается в больших делениях угломера
+
+7. `Скорость ветра`. Значения -  целые числа, могут быть только положительными, либо нулем. Минимальное значение `0`, максимальное  `15`, указывается в м/с. (`Ячейка есть если выбран вариант составления метеобюллетеня с использованием ДМК`)
+
+8. `Дальность сноса пуль` Значения -  целые числа, могут быть только положительными, либо нулем. Минимальное значение `0`, максимальное  `150`, указывается в м. (`Ячейка есть если выбран вариант составления метеобюллетеня с использованием ВР`)
+
+9. Кнопка `Составить МЕТЕО-11` становится активна для пользователя, когда он заполнены все ячейки с исходными данными. После нажатия кнопки на форме происходит заполнение таблицы `Метео-11 ПРИБЛИЖЕННЫЙ` и появляется кнопка `Принять как Действ.` (принять как действующий)
+
+Пример:
+
+![](./_Images/8.png)
+
+### Приближенный
+В качестве характеристик полученных результатов расчета оформляется как _заголовок_ к таблице с результатами расчета. 
+
+![](./_Images/9.png)
+
+* **ДДЧЧМ** - ДД - день (число) месяца, ЧЧМ - часы (ЧЧ)  и десятки минут (М) окончания зондирования атмосферы (время измерения). 
+
+Пример: `24093` - 24 число месяца, время измерения 09.30 минут. Данные должны браться из системной даты и времени
+
+* **ВВВВ** - высота расположения метеопоста над уровнем моря. 
+
+Пример: `0060` - 60 метров над уровнем моря. Значение берется из ячейки `Высота метеопоста`
+
+* **БББТТ** :  
+**БББ** - отклонение наземного давления атмосферы от табличного на уровне метеорологической станции на момент окончания зондирования атмосферы (измерения). Отклонение наземного давления атмосферы ΔНо, записываемое в бюллетень, определяют как разность измеренного давления Но и табличного значения наземного давления, равного 750 мм рт. ст., т. е. `ΔНо = Но — 750` 
+
+Если измеренное давление будет больше 750 мм рт. ст., то отклонение ΔНо имеет знак «плюс» 
+
+Пример `ΔНо = 757 — 750 = 7` мм рт. ст. Отклонение +7 мм.рт.ст. записывается  в виде 007, отклонение +13 мм. рт. ст. записывается в виде `013`), если меньше — знак «минус» 
+
+Пример `ΔНо = 743 — 750 = — 7` мм рт. ст., Если отрицательное значение то в начале ставится число **пять**. Отклонение -7 мм.рт.ст. записывается  в виде `507`, отклонение `- 13` мм. рт. ст. записывается в виде `513`) .
+
+Значение берется из ячейки `Давление`
+
+* **ТТ** - отклонение наземной виртуальной температуры от табличного на уровне метеорологической станции на момент зондирования атмосферы (измерения) в градусах. 
+
+#### Расчет температуры (ТТ)
+
+Отклонение приземной виртуальной температуры ,  записываемое в бюллетень, определяется как разность между приземной виртуальной температурой, измеренной метеорологическим постом, 
+и ее табличным значением, равным +15,9°С, т.е.
+
+
+$ΔT_{0}^{мп}$ = T0  – 15,9
+
+
+Если приземная  виртуальная температура будет больше +15,9°С, 
+от отклонение  имеет знак **«плюс»**, если меньше – знак **«минус»**.
+Приземная виртуальная температура τ0 вычисляется по формуле:
+
+T0 = t0  + $ΔТ_{V}$
+
+где:
+- t0 - измеренная приземная температура воздуха. Берется из ячейки `температура`
+- $ΔТ_{V}$ - виртуальная поправка, определяемая по таблице 1.
+
+**Таблица 1**
+
+|  $t_{0}$ | Ниже 0 | 0 - 5 | 10 - 15 | 20 | 25 | 30 | 40 |
+|----------|--------|-------|---------|----|----|----|----|
+| $ΔТ_{V}$ | 0      | 0.5   | 1       | 1.5  | 2  | 3.5 | 4.5 |
+
+##### Примеры расчета температуры
+- `Пример 1`: 
+    
+    измеренная температура = `23`<br> 
+   
+1. T0 = t0  + $ΔТ_{V}$
+
+    > T0 = 23 + 1,8 = 24,8<br> 
+    > _(1,8 это интерполированное значение виртуальной поправки из `таблицы 1`)_
+
+2. $ΔT_{0}^{мп}$ = T0  – 15,9
+
+    > $ΔT_{0}^{мп}$ = 24,8-15,9 = 9 <br>
+    > _(получается 8.9, но округляем до 9)_ 
+    
+    Записывается в виде **09**
+
+### Алгоритмы расчета
+
+1. **Наименование задачи**: `составление приближенного бюллетеня «Метеосредний» по результатам наземных измерений с использованием метеорологического поста`
+
+- [Описание алгоритма расчета](./AlgoritmDmk.md)
+
+2. **Наименование задачи**: `составление приближенного бюллетеня «Метеосредний» по результатам наземных измерений с использованием ветрового ружья`
+
+- [Описание алгоритма расчета](./AlgoritmBp.md)
+

BIN
_Docs/_Images/1.png


BIN
_Docs/_Images/10.png


BIN
_Docs/_Images/11.png


BIN
_Docs/_Images/12.png


BIN
_Docs/_Images/13.png


BIN
_Docs/_Images/14.png


BIN
_Docs/_Images/15.png


BIN
_Docs/_Images/16.png


BIN
_Docs/_Images/2.png


BIN
_Docs/_Images/3.png


BIN
_Docs/_Images/4.png


BIN
_Docs/_Images/5.png


BIN
_Docs/_Images/6.png


BIN
_Docs/_Images/8.png


BIN
_Docs/_Images/9.png