Add files via upload

This commit is contained in:
Gregory Bednov 2025-01-10 21:11:18 +03:00 committed by GitHub
commit 7663abb2ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 2152 additions and 0 deletions

1548
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

12
Cargo.toml Normal file
View file

@ -0,0 +1,12 @@
[package]
name = "fsa"
version = "0.1.0"
edition = "2021"
[dependencies]
svg = "0.18.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
futures-util = "0.3"
actix-web = "4.9.0"
actix-cors = "0.6"

49
src/main.rs Normal file
View file

@ -0,0 +1,49 @@
mod types;
use types::{as_svg::AsSVG, Объект};
use actix_web::{web, App, HttpServer, get, post, HttpResponse};
use actix_cors::Cors;
use std::sync::RwLock;
type ObjectStore = RwLock<Vec<Объект>>;
#[post("/add_object")]
async fn add_object(
data: web::Json<Объект>,
store: web::Data<ObjectStore>,
) -> HttpResponse {
let obj = data.into_inner();
let out = obj.to_svg(0.0, 0.0).to_string();
let mut objects = store.write().unwrap();
objects.push(obj);
HttpResponse::Ok().content_type("image/svg+xml").body(out)
}
#[get("/svg")]
async fn get_svg(store: web::Data<ObjectStore>) -> HttpResponse {
use types::as_svg::wrap_document;
use crate::types::as_svg::AsSVG;
let obj = store.read().unwrap();
let groups = obj.iter().map(|x| x.to_svg(0.0, 0.0)); // TODO 0.0, 0.0
let send_this = wrap_document(groups).to_string();
HttpResponse::Ok().content_type("image/svg+xml").body(send_this)
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let store = web::Data::new(RwLock::new(Vec::<Объект>::new()));
HttpServer::new(move || {
App::new()
.app_data(store.clone())
.service(add_object)
.service(get_svg)
.wrap(Cors::default()
.allow_any_origin() // Или укажите конкретные источники
.allow_any_method()
.allow_any_header())
})
.bind("127.0.0.1:8080")?
.run()
.await
}

120
src/types.rs Normal file
View file

