První commit — WPF aplikace pro generování výkresových rohových razítek (.NET 10, SkiaSharp, ACadSharp, ClosedXML) včetně xUnit testů, DXF šablony, log a dokumentace. Build výstupy (bin/, obj/, .vs/) jsou ignorovány. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
132 lines
8.0 KiB
C#
132 lines
8.0 KiB
C#
using System.Text;
|
||
using Rozpisky.Models;
|
||
|
||
namespace Rozpisky.Dxf;
|
||
|
||
/// <summary>
|
||
/// Generátor strojově čitelného <b>47-pozičního kódu</b> přílohy („kód pod rozpiskou", manuál SŽ
|
||
/// kap. 3.1.5 a 3.2.1–3.2.8). Kód má pevnou délku 47 znaků složenou ze 6 skupin oddělených znakem
|
||
/// <c>_</c> na pevných pozicích. Skládá se ze strukturovaných polí přílohy a projektu; každá pozice,
|
||
/// pro kterou není podklad, se vyplní znakem <c>X</c> – žádná pozice nezůstává prázdná.
|
||
/// <para>Rozložení (1-based pozice → obsah):</para>
|
||
/// <list type="bullet">
|
||
/// <item>1–11 Označení investora (S-kód), 12 = <c>_</c></item>
|
||
/// <item>13–16 Stupeň dokumentace, 17 = <c>_</c></item>
|
||
/// <item>18–21 Část (CAST) + řada = číslice z dílčí části (DILCI_CAST bez písmen a teček);
|
||
/// např. CAST <c>B</c> + DILCI_CAST <c>B.7</c> → <c>B7</c>, CAST <c>D</c> + <c>D110</c> → <c>D110</c>, 22 = <c>_</c></item>
|
||
/// <item>23–24 předpona objektu (SO/PS) + 25–31 číslo objektu (řada+úsek+řazení), 32 = <c>_</c></item>
|
||
/// <item>33–34 podobjekt, 35 = <c>_</c></item>
|
||
/// <item>36–37 typ přílohy (D1…), 38 = <c>_</c>, 39 část přílohy, 40 = <c>_</c></item>
|
||
/// <item>41–43 číslo přílohy, 44 = <c>_</c></item>
|
||
/// <item>45 typ revize (P/0/V) + 46–47 pořadí revize</item>
|
||
/// </list>
|
||
/// Sdružené objekty (SK/PK, kap. 3.3) a další okrajové případy zatím nejsou řešené – kód se skládá
|
||
/// přímočaře z dostupných hodnot.
|
||
/// </summary>
|
||
public static class KodPrilohy
|
||
{
|
||
/// <summary>Pevná délka kódu (počet pozic / buněk K1–K47).</summary>
|
||
public const int Delka = 47;
|
||
|
||
private const char Vypln = 'X';
|
||
|
||
/// <summary>Sestaví 47-znakový kód přílohy z údajů projektu a řádku přílohy.</summary>
|
||
public static string Sestav(Projekt p, Priloha r)
|
||
{
|
||
var obj = ObjektOznaceni.Rozpad(r.CisloObjektu);
|
||
var (revTyp, revCislo) = RozpadRevize(r.R1);
|
||
|
||
var sb = new StringBuilder(Delka);
|
||
Pole(sb, p.Skod, 11); // 1–11 investor / S-kód
|
||
sb.Append('_'); // 12
|
||
Pole(sb, p.StupenDokumentace, 4); // 13–16 stupeň dokumentace
|
||
sb.Append('_'); // 17
|
||
Pole(sb, ObjektOznaceni.Alfanum(r.OznaceniCasti) + Cislice(r.OznaceniDilciCasti), 4); // 18–21 část (CAST) + řada z DILCI_CAST bez písmen/teček
|
||
sb.Append('_'); // 22
|
||
Pole(sb, obj.Prefix, 2); // 23–24 SO/PS
|
||
Pole(sb, obj.Cislo + obj.Usek + obj.Razeni, 7); // 25–31 číslo objektu
|
||
sb.Append('_'); // 32
|
||
Pole(sb, obj.Podobjekt, 2); // 33–34 podobjekt
|
||
sb.Append('_'); // 35
|
||
Pole(sb, ObjektOznaceni.Alfanum(r.TypPrilohy), 2); // 36–37 typ přílohy (D1…)
|
||
sb.Append('_'); // 38
|
||
Pole(sb, r.DilciPriloha, 1); // 39 část přílohy
|
||
sb.Append('_'); // 40
|
||
Pole(sb, r.PoradiPrilohy, 3); // 41–43 číslo přílohy
|
||
sb.Append('_'); // 44
|
||
sb.Append(revTyp); // 45 typ revize
|
||
Pole(sb, revCislo, 2); // 46–47 pořadí revize
|
||
return sb.ToString();
|
||
}
|
||
|
||
/// <summary>Kód „seznamu příloh“ objektu (manuál SŽ kap. 5.1.11): pozice 1–34 jsou shodné s kódem
|
||
/// rozpisky objektu (S-kód, stupeň, část/řada, objekt, podobjekt), pozice 35–43 nahrazeny pevně
|
||
/// <c>_XX_X_001</c> (seznam = „příloha 001“ bez typu/dokumentu) a zbytek (44–47) je revize.
|
||
/// <paramref name="reprezentant"/> = libovolná příloha objektu (pozice 1–34 jsou objektové).
|
||
/// <paramref name="revizeKod"/> = zvolená revize seznamu (např. „V00“); null → revize z reprezentanta.</summary>
|
||
public static string SestavSeznam(Projekt p, Priloha reprezentant, string? revizeKod = null)
|
||
=> SestavObjektovy(p, reprezentant, "_XX_X_001", revizeKod);
|
||
|
||
/// <summary>Kód „rozpisky na spisové desky objektu“ (manuál SŽ): pozice 1–34 shodné s kódem
|
||
/// rozpisky objektu, pozice 35–43 nahrazeny pevně <c>_XX_X_000</c> (desky = „příloha 000“ bez
|
||
/// typu/dokumentu) a zbytek (44–47) je revize. Shoduje se s názvem souboru/rozvržení desek
|
||
/// (<c>objekt_[podobjekt]_XX_X_000</c>, viz <see cref="Models.Priloha.JmenoRozvrzeniAuto"/>) –
|
||
/// typ <c>XX</c> je literální placeholder a zůstává v obou.
|
||
/// <paramref name="reprezentant"/> = libovolná příloha objektu; <paramref name="revizeKod"/> =
|
||
/// zvolená revize, null → revize z reprezentanta.</summary>
|
||
public static string SestavDesky(Projekt p, Priloha reprezentant, string? revizeKod = null)
|
||
=> SestavObjektovy(p, reprezentant, "_XX_X_000", revizeKod);
|
||
|
||
/// <summary>Sdílený základ objektových kódů (seznam/desky): vezme 47-znakový kód reprezentanta
|
||
/// (pozice 1–34 = objektové, 44–47 = revize) a nahradí prostřední segment 35–43 hodnotou
|
||
/// <paramref name="stred"/> (např. <c>_XX_X_001</c> / <c>_XX_X_000</c>). <paramref name="revizeKod"/>
|
||
/// null → revize zůstane z reprezentanta, jinak se přepočítá přes <see cref="RozpadRevize"/>.</summary>
|
||
private static string SestavObjektovy(Projekt p, Priloha reprezentant, string stred, string? revizeKod)
|
||
{
|
||
var full = Sestav(p, reprezentant); // 47 znaků (revize z reprezentanta)
|
||
if (revizeKod is null)
|
||
return full[..34] + stred + full[43..]; // 1–34 | stred | 44–47 revize
|
||
|
||
var (typ, cislo) = RozpadRevize(revizeKod);
|
||
var sb = new StringBuilder(full[..34]); // 1–34
|
||
sb.Append(stred); // 35–43
|
||
sb.Append('_'); // 44
|
||
sb.Append(typ); // 45
|
||
Pole(sb, cislo, 2); // 46–47
|
||
return sb.ToString();
|
||
}
|
||
|
||
/// <summary>Ponechá jen číslice (zahodí písmena, tečky i další znaky) – použití pro řadu z pole
|
||
/// DILCI_CAST do kódu K18–K21: <c>B.7</c> → <c>7</c>, <c>D110</c> → <c>110</c>.</summary>
|
||
private static string Cislice(string? s)
|
||
=> string.IsNullOrEmpty(s) ? "" : new string(s.Where(char.IsDigit).ToArray());
|
||
|
||
/// <summary>Vloží hodnotu do pole pevné šířky: ořízne přebytek, chybějící pozice doplní <c>X</c>.</summary>
|
||
private static void Pole(StringBuilder sb, string? hodnota, int sirka)
|
||
{
|
||
var s = (hodnota ?? "").Trim();
|
||
if (s.Length > sirka) s = s[..sirka];
|
||
sb.Append(s);
|
||
for (int i = s.Length; i < sirka; i++) sb.Append(Vypln);
|
||
}
|
||
|
||
/// <summary>Z kódu revize (např. <c>R1</c>) odvodí typ verze (P/0/V) a její pořadí.
|
||
/// První znak je typ, zbytek je číslo revize. Bez revize → <c>X</c> a prázdné číslo.
|
||
/// Např. <c>V00</c> → (<c>V</c>, <c>00</c>), <c>002</c> → (<c>0</c>, <c>02</c>).</summary>
|
||
private static (char Typ, string Cislo) RozpadRevize(string? revKod)
|
||
{
|
||
string rev = (revKod ?? "").Trim();
|
||
if (rev.Length == 0) return (Vypln, "");
|
||
|
||
char typ = rev[0] switch
|
||
{
|
||
'P' or 'V' or '0' => rev[0],
|
||
_ when char.IsDigit(rev[0]) => '0',
|
||
_ => char.ToUpperInvariant(rev[0]),
|
||
};
|
||
// Číslo revize = číslice ZA typovým znakem (vedoucí znak je typ, ne součást čísla).
|
||
string cislo = new string(rev.Skip(1).Where(char.IsDigit).ToArray());
|
||
return (typ, cislo);
|
||
}
|
||
}
|