/* * public void SetBooks(List<Entity> entities) * { * EntityCollection collection = new EntityCollection(); * foreach(var entity in entities) * { * // TODO: 是否克隆 Entity? * entity.Container = collection; * collection.Add(entity); * } * books.SetSource(collection); * } */ public void SetBooks(EntityCollection collection) { books.SetSource(collection); // 后台处理刷新 var task = ShelfData.FillBookFields(collection, _cancel.Token, false); }
/* * public void SetBooks(List<Entity> entities) * { * EntityCollection collection = new EntityCollection(); * foreach(var entity in entities) * { * // TODO: 是否克隆 Entity? * entity.Container = collection; * collection.Add(entity); * } * books.SetSource(collection); * } */ public void SetBooks(EntityCollection collection) { books.SetSource(collection); // 后台处理刷新 var task = ShelfData.FillBookFieldsAsync(collection, _cancel.Token, "refreshData" /*, false*/); }
// 将指定门的暂存的信息保存为 Action。但并不立即提交 public async static Task SaveDoorActions(DoorItem door, bool clearOperator) { var result = await ShelfData.SaveActions((entity) => { var results = DoorItem.FindDoors(ShelfData.Doors, entity.ReaderName, entity.Antenna); // TODO: 如果遇到 results.Count > 1 是否抛出异常? if (results.Count > 1) { WpfClientInfo.WriteErrorLog($"读卡器名 '{entity.ReaderName}' 天线编号 {entity.Antenna} 匹配上 {results.Count} 个门"); throw new Exception($"读卡器名 '{entity.ReaderName}' 天线编号 {entity.Antenna} 匹配上 {results.Count} 个门。请检查 shelf.xml 并修正配置此错误,确保只匹配一个门"); } if (results.IndexOf(door) != -1) { return(door.Operator); // return GetOperator(entity); } return(null); }); if (result.Value == -1) { SetGlobalError("save_actions", $"SaveDoorActions() 出错: {result.ErrorInfo}"); PageShelf.TrySetMessage(null, $"SaveDoorActions() 出错: {result.ErrorInfo}。这是一个严重错误,请管理员及时介入处理"); } else { SetGlobalError("save_actions", null); } // 2019/12/21 if (clearOperator == true && door.State == "close") { door.Operator = null; // 清掉门上的操作者名字 } }
// 从 _entityList 中取出一批事项进行处理。由于是复制出来处理的,所以整个处理过程中(除了取出和最后删除的瞬间)不用对 _entityList 加锁 // 对每一个事项,要进行如下处理: // 1) 获得册记录和书目摘要 // 2) 尝试请求还书 // 3) 请求设置 UID // 4) 修改 currentLocation 和 location static async Task ProcessingAsync() { var list = CopyList(); foreach (var state in list) { // state.State = "???"; try { if (state.NewState == "open") { continue; } Debug.Assert(state.NewState == "close"); List <ActionInfo> actions = null; try { DateTime start = DateTime.Now; var result = await ShelfData.RefreshInventoryAsync(state.Door); WpfClientInfo.WriteInfoLog($"针对门 {state.Door.Name} 执行 RefreshInventoryAsync() 耗时 {(DateTime.Now - start).TotalSeconds.ToString()}"); start = DateTime.Now; // 2020/4/21 把这两句移动到 try 范围内 await SaveDoorActions(state.Door, true); WpfClientInfo.WriteInfoLog($"针对门 {state.Door.Name} 执行 SaveDoorActions() 耗时 {(DateTime.Now - start).TotalSeconds.ToString()}"); start = DateTime.Now; actions = ShelfData.PullActions(); WpfClientInfo.WriteInfoLog($"针对门 {state.Door.Name} 执行 PullActions() 耗时 {(DateTime.Now - start).TotalSeconds.ToString()}"); } finally { state.Door.DecWaiting(); //WpfClientInfo.WriteInfoLog($"--decWaiting() door '{e.Door.Name}' state changed"); } // 注: 调用完成后门控件上的 +- 数字才会消失 var task = PageMenu.PageShelf.DoRequestAsync(actions, ""); App.SetError("processing", null); } catch (Exception ex) { WpfClientInfo.WriteErrorLog($"ProcessingAsync() 出现异常: {ExceptionUtil.GetDebugText(ex)}"); App.SetError("processing", $"ProcessingAsync() 出现异常: {ex.Message}"); } finally { // state.State = ""; } } // 把处理过的 entity 从 list 中移走 RemoveList(list); // 2020/11/21 // 如果发现队列里面又有新的对象,则立即激活任务 if (GetListCount() > 0) { ActivateTask(); } }
// return.Value: // -1 出错 // 0 读者记录没有找到 // 1 成功 public static GetReaderInfoResult GetReaderInfo(string pii) { /* * reader_xml = ""; * recpath = ""; * timestamp = null; */ if (string.IsNullOrEmpty(App.dp2ServerUrl) == true) { return new GetReaderInfoResult { Value = -1, ErrorInfo = "dp2library 服务器 URL 尚未配置,无法获得读者信息" } } ; LibraryChannel channel = App.CurrentApp.GetChannel(); var old_timeout = channel.Timeout; channel.Timeout = TimeSpan.FromSeconds(5); // 设置 5 秒超时,避免等待太久 try { int nRedoCount = 0; REDO: long lRet = channel.GetReaderInfo(null, pii, "advancexml", // "xml", out string[] results, out string recpath, out byte[] timestamp, out string strError); if (lRet == -1 || lRet == 0) { // 2020/4/24 增加一次重试机会 if (lRet == -1 && (channel.ErrorCode == ErrorCode.RequestCanceled || channel.ErrorCode == ErrorCode.RequestError) && nRedoCount < 2) { nRedoCount++; goto REDO; } // 如果发生通讯失败,则主动重新探测一次网络状况 if (channel.ErrorCode == ErrorCode.RequestCanceled || channel.ErrorCode == ErrorCode.RequestError) { ShelfData.DetectLibraryNetwork(); } return(new GetReaderInfoResult { Value = (int)lRet, ErrorInfo = strError, RecPath = recpath, Timestamp = timestamp }); } // 2019/12/19 // 命中读者记录多于一条 if (lRet > 1) { return(new GetReaderInfoResult { Value = -1, ErrorInfo = $"装载读者记录失败:'{pii}' 检索命中读者记录 {lRet} 条" }); } string reader_xml = ""; if (results != null && results.Length > 0) { reader_xml = results[0]; } return(new GetReaderInfoResult { Value = 1, RecPath = recpath, Timestamp = timestamp, ReaderXml = reader_xml }); } finally { channel.Timeout = old_timeout; App.CurrentApp.ReturnChannel(channel); } }
// 刷新显示 // 把 actions 中的对象的状态变化更新到当前文档中 // TODO: 一个办法是整个 Paragraph 替换。一个办法只替换里面的部分 InLine 对象 // parameters: // actions 发生了状态改变的 action。也就是被执行同步了的 action // skipped 被跳过了(没有处理的) action public void Refresh(List <ActionInfo> actions) { // 更新 _actions 中的对象 int count = 0; foreach (var action in actions) { var old_action = _actions.Where(o => o.ID == action.ID).FirstOrDefault(); if (old_action != null) { int index = _actions.IndexOf(old_action); Debug.Assert(index != -1, ""); if (index != -1) { _actions.RemoveAt(index); _actions.Insert(index, action); count++; } } } if (count == 0) { return; } { // 检查超额图书 List <string> overflow_titles = new List <string>(); _actions.ForEach(item => { if (item.Action == "borrow" && item.SyncErrorCode == "overflow") { overflow_titles.Add($"{ShortTitle(item.Entity.Title)} [{ShelfData.GetPiiString(item.Entity)}]"); } }); // 显示超额的信息 if (overflow_titles.Count > 0) { // 定位 Paragraph var block = this.Blocks.Where(o => { if (!(o.Tag is string id)) { return(false); } return(id == OVERFLOW_ID); }).FirstOrDefault(); // block 不应为 null。替代方法: if (block == null) { // TODO: 在适当位置插入标志段落 throw new Exception("#overflow 标志段落没有找到"); } var p = BuildOverflowParagraph(overflow_titles); this.Blocks.InsertBefore(block, p); this.Blocks.Remove(block); // 获得人名 List <string> names = new List <string>(); { actions.ForEach((o) => { if (o.Operator != null) { names.Add(string.IsNullOrEmpty(o.Operator.PatronName) ? o.Operator.PatronBarcode : o.Operator.PatronName); } }); StringUtil.RemoveDupNoSort(ref names); } // 语音提醒 // 针对同一个人,短时间内密集提醒要注意避免 if (HasOverflowSpeaked(StringUtil.MakePathList(names), TimeSpan.FromSeconds(30)) == false) { App.CurrentApp.SpeakSequence("警告:借书超额"); } } } foreach (var action in actions) { // 定位 Paragraph var block = this.Blocks.Where(o => { if (!(o.Tag is ParagraphInfo info)) { return(false); } return(info.Action.ID == action.ID); }) .FirstOrDefault(); if (block == null) { continue; } // 替换 Paragraph { if (!(block.Tag is ParagraphInfo old_info)) { continue; } var new_block = BuildParagraph(action, old_info.Index, this.BaseFontSize, this.BuildStyle); this.Blocks.InsertBefore(block, new_block); this.Blocks.Remove(block); } } }
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); }
public static SubmitDocument Build(List <ActionInfo> actions, double baseFontSize, string style) { SubmitDocument doc = new SubmitDocument(); doc.BaseFontSize = baseFontSize; doc.BuildStyle = style; // 保存起来 doc._actions.Clear(); doc._actions.AddRange(actions); bool display_transfer = StringUtil.IsInList("transfer", style); // 第一部分,总结信息 List <string> names = new List <string>(); { actions.ForEach((o) => { if (o.Operator != null) { names.Add(string.IsNullOrEmpty(o.Operator.PatronName) ? o.Operator.PatronBarcode : o.Operator.PatronName); } }); StringUtil.RemoveDupNoSort(ref names); } int return_count = actions.FindAll((o) => { return(o.Action == "return"); }).Count; int borrow_count = actions.FindAll((o) => { return(o.Action == "borrow"); }).Count; // 修改了 currentLocation 并且向内转移的数量 int change_currentLocation_count = actions .FindAll((o) => { return(o.Action.StartsWith("transfer") && string.IsNullOrEmpty(o.CurrentShelfNo) == false && o.TransferDirection == "in"); }) .Count; // 修改了 location 的数量。这个意味着发生了调拨 int change_location_count = actions .FindAll(o => { return(o.Action.StartsWith("transfer") && string.IsNullOrEmpty(o.Location) == false); }) .Count; // (工作人员)普通下架。特点是去向不明,但至少知道这些图书是离开书柜了 int transferout_count = actions .FindAll(o => { return(o.Action.StartsWith("transfer") && o.TransferDirection == "out"); }) .Count; // 总结一下涉及到的门 var door_names = ShelfData.GetDoorName(actions); /* * int succeed_count = actions.FindAll((o) => { return o.ResultType == "succeed" || string.IsNullOrEmpty(o.ResultType); }).Count; * int error_count = items.FindAll((o) => { return o.ResultType == "error"; }).Count; * int warning_count = items.FindAll((o) => { return o.ResultType == "warning"; }).Count; * int information_count = 0; * if (display_transfer == false) * information_count = items.FindAll((o) => { return o.ResultType == "information" && o.Operation != "transfer"; }).Count; * else * information_count = items.FindAll((o) => { return o.ResultType == "information"; }).Count; * * // 检查超额图书 * List<string> overflow_titles = new List<string>(); * items.ForEach(item => * { * if (item.Operation == "borrow" && item.ErrorCode == "overflow") * overflow_titles.Add($"{ShortTitle(item.Entity.Title)} [{item.Entity.PII}]"); * }); */ { var p = new Paragraph(); p.FontFamily = new FontFamily("微软雅黑"); p.FontSize = baseFontSize; p.TextAlignment = TextAlignment.Center; p.Foreground = Brushes.Gray; // p.TextIndent = -20; p.Margin = new Thickness(0, 0, 0, baseFontSize /*18*/); doc.Blocks.Add(p); if (borrow_count + return_count + change_currentLocation_count + change_location_count + transferout_count > 0) { List <string> lines = new List <string>(); if (return_count > 0) { lines.Add($"还书 {return_count}"); } if (borrow_count > 0) { lines.Add($"借书 {borrow_count}"); } REDO: if (display_transfer && change_currentLocation_count > 0) { lines.Add($"上架 {change_currentLocation_count}"); } if (display_transfer && change_location_count > 0) { lines.Add($"调拨 {change_location_count}"); } if (display_transfer && transferout_count > 0) { lines.Add($"下架 {transferout_count}"); } if (lines.Count == 0 && display_transfer == false) { display_transfer = true; // 修正 style,便于 Refresh() 时候使用新的 style StringUtil.SetInList(ref style, "transfer", true); doc.BuildStyle = style; goto REDO; } p.Inlines.Add(new Run { Text = $"{StringUtil.MakePathList(names)}", //Background = Brushes.DarkRed, //Foreground = Brushes.White FontFamily = new FontFamily("楷体"), FontSize = baseFontSize * 5.5, // 3.5, // FontWeight = FontWeights.Bold, Foreground = Brushes.White, }); if (door_names.Count > 0) { p.Inlines.Add(new Run { Text = $"\r\n({StringUtil.MakePathList(door_names)})", //Background = Brushes.DarkRed, Foreground = Brushes.Green, // FontFamily = new FontFamily("楷体"), FontSize = baseFontSize * 2.0, // 1.0 // FontWeight = FontWeights.Bold, }); } p.Inlines.Add(new Run { Text = $"\r\n{StringUtil.MakePathList(lines, ", ")}\r\n", //Background = Brushes.DarkRed, //Foreground = Brushes.White FontSize = baseFontSize * 3.0, // * 1.2, Foreground = Brushes.White, }); App.CurrentApp.SpeakSequence(StringUtil.MakePathList(lines, ", ")); } #if NO if (error_count > 0) { p.Inlines.Add(new Run { Text = $" 错误 {error_count} ", Background = Brushes.DarkRed, Foreground = Brushes.White }); } if (warning_count > 0) { p.Inlines.Add(new Run { Text = $" 警告 {warning_count} ", Background = Brushes.DarkGoldenrod, Foreground = Brushes.White }); } if (information_count > 0) { p.Inlines.Add(new Run { Text = $" 信息 {information_count} ", Background = Brushes.Gray, Foreground = Brushes.White }); } if (succeed_count > 0) { p.Inlines.Add(new Run { Text = $" 成功 {succeed_count} ", Background = Brushes.DarkGreen, Foreground = Brushes.White }); } #endif } #if NO // 第二部分,列出超额的信息 if (overflow_titles.Count > 0) { var p = BuildOverflowParagraph(overflow_titles); doc.Blocks.Add(p); } #endif // 检查超额图书 List <string> overflow_titles = new List <string>(); actions.ForEach(item => { if (item.Action == "borrow" && item.SyncErrorCode == "overflow") { overflow_titles.Add($"{ShortTitle(item.Entity.Title)} [{ShelfData.GetPiiString(item.Entity)}]"); } }); // 显示超额的信息 if (overflow_titles.Count > 0) { var p = doc.BuildOverflowParagraph(overflow_titles); // p.Tag = "#overflow"; doc.Blocks.Add(p); // 语音提醒 App.CurrentApp.SpeakSequence("警告:借书超额"); } else { // 超额信息的占位符 var p = new Paragraph(); p.FontSize = 0.1; p.Margin = new Thickness(); p.Padding = new Thickness(); p.Tag = OVERFLOW_ID; // "#overflow"; doc.Blocks.Add(p); } // 第三部分,列出每一笔操作 int index = 0; foreach (var action in actions) { var p = BuildParagraph(action, index, baseFontSize, style); if (p != null) { doc.Blocks.Add(p); index++; } } #if NO // 构造提示语音 List <string> speaks = new List <string>(); if (overflow_titles.Count > 0) { speaks.Add($"警告:有 {overflow_titles.Count} 册图书超越许可册数,请放回书柜,谢谢"); } if (speaks.Count == 0) { speaks.Add("操作完成"); // TODO:可否增加姓名和借还册数?例如 王立文借书 5 册成功 } speak = StringUtil.MakePathList(speaks, "; "); #endif return(doc); }
// 刷新显示 // 把 actions 中的对象的状态变化更新到当前文档中 // TODO: 一个办法是整个 Paragraph 替换。一个办法只替换里面的部分 InLine 对象 // parameters: // actions 发生了状态改变的 action。也就是被执行同步了的 action // skipped 被跳过了(没有处理的) action public void Refresh(List <ActionInfo> actions) { // 更新 _actions 中的对象 int count = 0; foreach (var action in actions) { var old_action = _actions.Where(o => o.ID == action.ID).FirstOrDefault(); if (old_action != null) { int index = _actions.IndexOf(old_action); Debug.Assert(index != -1, ""); if (index != -1) { _actions.RemoveAt(index); _actions.Insert(index, action); count++; } } } if (count == 0) { return; } { // 检查超额图书 List <string> overflow_titles = new List <string>(); _actions.ForEach(item => { if (item.Action == "borrow" && item.SyncErrorCode == "overflow") { overflow_titles.Add($"{ShortTitle(item.Entity.Title)} [{ShelfData.GetPiiString(item.Entity)}]"); } }); // 显示超额的信息 if (overflow_titles.Count > 0) { // 定位 Paragraph var block = this.Blocks.Where(o => { if (!(o.Tag is string id)) { return(false); } return(id == OVERFLOW_ID); }).FirstOrDefault(); // block 不应为 null。替代方法: if (block == null) { // TODO: 在适当位置插入标志段落 throw new Exception("#overflow 标志段落没有找到"); } var p = BuildOverflowParagraph(overflow_titles); this.Blocks.InsertBefore(block, p); this.Blocks.Remove(block); // 语音提醒 App.CurrentApp.SpeakSequence("警告:借书超额"); } } foreach (var action in actions) { // 定位 Paragraph var block = this.Blocks.Where(o => { if (!(o.Tag is ParagraphInfo info)) { return(false); } return(info.Action.ID == action.ID); }) .FirstOrDefault(); if (block == null) { continue; } // 替换 Paragraph { if (!(block.Tag is ParagraphInfo old_info)) { continue; } var new_block = BuildParagraph(action, old_info.Index, this.BaseFontSize, this.BuildStyle); this.Blocks.InsertBefore(block, new_block); this.Blocks.Remove(block); } } }
public static bool GetBackLampState() { return(ShelfData.GetLampState("back")); }
public static void TurnBackLampOff() { ShelfData.TurnLamp("back", "off"); }
public static void TurnBackLampOn() { ShelfData.TurnLamp("back", "on"); }