202 lines
7.9 KiB
C#
202 lines
7.9 KiB
C#
using System.Collections.ObjectModel;
|
|
using System.ServiceProcess;
|
|
using System.Windows;
|
|
using System.Windows.Media;
|
|
using JJServiceManager.Remote;
|
|
|
|
namespace JJServiceManager
|
|
{
|
|
/// <summary>
|
|
/// 초기화면의 PC 하나 (내 PC 또는 관리 대상 PC)와 그 PC의 서비스 목록
|
|
/// </summary>
|
|
public sealed class HostGroup : ObservableObject
|
|
{
|
|
public const string LocalId = "local";
|
|
|
|
private string _title = "";
|
|
private string _statusText = "";
|
|
private Brush _statusBrush = Brushes2.Unknown;
|
|
private bool _isExpanded = true;
|
|
private string _emptyText = "";
|
|
|
|
/// <summary>"local" 또는 대상 PC Id</summary>
|
|
public string Id { get; init; } = LocalId;
|
|
public bool IsLocal => Id == LocalId;
|
|
|
|
/// <summary>원격 PC일 때 접속</summary>
|
|
public RemoteClient Client { get; init; }
|
|
|
|
public ObservableCollection<ServiceRow> Rows { get; } = new ObservableCollection<ServiceRow>();
|
|
|
|
/// <summary>제목: "내 PC (HOSTNAME)" 또는 "192.168.0.10 / SERVER01"</summary>
|
|
public string Title { get => _title; set => Set(ref _title, value); }
|
|
|
|
/// <summary>서비스 앞에 붙는 표시 (원격: "IP/hostname")</summary>
|
|
public string Prefix => IsLocal ? "" : Title.Replace(" / ", "/");
|
|
|
|
public string StatusText { get => _statusText; set => Set(ref _statusText, value); }
|
|
public Brush StatusBrush { get => _statusBrush; set => Set(ref _statusBrush, value); }
|
|
public bool IsExpanded { get => _isExpanded; set => Set(ref _isExpanded, value); }
|
|
|
|
public string EmptyText
|
|
{
|
|
get => _emptyText;
|
|
set { if (Set(ref _emptyText, value)) OnPropertyChanged(nameof(EmptyVisibility)); }
|
|
}
|
|
|
|
public Visibility EmptyVisibility => Rows.Count == 0 && !string.IsNullOrEmpty(_emptyText) ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
/// <summary>마지막으로 기록한 연결 실패 메시지 (같은 메시지 반복 기록 방지)</summary>
|
|
public string Tag { get; set; }
|
|
|
|
/// <summary>원격 PC 제목 줄의 [원격관리] 버튼</summary>
|
|
public Visibility RemoteButtonVisibility => IsLocal ? Visibility.Collapsed : Visibility.Visible;
|
|
|
|
public Brush HeaderBrush => IsLocal ? Brushes2.Make("#E3F2FD") : Brushes2.Make("#F3E5F5");
|
|
|
|
public HostGroup()
|
|
{
|
|
Rows.CollectionChanged += (s, e) => OnPropertyChanged(nameof(EmptyVisibility));
|
|
}
|
|
|
|
public void TitleChanged()
|
|
{
|
|
OnPropertyChanged(nameof(Prefix));
|
|
foreach (var r in Rows) r.HostChanged();
|
|
}
|
|
|
|
public override string ToString() => Title;
|
|
}
|
|
|
|
/// <summary>초기화면 서비스 목록의 한 줄</summary>
|
|
public sealed class ServiceRow : ObservableObject
|
|
{
|
|
private bool _isChecked;
|
|
private ServiceControllerStatus? _status;
|
|
private bool _statusKnown;
|
|
private bool _isBusy;
|
|
|
|
public ServiceEntry Entry { get; }
|
|
public HostGroup Host { get; }
|
|
public bool IsRemote => Host != null && !Host.IsLocal;
|
|
|
|
public ServiceRow(ServiceEntry entry, HostGroup host)
|
|
{
|
|
Entry = entry;
|
|
Host = host;
|
|
}
|
|
|
|
public string DisplayName => string.IsNullOrEmpty(Entry.DisplayName) ? Entry.ServiceName : Entry.DisplayName;
|
|
public string ServiceName => Entry.ServiceName;
|
|
public string ServiceNameText => string.Equals(Entry.DisplayName, Entry.ServiceName, System.StringComparison.OrdinalIgnoreCase) ? "" : $"({Entry.ServiceName})";
|
|
|
|
/// <summary>서비스 앞에 붙는 "IP/hostname" (내 PC는 없음)</summary>
|
|
public string HostPrefix => IsRemote ? Host.Prefix : "";
|
|
public Visibility HostPrefixVisibility => IsRemote ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
/// <summary>로그에 쓰는 이름: "[IP/hostname] [서비스]" 또는 "[서비스]"</summary>
|
|
public string LogName => IsRemote ? $"[{Host.Prefix}] [{DisplayName}]" : $"[{DisplayName}]";
|
|
|
|
public string Tooltip => IsRemote
|
|
? $"대상 PC: {Host.Title}\n서비스명: {Entry.ServiceName}"
|
|
: $"서비스명: {Entry.ServiceName}\n우클릭: 로그 경로 지정";
|
|
|
|
public bool IsChecked { get => _isChecked; set => Set(ref _isChecked, value); }
|
|
|
|
/// <summary>Start/Stop 진행 중이면 false</summary>
|
|
public bool IsIdle => !_isBusy;
|
|
public bool IsBusy
|
|
{
|
|
get => _isBusy;
|
|
set { if (Set(ref _isBusy, value)) OnPropertyChanged(nameof(IsIdle)); }
|
|
}
|
|
|
|
public ServiceControllerStatus? Status
|
|
{
|
|
get => _status;
|
|
set
|
|
{
|
|
if (_statusKnown && _status == value) return;
|
|
_status = value;
|
|
_statusKnown = true;
|
|
OnPropertyChanged();
|
|
OnPropertyChanged(nameof(StatusText));
|
|
OnPropertyChanged(nameof(StatusBrush));
|
|
}
|
|
}
|
|
|
|
public bool StatusKnown => _statusKnown;
|
|
|
|
/// <summary>원격 PC 연결이 끊긴 경우 등 상태를 모를 때</summary>
|
|
public void MarkUnknown()
|
|
{
|
|
if (!_statusKnown) return;
|
|
_statusKnown = false;
|
|
OnPropertyChanged(nameof(Status));
|
|
OnPropertyChanged(nameof(StatusText));
|
|
OnPropertyChanged(nameof(StatusBrush));
|
|
}
|
|
|
|
public string StatusText => _statusKnown ? ServiceOps.StatusText(_status) : (IsRemote ? "연결 안 됨" : "확인 중…");
|
|
|
|
public Brush StatusBrush => !_statusKnown ? Brushes2.Unknown : _status switch
|
|
{
|
|
ServiceControllerStatus.Running => Brushes2.Running,
|
|
ServiceControllerStatus.Stopped => Brushes2.Stopped,
|
|
null => Brushes2.Unknown,
|
|
ServiceControllerStatus.Paused => Brushes2.Unknown,
|
|
_ => Brushes2.Pending
|
|
};
|
|
|
|
public bool HasLog => !string.IsNullOrWhiteSpace(Entry.LogPath);
|
|
|
|
public string LogTooltip
|
|
{
|
|
get
|
|
{
|
|
if (IsRemote)
|
|
return HasLog
|
|
? "대상 PC 로그: " + (Entry.LogIsFolder ? "폴더(최신 파일) " : "") + Entry.LogPath
|
|
: "대상 PC에서 로그 경로가 지정되지 않았습니다.";
|
|
return HasLog
|
|
? (Entry.LogIsFolder ? "폴더(최신 파일): " : "파일: ") + Entry.LogPath + "\n우클릭: 변경"
|
|
: "로그 경로가 없습니다. 누르면 지정합니다. (우클릭: 변경)";
|
|
}
|
|
}
|
|
|
|
public FontWeight LogFontWeight => HasLog ? FontWeights.Bold : FontWeights.Normal;
|
|
|
|
public void LogPathChanged()
|
|
{
|
|
OnPropertyChanged(nameof(HasLog));
|
|
OnPropertyChanged(nameof(LogTooltip));
|
|
OnPropertyChanged(nameof(LogFontWeight));
|
|
}
|
|
|
|
public void HostChanged()
|
|
{
|
|
OnPropertyChanged(nameof(HostPrefix));
|
|
OnPropertyChanged(nameof(Tooltip));
|
|
OnPropertyChanged(nameof(LogName));
|
|
}
|
|
|
|
/// <summary>원격 목록을 새로 받았을 때 이름/로그 경로 갱신</summary>
|
|
public void UpdateFromRemote(RemoteServiceInfo info)
|
|
{
|
|
bool nameChanged = Entry.DisplayName != info.DisplayName;
|
|
bool logChanged = Entry.LogPath != info.LogPath || Entry.LogIsFolder != info.LogIsFolder;
|
|
Entry.DisplayName = info.DisplayName;
|
|
Entry.LogPath = info.LogPath;
|
|
Entry.LogIsFolder = info.LogIsFolder;
|
|
if (nameChanged)
|
|
{
|
|
OnPropertyChanged(nameof(DisplayName));
|
|
OnPropertyChanged(nameof(ServiceNameText));
|
|
OnPropertyChanged(nameof(LogName));
|
|
}
|
|
if (logChanged) LogPathChanged();
|
|
Status = info.Status.HasValue ? (ServiceControllerStatus?)info.Status.Value : null;
|
|
}
|
|
}
|
|
}
|