细节修改
This commit is contained in:
parent
95a859ec85
commit
b6a6febec4
@ -970,9 +970,9 @@ PowerShell.exe -ExecutionPolicy Bypass -File .\run.ps1
|
|||||||
|
|
||||||
| 阈值 | 默认值 | 含义 |
|
| 阈值 | 默认值 | 含义 |
|
||||||
| ----------------- | ------ | -------------------------- |
|
| ----------------- | ------ | -------------------------- |
|
||||||
| `MaxOnTimeMs` | 5.0 ms | 开启时间上限 |
|
| `MaxOnTimeMs` | 50.0 ms | 开启时间上限 |
|
||||||
| `MaxRiseTimeMs` | 3.0 ms | 上升时间上限 |
|
| `MaxRiseTimeMs` | 3.0 ms | 上升时间上限 |
|
||||||
| `MaxOffTimeMs` | 5.0 ms | 关断时间上限 |
|
| `MaxOffTimeMs` | 50.0 ms | 关断时间上限 |
|
||||||
| `MaxFallTimeMs` | 3.0 ms | 下降时间上限 |
|
| `MaxFallTimeMs` | 3.0 ms | 下降时间上限 |
|
||||||
|
|
||||||
> **【提示】** 阈值随项目保存(位于 `project.json` 的 `CurveThresholds` 字段),可针对不同型号被测件调整。
|
> **【提示】** 阈值随项目保存(位于 `project.json` 的 `CurveThresholds` 字段),可针对不同型号被测件调整。
|
||||||
@ -1734,8 +1734,8 @@ $$
|
|||||||
|
|
||||||
| 编号 | 中文名 | 单位 | 定义 | 测试方法 | 判定准则 | 软件位置 |
|
| 编号 | 中文名 | 单位 | 定义 | 测试方法 | 判定准则 | 软件位置 |
|
||||||
| ---- | ------------------ | ---- | ----------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------- | --------------- |
|
| ---- | ------------------ | ---- | ----------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------- | --------------- |
|
||||||
| 1.1 | 开启时间 | ms | 控制信号有效 → 输出电压上升到 90% V<sub>max</sub> 的时间 | 高速采集 + 上升沿百分位检测 | ≤ MaxOnTimeMs(默认 5) | 第 10 章 |
|
| 1.1 | 开启时间 | ms | 控制信号有效 → 输出电压上升到 90% V<sub>max</sub> 的时间 | 高速采集 + 上升沿百分位检测 | ≤ MaxOnTimeMs(默认 50) | 第 10 章 |
|
||||||
| 1.2 | 关闭时间 | ms | 控制信号撤销 → 输出电压下降到 10% V<sub>max</sub> 的时间 | 高速采集 + 下降沿百分位检测 | ≤ MaxOffTimeMs(默认 5) | 第 10 章 |
|
| 1.2 | 关闭时间 | ms | 控制信号撤销 → 输出电压下降到 10% V<sub>max</sub> 的时间 | 高速采集 + 下降沿百分位检测 | ≤ MaxOffTimeMs(默认 50) | 第 10 章 |
|
||||||
| 1.3 | 控制延迟时间 | ms | 控制端电平变化 → 内部驱动信号到达 | 厂商方法;本软件等同于"开启时间"近似 | 由型号规范确定 | 第 10 章(近似) |
|
| 1.3 | 控制延迟时间 | ms | 控制端电平变化 → 内部驱动信号到达 | 厂商方法;本软件等同于"开启时间"近似 | 由型号规范确定 | 第 10 章(近似) |
|
||||||
| 1.4 | 释放延迟时间 | ms | 控制端撤销 → 内部驱动撤销 | 同上 | 由型号规范确定 | 第 10 章(近似) |
|
| 1.4 | 释放延迟时间 | ms | 控制端撤销 → 内部驱动撤销 | 同上 | 由型号规范确定 | 第 10 章(近似) |
|
||||||
| 1.5 | 负载电压上升/下降时间 | ms | 负载侧电压 10% – 90% V<sub>max</sub> 的过渡时间 | 高速采集 + 双阈值检测 | ≤ MaxRiseTimeMs / MaxFallTimeMs(默认 3)| 第 10 章 |
|
| 1.5 | 负载电压上升/下降时间 | ms | 负载侧电压 10% – 90% V<sub>max</sub> 的过渡时间 | 高速采集 + 双阈值检测 | ≤ MaxRiseTimeMs / MaxFallTimeMs(默认 3)| 第 10 章 |
|
||||||
|
|||||||
@ -43,9 +43,9 @@ public partial class CurveResultRow : ObservableObject
|
|||||||
/// <summary>曲线测试阈值(合格判定)。</summary>
|
/// <summary>曲线测试阈值(合格判定)。</summary>
|
||||||
public sealed class CurveThresholds
|
public sealed class CurveThresholds
|
||||||
{
|
{
|
||||||
public double MaxOnTimeMs { get; set; } = 5.0;
|
public double MaxOnTimeMs { get; set; } = 50.0;
|
||||||
public double MaxRiseTimeMs { get; set; } = 3.0;
|
public double MaxRiseTimeMs { get; set; } = 3.0;
|
||||||
public double MaxOffTimeMs { get; set; } = 5.0;
|
public double MaxOffTimeMs { get; set; } = 50.0;
|
||||||
public double MaxFallTimeMs { get; set; } = 3.0;
|
public double MaxFallTimeMs { get; set; } = 3.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -62,7 +62,16 @@ public sealed class ReportGenerator
|
|||||||
column.Item().PaddingTop(45).Element(container => CoverIdentity(container, p, reportNo));
|
column.Item().PaddingTop(45).Element(container => CoverIdentity(container, p, reportNo));
|
||||||
column.Item().PaddingTop(34).Element(container => OverallPanel(container, p));
|
column.Item().PaddingTop(34).Element(container => OverallPanel(container, p));
|
||||||
column.Item().ExtendVertical();
|
column.Item().ExtendVertical();
|
||||||
column.Item().PaddingBottom(18).BorderTop(1).BorderColor(Line).PaddingTop(14).Table(table =>
|
});
|
||||||
|
|
||||||
|
page.Footer().Element(container => CoverFooter(container, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void CoverFooter(IContainer container, Project p)
|
||||||
|
{
|
||||||
|
container.Column(column =>
|
||||||
|
{
|
||||||
|
column.Item().BorderTop(1).BorderColor(Line).PaddingTop(14).Table(table =>
|
||||||
{
|
{
|
||||||
table.ColumnsDefinition(columns =>
|
table.ColumnsDefinition(columns =>
|
||||||
{
|
{
|
||||||
@ -74,13 +83,12 @@ public sealed class ReportGenerator
|
|||||||
CoverSignCell(table, "审核", "");
|
CoverSignCell(table, "审核", "");
|
||||||
CoverSignCell(table, "批准", "");
|
CoverSignCell(table, "批准", "");
|
||||||
});
|
});
|
||||||
});
|
column.Item().PaddingTop(12).Row(row =>
|
||||||
|
{
|
||||||
page.Footer().Row(row =>
|
row.RelativeItem().Text("本报告仅对所检样品及记录数据负责").FontSize(8).FontColor(Muted);
|
||||||
{
|
row.ConstantItem(180).AlignRight().Text($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm}")
|
||||||
row.RelativeItem().Text("本报告仅对所检样品及记录数据负责").FontSize(8).FontColor(Muted);
|
.FontSize(8).FontColor(Muted);
|
||||||
row.ConstantItem(180).AlignRight().Text($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm}")
|
});
|
||||||
.FontSize(8).FontColor(Muted);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +212,7 @@ public sealed class ReportGenerator
|
|||||||
Section(column, section++, "开启与关断曲线", "SWITCHING CHARACTERISTICS");
|
Section(column, section++, "开启与关断曲线", "SWITCHING CHARACTERISTICS");
|
||||||
if (p.CurveResults.Count > 0)
|
if (p.CurveResults.Count > 0)
|
||||||
{
|
{
|
||||||
string criteria = $"判据:开启≤{p.CurveThresholds.MaxOnTimeMs:F1}ms,上升≤{p.CurveThresholds.MaxRiseTimeMs:F1}ms,关断≤{p.CurveThresholds.MaxOffTimeMs:F1}ms,下降≤{p.CurveThresholds.MaxFallTimeMs:F1}ms";
|
string criteria = $"判据:开启时间≤{p.CurveThresholds.MaxOnTimeMs:F1}ms,上升时间≤{p.CurveThresholds.MaxRiseTimeMs:F1}ms,关断时间≤{p.CurveThresholds.MaxOffTimeMs:F1}ms,下降时间≤{p.CurveThresholds.MaxFallTimeMs:F1}ms";
|
||||||
column.Item().Element(x => ModuleSummary(x, criteria, p.CurveResults.Select(r => r.Status)));
|
column.Item().Element(x => ModuleSummary(x, criteria, p.CurveResults.Select(r => r.Status)));
|
||||||
column.Item().Element(x => CurveTable(x, p));
|
column.Item().Element(x => CurveTable(x, p));
|
||||||
}
|
}
|
||||||
@ -462,14 +470,14 @@ public sealed class ReportGenerator
|
|||||||
private static void OverallPanel(IContainer container, Project p)
|
private static void OverallPanel(IContainer container, Project p)
|
||||||
{
|
{
|
||||||
container.Border(1.2f).BorderColor(StatusColor(p.OverallStatus))
|
container.Border(1.2f).BorderColor(StatusColor(p.OverallStatus))
|
||||||
.Background(StatusBackground(p.OverallStatus)).Padding(14).Row(row =>
|
.Background(StatusBackground(p.OverallStatus)).Padding(12).Row(row =>
|
||||||
{
|
{
|
||||||
row.RelativeItem().Column(column =>
|
row.RelativeItem().Column(column =>
|
||||||
{
|
{
|
||||||
column.Item().Text("综合检测结论").FontSize(9).FontColor(Muted);
|
column.Item().Text("综合检测结论").FontSize(9).FontColor(Muted);
|
||||||
column.Item().PaddingTop(4).Text(ConclusionText(p.OverallStatus)).FontSize(9);
|
column.Item().PaddingTop(4).Text(ConclusionText(p.OverallStatus)).FontSize(9);
|
||||||
});
|
});
|
||||||
row.ConstantItem(110).AlignRight().AlignMiddle().Text(StatusText(p.OverallStatus))
|
row.ConstantItem(72).PaddingLeft(10).AlignRight().AlignMiddle().Text(StatusText(p.OverallStatus))
|
||||||
.FontSize(18).Bold().FontColor(StatusColor(p.OverallStatus));
|
.FontSize(18).Bold().FontColor(StatusColor(p.OverallStatus));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,10 +34,11 @@ public sealed class WordReportGenerator
|
|||||||
Body body = main.Document.Body!;
|
Body body = main.Document.Body!;
|
||||||
|
|
||||||
string reportNo = BuildReportNumber(project);
|
string reportNo = BuildReportNumber(project);
|
||||||
|
string? firstPageFooterId = project.Report.Cover ? AddFirstPageFooter(main) : null;
|
||||||
if (project.Report.Cover)
|
if (project.Report.Cover)
|
||||||
ComposeCover(body, project, reportNo);
|
ComposeCover(body, project, reportNo);
|
||||||
ComposeBody(body, main, project, reportNo);
|
ComposeBody(body, main, project, reportNo);
|
||||||
body.Append(PageSectionProperties());
|
body.Append(PageSectionProperties(firstPageFooterId));
|
||||||
|
|
||||||
main.Document.Save();
|
main.Document.Save();
|
||||||
}
|
}
|
||||||
@ -95,11 +96,17 @@ public sealed class WordReportGenerator
|
|||||||
}),
|
}),
|
||||||
Spacer(18),
|
Spacer(18),
|
||||||
ConclusionBox(p),
|
ConclusionBox(p),
|
||||||
Spacer(52),
|
|
||||||
SignatureTable(),
|
|
||||||
PageBreak());
|
PageBreak());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string AddFirstPageFooter(MainDocumentPart main)
|
||||||
|
{
|
||||||
|
var footerPart = main.AddNewPart<FooterPart>();
|
||||||
|
footerPart.Footer = new Footer(SignatureTable(), FooterParagraph());
|
||||||
|
footerPart.Footer.Save();
|
||||||
|
return main.GetIdOfPart(footerPart);
|
||||||
|
}
|
||||||
|
|
||||||
private void ComposeBody(Body body, MainDocumentPart main, Project p, string reportNo)
|
private void ComposeBody(Body body, MainDocumentPart main, Project p, string reportNo)
|
||||||
{
|
{
|
||||||
body.Append(HeaderParagraph("固态功率控制器动态参数检测报告", reportNo));
|
body.Append(HeaderParagraph("固态功率控制器动态参数检测报告", reportNo));
|
||||||
@ -388,10 +395,21 @@ public sealed class WordReportGenerator
|
|||||||
private static Paragraph FooterParagraph() =>
|
private static Paragraph FooterParagraph() =>
|
||||||
Paragraph($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm} 本报告仅对所检样品及记录数据负责。", 16, Muted, JustificationValues.Center);
|
Paragraph($"生成时间:{DateTime.Now:yyyy-MM-dd HH:mm} 本报告仅对所检样品及记录数据负责。", 16, Muted, JustificationValues.Center);
|
||||||
|
|
||||||
private static SectionProperties PageSectionProperties() =>
|
private static SectionProperties PageSectionProperties(string? firstPageFooterId = null)
|
||||||
new(
|
{
|
||||||
|
var properties = new SectionProperties();
|
||||||
|
if (!string.IsNullOrWhiteSpace(firstPageFooterId))
|
||||||
|
{
|
||||||
|
properties.Append(
|
||||||
|
new FooterReference { Type = HeaderFooterValues.First, Id = firstPageFooterId },
|
||||||
|
new TitlePage());
|
||||||
|
}
|
||||||
|
|
||||||
|
properties.Append(
|
||||||
new PageSize { Width = 11906U, Height = 16838U },
|
new PageSize { Width = 11906U, Height = 16838U },
|
||||||
new PageMargin { Top = 900, Right = 850U, Bottom = 900, Left = 850U, Header = 450U, Footer = 450U, Gutter = 0U });
|
new PageMargin { Top = 900, Right = 850U, Bottom = 900, Left = 850U, Header = 450U, Footer = 450U, Gutter = 0U });
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
private static long CmToEmu(double cm) => (long)Math.Round(cm * 360000.0);
|
private static long CmToEmu(double cm) => (long)Math.Round(cm * 360000.0);
|
||||||
|
|
||||||
|
|||||||
@ -39,6 +39,15 @@ public sealed class WordReportGeneratorTests
|
|||||||
.ToArray();
|
.ToArray();
|
||||||
Assert.Contains("SimSun", fontNames);
|
Assert.Contains("SimSun", fontNames);
|
||||||
Assert.Equal(2, document.MainDocumentPart.ImageParts.Count());
|
Assert.Equal(2, document.MainDocumentPart.ImageParts.Count());
|
||||||
|
|
||||||
|
var sectionProperties = document.MainDocumentPart.Document.Body!.Descendants<SectionProperties>().Single();
|
||||||
|
var firstFooterReference = sectionProperties.Elements<FooterReference>()
|
||||||
|
.Single(x => x.Type?.Value == HeaderFooterValues.First);
|
||||||
|
Assert.Contains(sectionProperties.Elements<TitlePage>(), _ => true);
|
||||||
|
|
||||||
|
var firstFooter = (FooterPart)document.MainDocumentPart.GetPartById(firstFooterReference.Id!);
|
||||||
|
Assert.Single(firstFooter.Footer!.Elements<Table>());
|
||||||
|
Assert.Equal(2, firstFooter.Footer!.Descendants<TableRow>().Count());
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|||||||
@ -377,10 +377,35 @@ public partial class MainViewModel : ObservableObject
|
|||||||
_viewBeforeSettings = CurrentView;
|
_viewBeforeSettings = CurrentView;
|
||||||
var vm = _sp.GetRequiredService<SettingsViewModel>();
|
var vm = _sp.GetRequiredService<SettingsViewModel>();
|
||||||
vm.SetCloseAction(CloseSettings);
|
vm.SetCloseAction(CloseSettings);
|
||||||
|
vm.SetProjectRefreshAction(RefreshProjectsFromSettingsAsync, CanRefreshProjectsFromSettings);
|
||||||
CurrentView = vm;
|
CurrentView = vm;
|
||||||
DevicePanelOpen = false;
|
DevicePanelOpen = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool CanRefreshProjectsFromSettings()
|
||||||
|
{
|
||||||
|
if (State != AppState.Testing) return true;
|
||||||
|
|
||||||
|
_logSink.Add(UiLogLevel.Warning, "测试中不可切换项目目录,请退出测试后再刷新工程。");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RefreshProjectsFromSettingsAsync(string projectsRoot)
|
||||||
|
{
|
||||||
|
if (!CanRefreshProjectsFromSettings()) return;
|
||||||
|
if (string.IsNullOrWhiteSpace(projectsRoot))
|
||||||
|
{
|
||||||
|
_logSink.Add(UiLogLevel.Error, "项目数据路径为空,无法刷新工程。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Directory.CreateDirectory(projectsRoot);
|
||||||
|
_store.ProjectsRoot = projectsRoot;
|
||||||
|
CurrentProject = null;
|
||||||
|
_viewBeforeSettings = _sp.GetRequiredService<HomeViewModel>();
|
||||||
|
await LoadProjectsAsync();
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenSettingsFromDevicePanel()
|
private void OpenSettingsFromDevicePanel()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -9,6 +9,7 @@ using Microsoft.Extensions.Options;
|
|||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using SSPCTester.Devices.Config;
|
using SSPCTester.Devices.Config;
|
||||||
using SSPCTester.Devices.Interfaces;
|
using SSPCTester.Devices.Interfaces;
|
||||||
|
using SSPCTester.Logic.Storage;
|
||||||
using SSPCTester.UI.Services;
|
using SSPCTester.UI.Services;
|
||||||
|
|
||||||
namespace SSPCTester.UI.ViewModels;
|
namespace SSPCTester.UI.ViewModels;
|
||||||
@ -25,14 +26,18 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
private readonly StorageOptions _runtimeStorage;
|
private readonly StorageOptions _runtimeStorage;
|
||||||
private readonly ReportOptions _runtimeReport;
|
private readonly ReportOptions _runtimeReport;
|
||||||
private readonly UserSettingsStore _settingsStore;
|
private readonly UserSettingsStore _settingsStore;
|
||||||
|
private readonly ProjectStore _projectStore;
|
||||||
private Action? _closeAction;
|
private Action? _closeAction;
|
||||||
|
private Func<bool>? _canRefreshProjects;
|
||||||
|
private Func<string, Task>? _projectRefreshAction;
|
||||||
|
|
||||||
public SettingsViewModel(
|
public SettingsViewModel(
|
||||||
IOptions<DeviceOptions> devOpts,
|
IOptions<DeviceOptions> devOpts,
|
||||||
IOptions<StorageOptions> storeOpts,
|
IOptions<StorageOptions> storeOpts,
|
||||||
IOptions<ReportOptions> reportOpts,
|
IOptions<ReportOptions> reportOpts,
|
||||||
ISspc sspc, IPowerSupply power, ILoad load, IDaq daq, ISafetyDio safetyDio, UiLogSink log,
|
ISspc sspc, IPowerSupply power, ILoad load, IDaq daq, ISafetyDio safetyDio, UiLogSink log,
|
||||||
UserSettingsStore settingsStore)
|
UserSettingsStore settingsStore,
|
||||||
|
ProjectStore projectStore)
|
||||||
{
|
{
|
||||||
_runtimeDevices = devOpts.Value;
|
_runtimeDevices = devOpts.Value;
|
||||||
_runtimeStorage = storeOpts.Value;
|
_runtimeStorage = storeOpts.Value;
|
||||||
@ -44,6 +49,7 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
_safetyDio = safetyDio;
|
_safetyDio = safetyDio;
|
||||||
_log = log;
|
_log = log;
|
||||||
_settingsStore = settingsStore;
|
_settingsStore = settingsStore;
|
||||||
|
_projectStore = projectStore;
|
||||||
|
|
||||||
Devices = CloneDeviceOptions(_runtimeDevices);
|
Devices = CloneDeviceOptions(_runtimeDevices);
|
||||||
Storage = CloneStorageOptions(_runtimeStorage);
|
Storage = CloneStorageOptions(_runtimeStorage);
|
||||||
@ -141,6 +147,12 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
|
|
||||||
public void SetCloseAction(Action closeAction) => _closeAction = closeAction;
|
public void SetCloseAction(Action closeAction) => _closeAction = closeAction;
|
||||||
|
|
||||||
|
public void SetProjectRefreshAction(Func<string, Task> refreshAction, Func<bool>? canRefresh = null)
|
||||||
|
{
|
||||||
|
_projectRefreshAction = refreshAction;
|
||||||
|
_canRefreshProjects = canRefresh;
|
||||||
|
}
|
||||||
|
|
||||||
public void NotifyDraftChanged()
|
public void NotifyDraftChanged()
|
||||||
{
|
{
|
||||||
ApplyDaqChannelEditorsToDraft();
|
ApplyDaqChannelEditorsToDraft();
|
||||||
@ -154,17 +166,29 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
private void Close() => _closeAction?.Invoke();
|
private void Close() => _closeAction?.Invoke();
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task SaveSettings()
|
private async Task SaveSettings() =>
|
||||||
|
await SaveSettingsCore(logSaved: true);
|
||||||
|
|
||||||
|
private async Task SaveSettingsCore(bool logSaved)
|
||||||
{
|
{
|
||||||
ApplyDaqChannelEditorsToDraft();
|
ApplyDaqChannelEditorsToDraft();
|
||||||
Devices.Daq.Channels = DaqChannelOptionsNormalizer.Normalize(Devices.Daq.Channels);
|
Devices.Daq.Channels = DaqChannelOptionsNormalizer.Normalize(Devices.Daq.Channels);
|
||||||
ApplyToRuntime(Devices, _runtimeDevices);
|
ApplyToRuntime(Devices, _runtimeDevices);
|
||||||
ApplyToRuntime(Storage, _runtimeStorage);
|
ApplyToRuntime(Storage, _runtimeStorage);
|
||||||
ApplyToRuntime(Report, _runtimeReport);
|
ApplyToRuntime(Report, _runtimeReport);
|
||||||
await _settingsStore.SaveAsync().ConfigureAwait(false);
|
ApplyProjectsRootToStore();
|
||||||
|
await _settingsStore.SaveAsync();
|
||||||
OnPropertyChanged(nameof(HasUnsavedChanges));
|
OnPropertyChanged(nameof(HasUnsavedChanges));
|
||||||
OnPropertyChanged(nameof(DaqValidationMessage));
|
OnPropertyChanged(nameof(DaqValidationMessage));
|
||||||
_log.Add(UiLogLevel.Success, "设置已保存。重新连接请单独点击“重新连接”。");
|
if (logSaved)
|
||||||
|
_log.Add(UiLogLevel.Success, "设置已保存。重新连接请单独点击“重新连接”。");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyProjectsRootToStore()
|
||||||
|
{
|
||||||
|
_projectStore.ProjectsRoot = _runtimeStorage.ProjectsRoot;
|
||||||
|
if (!string.IsNullOrWhiteSpace(_projectStore.ProjectsRoot))
|
||||||
|
Directory.CreateDirectory(_projectStore.ProjectsRoot);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
@ -350,6 +374,42 @@ public partial class SettingsViewModel : ObservableObject
|
|||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenProjectsDirectory() => OpenDirectory(Storage.ProjectsRoot);
|
private void OpenProjectsDirectory() => OpenDirectory(Storage.ProjectsRoot);
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task RefreshProjects()
|
||||||
|
{
|
||||||
|
NotifyDraftChanged();
|
||||||
|
if (string.IsNullOrWhiteSpace(Storage.ProjectsRoot))
|
||||||
|
{
|
||||||
|
_log.Add(UiLogLevel.Error, "项目数据路径为空,无法刷新工程。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_canRefreshProjects?.Invoke() == false)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_projectRefreshAction == null)
|
||||||
|
{
|
||||||
|
_log.Add(UiLogLevel.Warning, "刷新工程功能尚未初始化。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await SaveSettingsCore(logSaved: false);
|
||||||
|
await _projectRefreshAction(_projectStore.ProjectsRoot);
|
||||||
|
await RunOnUiAsync(() =>
|
||||||
|
{
|
||||||
|
OnPropertyChanged(nameof(HasUnsavedChanges));
|
||||||
|
OnPropertyChanged(nameof(DaqValidationMessage));
|
||||||
|
});
|
||||||
|
_log.Add(UiLogLevel.Success, "工程列表已刷新。");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Add(UiLogLevel.Error, $"刷新工程失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private void OpenReportsDirectory() => OpenDirectory(Report.SavePath);
|
private void OpenReportsDirectory() => OpenDirectory(Report.SavePath);
|
||||||
|
|
||||||
|
|||||||
@ -37,8 +37,48 @@
|
|||||||
FontSize="{StaticResource FsTitle}" FontWeight="Bold" />
|
FontSize="{StaticResource FsTitle}" FontWeight="Bold" />
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
<StackPanel Orientation="Horizontal" Grid.Column="2" VerticalAlignment="Center">
|
<StackPanel Orientation="Horizontal" Grid.Column="2" VerticalAlignment="Center">
|
||||||
<Viewbox Width="26" Height="26" Margin="0,0,6,0"
|
<Grid Width="48" Height="48" Margin="0,0,6,0"
|
||||||
ToolTip="{Binding SafetyDetailText}">
|
ToolTip="{Binding SafetyDetailText}">
|
||||||
|
<Ellipse x:Name="AlarmHalo" IsHitTestVisible="False"
|
||||||
|
RenderTransformOrigin="0.5,0.5">
|
||||||
|
<Ellipse.Fill>
|
||||||
|
<RadialGradientBrush Center="0.5,0.5" GradientOrigin="0.5,0.5" RadiusX="0.5" RadiusY="0.5">
|
||||||
|
<GradientStop Color="#66FF001A" Offset="0.0" />
|
||||||
|
<GradientStop Color="#99FF1030" Offset="0.30" />
|
||||||
|
<GradientStop Color="#44FF0018" Offset="0.60" />
|
||||||
|
<GradientStop Color="#00FF0018" Offset="1.0" />
|
||||||
|
</RadialGradientBrush>
|
||||||
|
</Ellipse.Fill>
|
||||||
|
<Ellipse.RenderTransform>
|
||||||
|
<ScaleTransform ScaleX="1" ScaleY="1" />
|
||||||
|
</Ellipse.RenderTransform>
|
||||||
|
<Ellipse.Style>
|
||||||
|
<Style TargetType="Ellipse">
|
||||||
|
<Setter Property="Opacity" Value="0" />
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
|
||||||
|
<Setter Property="Opacity" Value="1" />
|
||||||
|
<DataTrigger.EnterActions>
|
||||||
|
<BeginStoryboard x:Name="AlarmHaloPulse">
|
||||||
|
<Storyboard>
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="Opacity"
|
||||||
|
From="1.0" To="0.35" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleX)"
|
||||||
|
From="0.9" To="1.12" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
|
||||||
|
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(ScaleTransform.ScaleY)"
|
||||||
|
From="0.9" To="1.12" Duration="0:0:0.5" AutoReverse="True" RepeatBehavior="Forever" />
|
||||||
|
</Storyboard>
|
||||||
|
</BeginStoryboard>
|
||||||
|
</DataTrigger.EnterActions>
|
||||||
|
<DataTrigger.ExitActions>
|
||||||
|
<StopStoryboard BeginStoryboardName="AlarmHaloPulse" />
|
||||||
|
</DataTrigger.ExitActions>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Ellipse.Style>
|
||||||
|
</Ellipse>
|
||||||
|
<Viewbox Width="36" Height="36" HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
<Canvas Width="64" Height="64">
|
<Canvas Width="64" Height="64">
|
||||||
<Canvas.Resources>
|
<Canvas.Resources>
|
||||||
<RadialGradientBrush x:Key="AlarmRedGlassBrush" Center="0.34,0.16" GradientOrigin="0.34,0.16" RadiusX="0.78" RadiusY="0.78">
|
<RadialGradientBrush x:Key="AlarmRedGlassBrush" Center="0.34,0.16" GradientOrigin="0.34,0.16" RadiusX="0.78" RadiusY="0.78">
|
||||||
@ -106,33 +146,38 @@
|
|||||||
</Style>
|
</Style>
|
||||||
</Path.Style>
|
</Path.Style>
|
||||||
</Path>
|
</Path>
|
||||||
<Path x:Name="AlarmSweep" Data="M17 6h8l-11 42h-8z"
|
<Canvas Width="64" Height="64" IsHitTestVisible="False">
|
||||||
Fill="White">
|
<Canvas.Clip>
|
||||||
<Path.RenderTransform>
|
<PathGeometry Figures="M14 44V23C14 10.5 22.1 4 32 4s18 6.5 18 19v21z" />
|
||||||
<TranslateTransform />
|
</Canvas.Clip>
|
||||||
</Path.RenderTransform>
|
<Path x:Name="AlarmSweep" Data="M17 6h8l-11 42h-8z"
|
||||||
<Path.Style>
|
Fill="White">
|
||||||
<Style TargetType="Path">
|
<Path.RenderTransform>
|
||||||
<Setter Property="Opacity" Value="0" />
|
<TranslateTransform />
|
||||||
<Style.Triggers>
|
</Path.RenderTransform>
|
||||||
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
|
<Path.Style>
|
||||||
<Setter Property="Opacity" Value=".65" />
|
<Style TargetType="Path">
|
||||||
<DataTrigger.EnterActions>
|
<Setter Property="Opacity" Value="0" />
|
||||||
<BeginStoryboard x:Name="AlarmSweepMove">
|
<Style.Triggers>
|
||||||
<Storyboard>
|
<DataTrigger Binding="{Binding AlarmLampActive}" Value="True">
|
||||||
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
|
<Setter Property="Opacity" Value=".65" />
|
||||||
From="-24" To="30" Duration="0:0:0.95" RepeatBehavior="Forever" />
|
<DataTrigger.EnterActions>
|
||||||
</Storyboard>
|
<BeginStoryboard x:Name="AlarmSweepMove">
|
||||||
</BeginStoryboard>
|
<Storyboard>
|
||||||
</DataTrigger.EnterActions>
|
<DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(TranslateTransform.X)"
|
||||||
<DataTrigger.ExitActions>
|
From="-24" To="30" Duration="0:0:0.95" RepeatBehavior="Forever" />
|
||||||
<StopStoryboard BeginStoryboardName="AlarmSweepMove" />
|
</Storyboard>
|
||||||
</DataTrigger.ExitActions>
|
</BeginStoryboard>
|
||||||
</DataTrigger>
|
</DataTrigger.EnterActions>
|
||||||
</Style.Triggers>
|
<DataTrigger.ExitActions>
|
||||||
</Style>
|
<StopStoryboard BeginStoryboardName="AlarmSweepMove" />
|
||||||
</Path.Style>
|
</DataTrigger.ExitActions>
|
||||||
</Path>
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Path.Style>
|
||||||
|
</Path>
|
||||||
|
</Canvas>
|
||||||
<Path Data="M25 13l7-3 3 6-8 3zM38 11c4 1 6 3 8 7l-5 2c-1-3-3-4-6-5z"
|
<Path Data="M25 13l7-3 3 6-8 3zM38 11c4 1 6 3 8 7l-5 2c-1-3-3-4-6-5z"
|
||||||
Fill="White" Opacity=".72" />
|
Fill="White" Opacity=".72" />
|
||||||
<Ellipse Canvas.Left="10" Canvas.Top="39" Width="44" Height="10" Fill="#771A0000" />
|
<Ellipse Canvas.Left="10" Canvas.Top="39" Width="44" Height="10" Fill="#771A0000" />
|
||||||
@ -167,19 +212,53 @@
|
|||||||
<Path Data="M42 56v4M47 55v4M52 54v3" Stroke="#050505" StrokeThickness="2" StrokeStartLineCap="Round" StrokeEndLineCap="Round" />
|
<Path Data="M42 56v4M47 55v4M52 54v3" Stroke="#050505" StrokeThickness="2" StrokeStartLineCap="Round" StrokeEndLineCap="Round" />
|
||||||
</Canvas>
|
</Canvas>
|
||||||
</Viewbox>
|
</Viewbox>
|
||||||
|
</Grid>
|
||||||
<Button Margin="0,0,8,0" Padding="12,4"
|
<Button Margin="0,0,8,0" Padding="12,4"
|
||||||
Command="{Binding ToggleEmergencyAlarmCommand}"
|
Command="{Binding ToggleEmergencyAlarmCommand}"
|
||||||
IsEnabled="{Binding EmergencyOperationBusy, Converter={StaticResource InverseBoolConverter}}"
|
IsEnabled="{Binding EmergencyOperationBusy, Converter={StaticResource InverseBoolConverter}}"
|
||||||
ToolTip="{Binding SafetyDetailText}">
|
ToolTip="{Binding SafetyDetailText}">
|
||||||
<Button.Style>
|
<Button.Style>
|
||||||
<Style TargetType="Button" BasedOn="{StaticResource DangerButton}">
|
<Style TargetType="Button" BasedOn="{StaticResource BaseButton}">
|
||||||
<Setter Property="Content" Value="{Binding EmergencyButtonText}" />
|
<Setter Property="Content" Value="{Binding EmergencyButtonText}" />
|
||||||
<Style.Triggers>
|
<Setter Property="Background" Value="{StaticResource DangerBrush}" />
|
||||||
<DataTrigger Binding="{Binding AlarmLampActive}" Value="False">
|
<Setter Property="BorderBrush" Value="{StaticResource DangerBrush}" />
|
||||||
<Setter Property="Background" Value="{StaticResource WarningBrush}" />
|
<Setter Property="Foreground" Value="{StaticResource TextInverseBrush}" />
|
||||||
<Setter Property="Foreground" Value="White" />
|
<Setter Property="FontWeight" Value="SemiBold" />
|
||||||
</DataTrigger>
|
<Setter Property="Template">
|
||||||
</Style.Triggers>
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="EmergencyButtonChrome"
|
||||||
|
Background="{TemplateBinding Background}"
|
||||||
|
BorderBrush="{TemplateBinding BorderBrush}"
|
||||||
|
BorderThickness="{TemplateBinding BorderThickness}"
|
||||||
|
CornerRadius="{StaticResource RadiusMd}"
|
||||||
|
SnapsToDevicePixels="True">
|
||||||
|
<TextBlock x:Name="EmergencyButtonText"
|
||||||
|
Text="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
Foreground="{Binding Foreground, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
FontFamily="{Binding FontFamily, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
FontSize="{Binding FontSize, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
FontWeight="{Binding FontWeight, RelativeSource={RelativeSource TemplatedParent}}"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="{TemplateBinding Padding}" />
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="EmergencyButtonChrome" Property="Background" Value="#EAF3FC" />
|
||||||
|
<Setter TargetName="EmergencyButtonChrome" Property="BorderBrush" Value="{StaticResource DangerBrush}" />
|
||||||
|
<Setter Property="Foreground" Value="#000000" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsPressed" Value="True">
|
||||||
|
<Setter TargetName="EmergencyButtonChrome" Property="Opacity" Value="0.9" />
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter Property="Opacity" Value="0.5" />
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
</Button.Style>
|
</Button.Style>
|
||||||
</Button>
|
</Button>
|
||||||
@ -273,18 +352,6 @@
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|
||||||
<Button DockPanel.Dock="Bottom" Margin="12,8" Height="30" HorizontalContentAlignment="Left"
|
|
||||||
Padding="10,0" Command="{Binding OpenProjectsFolderCommand}">
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<Viewbox Width="14" Height="14" Margin="0,0,6,0">
|
|
||||||
<Canvas Width="24" Height="24">
|
|
||||||
<Path Data="{StaticResource FolderGeometry}" Fill="{StaticResource TextSecondaryBrush}" />
|
|
||||||
</Canvas>
|
|
||||||
</Viewbox>
|
|
||||||
<TextBlock Text="打开项目文件夹" />
|
|
||||||
</StackPanel>
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<TreeView x:Name="ProjectTree" ItemsSource="{Binding TreeRoots}"
|
<TreeView x:Name="ProjectTree" ItemsSource="{Binding TreeRoots}"
|
||||||
MouseDoubleClick="ProjectTree_MouseDoubleClick" Padding="6,6">
|
MouseDoubleClick="ProjectTree_MouseDoubleClick" Padding="6,6">
|
||||||
<TreeView.Resources>
|
<TreeView.Resources>
|
||||||
|
|||||||
@ -277,12 +277,12 @@
|
|||||||
<ColumnDefinition Width="70" />
|
<ColumnDefinition Width="70" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="70" />
|
<ColumnDefinition Width="70" />
|
||||||
<ColumnDefinition Width="80" />
|
<ColumnDefinition Width="92" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Text="根目录" Style="{StaticResource FieldLabel}" />
|
<TextBlock Text="根目录" Style="{StaticResource FieldLabel}" />
|
||||||
<TextBox Grid.Column="1" Text="{Binding Storage.ProjectsRoot, UpdateSourceTrigger=PropertyChanged}" TextChanged="DraftChanged" FontFamily="{StaticResource FontMono}" />
|
<TextBox Grid.Column="1" Text="{Binding Storage.ProjectsRoot, UpdateSourceTrigger=PropertyChanged}" TextChanged="DraftChanged" FontFamily="{StaticResource FontMono}" />
|
||||||
<Button Grid.Column="2" Margin="8,0,0,0" Content="浏览" Command="{Binding BrowseProjectsCommand}" />
|
<Button Grid.Column="2" Margin="8,0,0,0" Content="设置" Command="{Binding BrowseProjectsCommand}" />
|
||||||
<Button Grid.Column="3" Margin="8,0,0,0" Content="打开" Command="{Binding OpenProjectsDirectoryCommand}" />
|
<Button Grid.Column="3" Margin="8,0,0,0" Content="刷新工程" Command="{Binding RefreshProjectsCommand}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user