@ -0,0 +1,120 @@
pub mod to_letter;
pub mod as_svg;
pub mod display;
use serde::{Serialize, Deserialize};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ОсновнаяВеличина {
АнализКачество,
ПламяГорение,
ополнительный,
ополнительный,
Напряжение,
Расход,
ополнительный,
РучноеВоздействие,
Ток,
Мощность,
Время,
Уровень,
ополнительный,
ополнительный,
ополнительный,
Давление,
Количество,
Радиоактивность,
СкоростьЧастота,
Температура,
Разнородные, // несколько величин ТОЛЬКО здесь
Вибрация,
Вес,
Нерекомендовано,
Событие,
Размер,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum УточнениеВеличины {
РазностьПерепад,
СоотношениеДоляДробь,
АвтоматическоеПереключениеОбегание,
ИнтегрированиеСуммированиеПоВремени,
СамосрабатывающееУБ,
СистемаИнструментальнойБезопасности
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ВыполняемаяФункция {
Сигнализация,
АвтоматическоеРегулирование,
ВеличинаОтклоненияОтЗаданной,
Регистрация
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ФункциональныйПризнак {
ЧувствительныйЭлемент,
ПервичныйПоказывающийПрибор,
ВторичныйПоказывающийПрибор,
СтанцияУправления,
ополнительный,
ВключениеОтключениеПереключение,
Преобразование,
ополнительный,
ВспомогательныеКомпьютерныеУстройства,
ВспомогательныеВычислительноеУстройство,
ополнительный
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ДополнительныеОбозначения {
ВерхнийПределИзмеряемойВеличины,
НижнийПределИзмеряемойВеличины,
ВеличинаИлиСреднееПоложение,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Ф {
ВыполняемаяФункция(ВыполняемаяФункция),
ФункциональныйПризнак(ФункциональныйПризнак),
ДополнительноеОбозначение(ДополнительныеОбозначения), // уточнить, не option ли это
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Направление {
Открывается,
Закрывается,
ОстаётсяНаМесте
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Фиксация {
НаМесте,
НаЩите,
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum ТипПрибора {
Величина {
величина: ОсновнаяВеличина,
уточнение: Option<УточнениеВеличины>,
функции: Vec<Ф>
},
Мультивеличина {
список: Vec<crate::types::ТипПрибора> // FIXME
}
}
#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub enum Объект {
Прибор {
фиксация: Фиксация,
тип_прибора: ТипПрибора,
является_паз: bool,
},
ИсполнительныйМеханизм {
имеет_ручной: bool,
направление: Option<Направление>,
},
}

218
src/types/as_svg.rs Normal file
View file

@ -0,0 +1,218 @@
use svg::{Document, Node};
use crate::types::*;
use svg::node::element::{Circle, Line, Group, Rectangle, Text, Marker, Path, Definitions};
use svg::node::element::Polygon;
const PRIMARY_COLOR: &str = "CanvasText";
const SECONDARY_COLOR: &str = "Canvas";
const TRANSPARENT_COLOR: &str = "transparent"; // used to be transparent
const HIGHLIGHT_COLOR: &str = "highlight"; // use to be red
macro_rules! svg {
($obj:expr, $($key:ident: $value:expr),* $(,)?) => {{
let mut obj = $obj;
$(
obj = obj.set(stringify!($key), $value);
)*
obj
}};
}
macro_rules! create_if {
($condition:expr, $create:expr, $($key:ident: $value:expr),*) => {{
if $condition {
svg!($create, $($key: $value),*)
} else {
$create
}
}};
($condition:expr, $create:expr) => {{
if $condition {
$create
} else {
$create
}
}};
}
pub trait AsSVG {
fn to_svg(&self, left_x:f32, top_y:f32) -> Group;
}
pub fn make_main_letters(тип: &ТипПрибора) -> String {
use crate::types::to_letter::ToLetter;
match тип {
ТипПрибора::Величина {
величина,
уточнение,
функции
} => {
let str1 = функции.iter()
.map(|x|
match x {
Ф::ВыполняемаяФункция(л) => л.to_letter(),
Ф::ФункциональныйПризнак(л) => л.to_letter(),
Ф::ДополнительноеОбозначение(_) => ' '
})
.filter(|&x| x != ' ').collect::<String>();
let c =
if let Some(x) = уточнение { x.to_letter() } else { ' ' };
let str2:String = vec!(величина.to_letter(), c)
.into_iter()
.filter(|&x| x != ' ')
.collect();
str2+&str1
},
_ => "U".to_owned() // TODO !!!
}
}
impl AsSVG for Объект {
fn to_svg(&self, left_x: f32, top_y: f32) -> Group {
let mut group = Group::new().set("class", "draggable-group");
match self {
Объект::Прибор {
фиксация,
является_паз,
тип_прибора
} => {
if ! *является_паз {
group.append(Circle::new()
.set("cx", left_x + 5.0)
.set("cy", top_y + 4.0)
.set("r", 5)
.set("stroke", PRIMARY_COLOR)
.set("fill", SECONDARY_COLOR));
} else {
group.append(
diamond(10.0, 10.0, left_x, top_y)
.set("fill", SECONDARY_COLOR));
group.append(
Rectangle::new()
.set("x", left_x)
.set("y", top_y)
.set("width", 10)
.set("height", 10)
.set("stroke", PRIMARY_COLOR)
.set("fill", TRANSPARENT_COLOR)
);
}
group.add(svg!(
Text::new(make_main_letters(тип_прибора)),
x: left_x + 5.0, y: top_y + 4.0,
stroke: "none", fill: PRIMARY_COLOR
)
.set("alignment-baseline", "baseline")
.set("font-size", "1mm")
.set("text-anchor", "middle"))
.add(create_if!(*фиксация == Фиксация::НаЩите,
Line::new(),
x1: left_x, y1: top_y + 5.0,
x2: left_x + 10.0, y2: top_y + 5.0,
stroke: PRIMARY_COLOR)
.set("stroke-width", "0.075mm"))
}
Объект::ИсполнительныйМеханизм {
имеет_ручной,
направление,
} => {
let d = 5.0;
let r = d / 2.0;
group.append(
Circle::new()
.set("cx", left_x + 2.5)
.set("cy", top_y + 2.5)
.set("r", 2.5)
.set("stroke", PRIMARY_COLOR)
.set("fill", SECONDARY_COLOR));
let basic_line = svg!(Line::new(),
x1: left_x + r, y1: top_y + d,
x2: left_x + r, y2: top_y + d + 10.0,
stroke: PRIMARY_COLOR);
let arrow = match направление {
Some(Направление::Открывается) => basic_line.set("marker-start", "url(#arrow)"),
Some(Направление::Закрывается) => basic_line.set("marker-end", "url(#arrow)"),
Some(Направление::ОстаётсяНаМесте) => basic_line.set("marker-start", "url(#arrow)").set("marker-end", "url(#arrow)"),
None => basic_line
};
if *имеет_ручной {
let h: Text = svg!(Text::new("H"),
x: left_x + r,
y: top_y + r,
fill: PRIMARY_COLOR,
stroke: "none")
.set("text-align", "center")
.set("font-size", "1mm")
.set("text-anchor", "middle")
.set("dominant-baseline", "middle");
group.append(h);
}
group.add(arrow)
}
}
}
}
fn diamond(width: f32, height: f32, left_x: f32, top_y: f32) -> Polygon {
let pw = width / 2.0;
let w = width;
let h = height;
let ph = height / 2.0;
let points = format!(
"{},{} {},{} {},{} {},{}",
left_x + pw,
top_y - ph,
left_x + w + pw,
top_y + ph,
left_x + pw,
top_y + h + ph,
left_x - pw,
top_y + ph
);
Polygon::new().set("points", points)
.set("stroke", PRIMARY_COLOR)
.set("fill", TRANSPARENT_COLOR)
}
pub fn wrap_document<I>(ls: I) -> Document
where
I: IntoIterator<Item = Group>, // Обобщаем входной тип через IntoIterator
{
let marker = Marker::new()
.set("id", "arrow")
.set("viewBox", "0 0 297 210")
.set("refX", "9")
.set("refY", "5")
.set("fill", PRIMARY_COLOR)
.set("markerWidth", "6")
.set("markerHeight", "6")
.set("orient", "auto-start-reverse")
.add(Path::new().set("d", "M 0 0 L 10 5 L 0 10 z"));
let mut doc = Document::new()
.set("xmlns", "http://www.w3.org/2000/svg")
.set("width", "297mm")
.set("height", "210mm")
.set("viewBox", (0, 0, 297, 210))
.set("stroke", PRIMARY_COLOR)
.set("stroke-width", "0.125mm")
.set("fill", TRANSPARENT_COLOR)
.add(Definitions::new().add(marker));
for el in ls {
doc.append(el);
}
doc
}

110
src/types/display.rs Normal file
View file

@ -0,0 +1,110 @@
use std::fmt::Display;
use crate::types::*;
impl Display for ОсновнаяВеличина {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::types::ОсновнаяВеличина::*;
let str = match self {
АнализКачество => "Анализ, качество",
ПламяГорение => "Пламя, горение",
ополнительный => "Дополнительная величина",
ополнительный => "Дополнительная величина",
Напряжение => "Напряжение",
Расход => "Расход",
ополнительный => "Дополнительная величина",
РучноеВоздействие => "Ручное воздействие",
Ток => "Ток",
Мощность => "Мощность",
Время => "Время",
Уровень => "Уровень",
ополнительный => "Дополнительная величина",
ополнительный => "Дополнительная величина",
ополнительный => "Дополнительная величина",
Давление => "Давление",
Количество => "Количество",
Радиоактивность => "Радиоактивность",
СкоростьЧастота => "Скорость, частота",
Температура => "Температура",
Разнородные => "Разнородные величины", // несколько величин ТОЛЬКО здесь
Вибрация => "Вибрация",
Вес => "Вес",
Нерекомендовано => "Дополнительная величина (нерекомендованная буква)",
Событие => "Событие",
Размер => "Размер"
}.to_owned();
write!(f, "{}", str)
}
}
impl Display for УточнениеВеличины {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::types::УточнениеВеличины::*;
let str = match self {
РазностьПерепад => "Разность, перепад",
СоотношениеДоляДробь => "Соотношение, доля, дробь",
АвтоматическоеПереключениеОбегание => "Автоматическое переключение, обегание",
ИнтегрированиеСуммированиеПоВремени => "Интегрирование, суммирование по времени",
СамосрабатывающееУБ => "Самосрабаывающее устройство безопасности",
СистемаИнструментальнойБезопасности => "Система инструментальной безопасности",
}.to_owned();
write!(f, "{}", str)
}
}
impl Display for ВыполняемаяФункция {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::types::ВыполняемаяФункция::*;
let str = match self {
Сигнализация => "Сигнализация",
АвтоматическоеРегулирование => "Автоматическое регулирование",
ВеличинаОтклоненияОтЗаданной => "Величина отклонения от заданной",
Регистрация => "Регистрация"
}.to_owned();
write!(f, "{}", str)
}
}
impl Display for ФункциональныйПризнак {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::types::ФункциональныйПризнак::*;
let str = match self {
ЧувствительныйЭлемент => "Чувтсвительный эе",
ПервичныйПоказывающийПрибор => "Первичный показывающий прибор",
ВторичныйПоказывающийПрибор => "Вторичный показывающий прибор",
СтанцияУправления => "Станция управления",
ополнительный => "Дополнительный функциональный признак",
ВключениеОтключениеПереключение => "Включение, отключение, переключение",
Преобразование => "Преобразование",
ополнительный => "Дополнительный функциональный признак",
ВспомогательныеКомпьютерныеУстройства => "Вспомогательные компьютерные устройства",
ВспомогательныеВычислительноеУстройство => "Вспомогательные вычислительные устройства",
ополнительный => "Дополнительный функциональный признак"
}.to_owned();
write!(f, "{}", str)
}
}
impl Display for Направление {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::types::Направление::*;
let str = match self {
Открывается => "открывается",
Закрывается => "закрывается",
ОстаётсяНаМесте => "остаётся на месте"
}.to_owned();
write!(f, "{}", str)
}
}
impl Display for Фиксация {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use crate::types::Фиксация::*;
let str = match self {
НаМесте => "на месте",
НаЩите => "на щите"
}.to_owned();
write!(f, "{}", str)
}
}

95
src/types/to_letter.rs Normal file
View file

@ -0,0 +1,95 @@
use crate::types;
pub trait ToLetter {
fn to_letter(&self) -> char;
}
impl ToLetter for types::ОсновнаяВеличина {
fn to_letter(&self) -> char {
use types::ОсновнаяВеличина::*;
match self {
АнализКачество => 'A',
ПламяГорение => 'B',
ополнительный => 'C',
ополнительный => 'D',
Напряжение => 'E',
Расход => 'F',
ополнительный => 'G',
РучноеВоздействие => 'H',
Ток => 'I',
Мощность => 'J',
Время => 'K',
Уровень => 'L',
ополнительный => 'M',
ополнительный => 'N',
ополнительный => 'O',
Давление => 'P',
Количество => 'Q',
Радиоактивность => 'R',
СкоростьЧастота => 'S',
Температура => 'T',
Разнородные => 'U',
Вибрация => 'V',
Вес => 'W',
Нерекомендовано => 'X',
Событие => 'Y',
Размер => 'Z',
}
}
}
impl ToLetter for types::УточнениеВеличины {
fn to_letter(&self) -> char {
use types::УточнениеВеличины::*;
match self {
РазностьПерепад => 'D',
СоотношениеДоляДробь => 'F',
АвтоматическоеПереключениеОбегание => 'J',
ИнтегрированиеСуммированиеПоВремени => 'Q',
СамосрабатывающееУБ => 'S',
СистемаИнструментальнойБезопасности => 'Z',
}
}
}
impl ToLetter for types::ВыполняемаяФункция {
fn to_letter(&self) -> char {
use types::ВыполняемаяФункция::*;
match self {
Сигнализация => 'A',
АвтоматическоеРегулирование => 'C',
ВеличинаОтклоненияОтЗаданной => 'D',
Регистрация => 'R',
}
}
}
impl ToLetter for types::ФункциональныйПризнак {
fn to_letter(&self) -> char {
use types::ФункциональныйПризнак::*;
match self {
ЧувствительныйЭлемент => 'E',
ПервичныйПоказывающийПрибор => 'G',
ВторичныйПоказывающийПрибор => 'I',
СтанцияУправления => 'K',
ВключениеОтключениеПереключение => 'S',
Преобразование => 'T',
ВспомогательныеКомпьютерныеУстройства => 'X',
ВспомогательныеВычислительноеУстройство => 'Y',
ополнительный => 'Q',
ополнительный => 'V',
ополнительный => 'Z'
}
}
}
impl ToLetter for types::ДополнительныеОбозначения {
fn to_letter(&self) -> char {
use types::ДополнительныеОбозначения::*;
match self {
ВерхнийПределИзмеряемойВеличины => 'H',
НижнийПределИзмеряемойВеличины => 'L',
ВеличинаИлиСреднееПоложение => 'M',
}
}
}