Files
Rozpisky/Rozpisky/Dxf/RozpiskaValues.cs
T
marekandClaude Opus 5 a6377ab583 Baseline: současný stav projektu Rozpisky
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>
2026-09-07 09:45:49 +02:00

123 lines
5.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.Collections.Concurrent;
using System.Reflection;
using Rozpisky.Models;
namespace Rozpisky.Dxf;
/// <summary>
/// Sestaví slovník „tag DXF → hodnota" pro jednu rozpisku z údajů projektu, řádku přílohy a
/// sdílených číselníků. Mapování (které pole programu plní který atribut) <b>není v kódu</b>
/// čte se z dodávaného <c>mapovani.json</c> vedle .exe přes <see cref="MapovaniStore"/>, hodnoty
/// se dotahují reflexí. Klíče jsou kanonické (bez suffixu _NNN) párování řeší
/// <see cref="DxfTemplate.NormalizeTag"/>.
/// </summary>
public static class RozpiskaValues
{
public static Dictionary<string, string> Build(Projekt p, Priloha r, IEnumerable<Revize> revize, Ciselniky c)
{
// Tabulky pro dohledání (číslo revize → řádek, název firmy → firma, označení → část).
var revLookup = ToLookup(revize, x => x.Cislo);
var firmaLookup = ToLookup(c.Firmy, f => f.Nazev);
var castLookup = ToLookup(c.Casti, x => x.Oznaceni);
var v = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var m in MapovaniStore.Mapovani)
{
if (string.IsNullOrWhiteSpace(m.Tag) || string.IsNullOrWhiteSpace(m.Zdroj)) continue;
var val = Resolve(m.Zdroj, p, r, revLookup, firmaLookup, castLookup);
if (string.IsNullOrEmpty(val))
{
// Pole, která dle manuálu nesmí zůstat prázdná (kap. 4.1.7), dostanou „—"; ostatní se přeskočí.
if (!string.IsNullOrEmpty(m.Vychozi))
v[DxfTemplate.NormalizeTag(m.Tag)] = m.Vychozi;
continue;
}
val = ApplyTransform(m.Transform, val);
if (!string.IsNullOrEmpty(m.Prefix)) val = m.Prefix + val;
v[DxfTemplate.NormalizeTag(m.Tag)] = val;
}
// Strojový 47-poziční kód (K1K47) „kód pod rozpiskou". Negeneruje se přes mapování
// (skládá se z více polí), ale algoritmicky; každá pozice = jedna buňka K1..K47.
// Desky objektu mají pevný střed _XX_X_000 (typ/dílčí/pořadí se neberou z řádku).
string kod = r.JeDesky ? KodPrilohy.SestavDesky(p, r) : KodPrilohy.Sestav(p, r);
for (int i = 0; i < kod.Length && i < KodPrilohy.Delka; i++)
v[DxfTemplate.NormalizeTag($"K{i + 1}")] = kod[i].ToString();
return v;
}
/// <summary>Vyhodnotí <c>Zdroj</c> z mapování na konkrétní hodnotu. Tvary:
/// <c>Projekt.X</c>, <c>Priloha.Y</c>, a lookup <c>Revize:R1.Datum</c> / <c>Firma:ZhotovitelCasti.Adresa</c>
/// / <c>Cast:OznaceniCasti.Nazev</c> (klíč z vlastnosti přílohy → řádek číselníku → cílová vlastnost).</summary>
private static string? Resolve(string zdroj, Projekt p, Priloha r,
IReadOnlyDictionary<string, Revize> revLookup,
IReadOnlyDictionary<string, Firma> firmaLookup,
IReadOnlyDictionary<string, Cast> castLookup)
{
int colon = zdroj.IndexOf(':');
if (colon > 0)
{
var kind = zdroj[..colon];
var rest = zdroj[(colon + 1)..]; // "ZhotovitelCasti.Adresa"
int dot = rest.IndexOf('.');
if (dot <= 0) return null;
var klic = GetProp(r, rest[..dot]); // hodnota klíče z přílohy
var cilProp = rest[(dot + 1)..];
if (string.IsNullOrWhiteSpace(klic)) return null;
return kind switch
{
"Revize" => revLookup.TryGetValue(klic, out var rev) ? GetProp(rev, cilProp) : null,
"Firma" => firmaLookup.TryGetValue(klic, out var f) ? GetProp(f, cilProp) : null,
"Cast" => castLookup.TryGetValue(klic, out var ca) ? GetProp(ca, cilProp) : null,
_ => null,
};
}
int sep = zdroj.IndexOf('.');
if (sep <= 0) return null;
object? obj = zdroj[..sep] switch
{
"Projekt" => p,
"Priloha" => r,
_ => null,
};
return obj is null ? null : GetProp(obj, zdroj[(sep + 1)..]);
}
/// <summary>Pojmenované úpravy hodnoty. <c>FormatA4</c>: čistě číselný počet → „4 x A4".</summary>
private static string ApplyTransform(string? transform, string val) => transform switch
{
"FormatA4" => IsNumeric(val) ? $"{val.Trim()} x A4" : val,
_ => val,
};
private static bool IsNumeric(string s)
{
s = s.Trim();
return s.Length > 0 && s.All(char.IsDigit);
}
/// <summary>Slovník klíč→první výskyt (case-insensitive), prázdné klíče ignoruje.</summary>
private static Dictionary<string, T> ToLookup<T>(IEnumerable<T> src, Func<T, string> key)
{
var d = new Dictionary<string, T>(StringComparer.OrdinalIgnoreCase);
foreach (var x in src)
{
var k = key(x);
if (!string.IsNullOrWhiteSpace(k)) d.TryAdd(k, x);
}
return d;
}
// Cache PropertyInfo Build se volá pro každý řádek (rozpisku) při exportu.
private static readonly ConcurrentDictionary<(Type, string), PropertyInfo?> PropCache = new();
private static string? GetProp(object obj, string name)
{
var pi = PropCache.GetOrAdd((obj.GetType(), name),
key => key.Item1.GetProperty(key.Item2, BindingFlags.Public | BindingFlags.Instance));
return pi?.GetValue(obj) as string;
}
}