public void PushContent(MessageDocument doc) { _contents.Add(new DisplayContent { Document = doc }); // 变化按钮文字 RefreshButtonText(); }
private void Message_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ChangedButton == MouseButton.Right) { Clipboard.SetDataObject(this.message.Text, true); } if (e.ChangedButton == MouseButton.Left) { // 测试功能 MessageDocument doc = new MessageDocument(); DateTime now = DateTime.Now; doc.Add(new Operator { PatronName = "姓名" }, now, "borrow", "succeed", "", "", new Entity { Title = "书名1" }); doc.Add(new Operator { PatronName = "姓名" }, now, "borrow", "succeed", "", "", new Entity { Title = "书名2" }); doc.Add(new Operator { PatronName = "姓名" }, now, "return", "warning", "这是警告信息", "", new Entity { Title = "书名3" }); doc.Add(new Operator { PatronName = "姓名" }, now, "return", "error", "还书出错", "errorCode", new Entity { Title = "书名4" }); ProgressWindow progress = null; App.Invoke(new Action(() => { progress = new ProgressWindow(); // progress.MessageText = "正在处理,请稍候 ..."; progress.MessageDocument = doc.BuildDocument( MessageDocument.BaseFontSize, "", out string speak); progress.Owner = Application.Current.MainWindow; progress.WindowStartupLocation = WindowStartupLocation.CenterOwner; //progress.Closed += Progress_Closed; App.SetSize(progress, "wide"); //progress.Width = Math.Min(700, this.ActualWidth); //progress.Height = Math.Min(500, this.ActualHeight); progress.Show(); //AddLayer(); })); } }
// 启动同步任务。此任务长期在后台运行 public static void StartSyncTask() { if (_syncTask != null) { return; } CancellationToken token = _cancel.Token; token.Register(() => { _eventSync.Set(); }); // 启动重试专用线程 _syncTask = Task.Factory.StartNew(async() => { WpfClientInfo.WriteInfoLog("重试专用线程开始"); try { while (token.IsCancellationRequested == false) { // TODO: 无论是整体退出,还是需要激活,都需要能中断 Delay // Task.Delay(TimeSpan.FromSeconds(10)).Wait(token); _eventSync.WaitOne(_syncIdleLength); token.ThrowIfCancellationRequested(); #if REMOVED // 顺便检查和确保连接到消息服务器 App.CurrentApp.EnsureConnectMessageServer().Wait(token); #endif #if REMOVED // 顺便关闭天线射频 if (_tagAdded) { _ = Task.Run(async() => { try { await SelectAntennaAsync(); } catch { // TODO: 写入错误日志 } }); _tagAdded = false; } #endif if (PauseSubmit) { continue; } // 2020/6/21 if (ShelfData.LibraryNetworkCondition != "OK") { continue; } // TODO: 从本地数据库中装载需要同步的那些 Actions List <ActionInfo> actions = await LoadRetryActionsFromDatabaseAsync(); if (actions.Count == 0) { continue; } // RefreshRetryInfo() ??? // 一般来说,只要 SubmitWindow 开着,就要显示请求情况结果。 // 特殊地,如果 SubmitWindow 没有开着,但本次至少有一个成功的请求结果了,那就专门打开 SubmitWindow 显示信息 int succeedCount = 0; // 同步成功的事项数量 int newCount = 0; // 首次进行同步的事项数量 // 排序和分组。按照分组提交给 dp2library 服务器 // TODO: 但进度显示不应该太细碎?应该按照总的进度来显示 var groups = GroupActions(actions); // List<MessageItem> messages = new List<MessageItem>(); // 准备对话框 // SubmitWindow progress = PageMenu.PageShelf?.OpenProgressWindow(); SubmitWindow progress = PageMenu.PageShelf?.ProgressWindow; int start = 0; // 当前 group 开始的偏移 int total = actions.Count; foreach (var group in groups) { int current_count = group.Count; // 当前 group 包含的动作数量 var result = await SubmitCheckInOutAsync( (min, max, value, text) => { // 2020/4/2 // 修正三个值 if (max != -1) { max = total; } //if (min != -1) // min += start; if (value != -1) { value += start; } if (progress != null) { App.Invoke(new Action(() => { if (min == -1 && max == -1 && value == -1 && groups.IndexOf(group) == groups.Count - 1) // 只有最后一次才隐藏进度条 { progress.ProgressBar.Visibility = Visibility.Collapsed; } else { progress.ProgressBar.Visibility = Visibility.Visible; } if (text != null) { progress.TitleText = text; // + " " + (progress.Tag as string); } if (min != -1) { progress.ProgressBar.Minimum = min; } if (max != -1) { progress.ProgressBar.Maximum = max; } if (value != -1) { progress.ProgressBar.Value = value; } })); } }, group, "auto_stop"); // TODO: 把 group 中报错的信息写入本地数据库的对应事项中 /* * // 把已经处理成功的 Action 对应在本地数据库中的事项的状态修改 * List<ActionInfo> processed = new List<ActionInfo>(); * if (result.RetryActions != null) * { * foreach (var action in group) * { * if (result.RetryActions.IndexOf(action) == -1) * { * ChangeDatabaseActionState(action.ID, "sync"); * processed.Add(action); * } * } * } */ if (result.ProcessedActions != null) { // result.ProcessedActions.ForEach(o => { if (o.SyncCount == 0) newCount++; }); foreach (var action in result.ProcessedActions) { if (action.State == "sync") { succeedCount++; } if (action.SyncCount == 1) { newCount++; } // sync/commerror/normalerror/空 // 同步成功/通讯出错/一般出错/从未同步过 await ChangeDatabaseActionStateAsync(action.ID, action); } // TODO: 通知消息正文是否也告知一下同一个 PII 后面有多少个动作被跳过处理? MessageNotifyOverflow(result.ProcessedActions); } if (progress != null && progress.IsVisible) { // 根据全部和已处理集合得到未处理(被跳过的)集合 var skipped = GetSkippedActions(group, result.ProcessedActions); foreach (var action in skipped) { action.State = "_"; action.SyncErrorCode = "skipped"; // action.SyncErrorInfo = "暂时跳过同步"; } List <ActionInfo> display = new List <ActionInfo>(result.ProcessedActions); display.AddRange(skipped); // Thread.Sleep(3000); // 刷新显示 App.Invoke(new Action(() => { progress?.Refresh(display); })); } /* * if (result.MessageDocument != null) * messages.AddRange(result.MessageDocument.Items); */ start += current_count; } // TODO: 更新每个事项的 RetryCount。如果超过 10 次,要把 State 更新为 fail // 将 submit 情况写入日志备查 // WpfClientInfo.WriteInfoLog($"重试提交请求:\r\n{ActionInfo.ToString(actions)}\r\n返回结果:{result.ToString()}"); #if REMOVED // 如果本轮有成功的请求,并且进度窗口没有打开,则补充打开进度窗口 if ((progress == null || progress.IsVisible == false) && succeedCount > 0) { progress = PageMenu.PageShelf?.OpenProgressWindow(); } // 把执行结果显示到对话框内 // 全部事项都重试失败的时候不需要显示 if (progress != null && progress.IsVisible && (succeedCount > 0 || newCount > 0)) { Application.Current.Dispatcher.Invoke(new Action(() => { MessageDocument doc = new MessageDocument(); doc.AddRange(messages); progress?.PushContent(doc); })); // 显示出来 Application.Current.Dispatcher.Invoke(new Action(() => { progress?.ShowContent(); })); } #endif } _syncTask = null; } catch (OperationCanceledException) { } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"重试专用线程出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("sync", $"重试专用线程出现异常: {ex.Message}"); } finally { WpfClientInfo.WriteInfoLog("重试专用线程结束"); } }, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); }
public Paragraph BuildParagraph(int index, double baseFontSize, string style) { bool display_transfer = StringUtil.IsInList("transfer", style); if (Operation == "transfer" && display_transfer == false) { return(null); } var p = new Paragraph(); p.FontFamily = new FontFamily("微软雅黑"); p.FontSize = baseFontSize; // p.FontStyle = FontStyles.Italic; p.TextAlignment = TextAlignment.Left; p.Foreground = Brushes.Gray; // p.LineHeight = 18; p.TextIndent = -20; p.Margin = new Thickness(10, 0, 0, 8); // 10,0,0,8 p.Tag = this; // 记忆下来后面隐藏事项的时候可以用到 // 序号 p.Inlines.Add(new Run($"{(index + 1).ToString()}) ")); Brush back = Brushes.Transparent; // 成功和失败状态 if (ResultType == "error") { back = Brushes.DarkRed; p.Inlines.Add(new Run { Text = " 失败 ", Background = back, Foreground = Brushes.White }); } else if (ResultType == "warning") { back = Brushes.DarkGoldenrod; p.Inlines.Add(new Run { Text = " 警告 ", Background = back, Foreground = Brushes.White }); } else if (ResultType == "information") { back = Brushes.Gray; p.Inlines.Add(new Run { Text = " 信息 ", Background = back, Foreground = Brushes.White }); } else { back = Brushes.DarkGreen; p.Inlines.Add(new Run { Text = " 成功 ", Background = back, Foreground = Brushes.White }); } // 操作名称 p.Inlines.Add(new Run { Text = GetOperationCaption(Operation) + " ", Foreground = Brushes.White }); // 转移方向 if (Operation == "transfer" && string.IsNullOrEmpty(Direction) == false) { p.Inlines.Add(new Run { Text = GetOperationCaption(Direction) + " ", Foreground = Brushes.White }); } // 书目摘要 if (Entity != null && string.IsNullOrEmpty(Entity.Title) == false) { Run run = new Run(MessageDocument.ShortTitle(Entity.Title)); /* * run.FontSize = 14; * run.FontStyle = FontStyles.Normal; * run.Background = Brushes.DarkRed; * run.Foreground = Brushes.White; */ p.Inlines.Add(run); } // 错误码和错误信息 if (string.IsNullOrEmpty(ErrorInfo) == false) { p.Inlines.Add(new Run { Text = "\r\n" + ErrorInfo, Background = back, Foreground = Brushes.White }); } return(p); }
public static Paragraph BuildParagraph( ActionInfo action, int index, double baseFontSize, string style) { // 是否显示 transfer (in) 条目。注意,即便 false, 也要显示 transfer (out) 条目的 bool display_transfer = StringUtil.IsInList("transfer", style); if (action.Action.StartsWith("transfer") && action.TransferDirection == "in" && display_transfer == false) { return(null); } var p = new Paragraph(); p.FontFamily = new FontFamily("微软雅黑"); p.FontSize = baseFontSize; // p.FontStyle = FontStyles.Italic; p.TextAlignment = TextAlignment.Left; p.Foreground = Brushes.Gray; // p.LineHeight = 18; p.TextIndent = -20; p.Margin = new Thickness(10, 0, 0, 8); // 10,0,0,8 p.Tag = new ParagraphInfo { Action = action, Index = index }; // 记忆下来后面刷新事项的时候可以用到 // 序号 p.Inlines.Add(new Run($"{(index + 1).ToString()}) ")); Brush back = Brushes.Transparent; // 状态 { // 等待动画 if (string.IsNullOrEmpty(action.State)) { var image = new FontAwesome.WPF.ImageAwesome(); image.Icon = FontAwesome.WPF.FontAwesomeIcon.Spinner; image.Spin = true; image.SpinDuration = 5; image.Height = baseFontSize * 2.0; image.Foreground = Brushes.DarkGray; var container = new InlineUIContainer(image); container.Name = "image_id"; p.Inlines.Add(container); } else if (action.SyncErrorCode == "overflow") { back = Brushes.DarkRed; p.Inlines.Add(new Run { Text = " 超额 ", Background = back, Foreground = Brushes.White }); } else if (action.State == "sync") { back = Brushes.DarkGreen; p.Inlines.Add(new Run { Text = " 成功 ", Background = back, Foreground = Brushes.White }); } else if (action.SyncErrorCode == "skipped") { back = Brushes.DeepSkyBlue; p.Inlines.Add(new Run { Text = $" 暂时跳过同步 ", Background = back, Foreground = Brushes.White }); } else if (action.State == "commerror" || action.State == "normalerror") { if (ShelfData.LibraryNetworkCondition == "Bad") { back = Brushes.DeepSkyBlue; } else { back = Brushes.DarkRed; } p.Inlines.Add(new Run { Text = $" 同步失败({action.State}) ", Background = back, Foreground = Brushes.White }); } else if (action.State == "dontsync") { back = Brushes.DarkBlue; p.Inlines.Add(new Run { Text = $" 不再同步 ", Background = back, Foreground = Brushes.White }); } else { back = Brushes.DarkRed; p.Inlines.Add(new Run { Text = $" {action.State} ", Background = back, Foreground = Brushes.White }); } } // 转移方向 if (action.Action.StartsWith("transfer") /*&& string.IsNullOrEmpty(action.TransferDirection) == false*/) { p.Inlines.Add(new Run { Text = GetTransferDirCaption(action.TransferDirection, action.Location) + " ", Foreground = Brushes.White }); } else { // 操作名称 p.Inlines.Add(new Run { Text = GetOperationCaption(action.Action) + " ", Foreground = Brushes.White }); } string title = ""; if (action.Entity != null) { title = MessageDocument.ShortTitle(action.Entity.Title); // 2020/5/6 // 尝试从本地缓存中获取书目摘要 if (string.IsNullOrEmpty(title)) { title = LibraryChannelUtil.GetBiblioSummaryFromLocal(action.Entity.GetOiPii(true)); } if (string.IsNullOrEmpty(title)) { title = ShelfData.GetPiiString(action.Entity); } else { // 2020/7/22 title = $"[{ShelfData.GetPiiString(action.Entity)}] {title}"; } } else { title = "(action.Entity 为空)"; } // 书目摘要 if (string.IsNullOrEmpty(title) == false) { Run run = new Run(title); /* * run.FontSize = 14; * run.FontStyle = FontStyles.Normal; * run.Background = Brushes.DarkRed; * run.Foreground = Brushes.White; */ p.Inlines.Add(run); } // 对于上架/下架来说,还要补充显示一些细节信息:location 去向;和 currentLocation 去向 if (action.Action.StartsWith("transfer")) { List <string> details = new List <string>(); if (string.IsNullOrEmpty(action.Location) == false) { details.Add($"调拨到:{action.Location}"); } if (string.IsNullOrEmpty(action.CurrentShelfNo) == false) { details.Add($"新架位:{action.CurrentShelfNo}"); } p.Inlines.Add(new Run { Text = " " + StringUtil.MakePathList(details, " ") + " ", Foreground = Brushes.Green }); } // 错误码和错误信息 if (string.IsNullOrEmpty(action.SyncErrorInfo) == false && (action.State != "sync" || action.SyncErrorCode == "overflow")) { p.Inlines.Add(new Run { Text = "\r\n" + action.SyncErrorInfo, Background = back, Foreground = Brushes.White }); } return(p); }