DigitalPlatform / dp2

Integrated Library System / 图书馆集成系统
http://digitalplatform.github.io/dp2
Apache License 2.0
106 stars 54 forks source link

智能书柜功能完善 #680

Open DigitalPlatform opened 4 years ago

DigitalPlatform commented 4 years ago

如题

接续:https://github.com/DigitalPlatform/dp2/issues/527

DigitalPlatform commented 4 years ago

动作库的记录结构分析

同步请求分析

动作库记录需要设立哪些数据字段,首先需要根据 dp2ssl 向 dp2library 发出的同步请求决定。下面分析一下当前的几种同步请求用到的数据字段。

Borrow() 请求

                            lRet = channel.Borrow(null,
                                action == "renew",
                                info.Operator.PatronBarcode,
                                entity.PII,
                                entity.ItemRecPath,
                                false,
                                null,
                                "item,reader,biblio,overflowable" + operTimeStyle, // style,
                                "xml", // item_format_list
                                out item_records,
                                "xml",
                                out string[] reader_records,
                                "summary",
                                out biblio_records,
                                out string[] dup_path,
                                out string output_reader_barcode,
                                out borrow_info,
                                out strError);

以上是 Borrow() 请求的语句。

需要下列字段: 读者证条码号 图书 PII 图书册记录路径(可选) 实际操作时间(决定 style 中的 operTime 子参数)

Return() 请求

                            lRet = channel.Return(null,
                                "return",
                                "", // _patron.Barcode,
                                entity.PII,
                                entity.ItemRecPath,
                                false,
                                "item,reader,biblio" + operTimeStyle, // style,
                                "xml", // item_format_list
                                out item_records,
                                "xml",
                                out string[] reader_records,
                                "summary",
                                out biblio_records,
                                out string[] dup_path,
                                out string output_reader_barcode,
                                out ReturnInfo return_info,
                                out strError);

以上是 Return() 请求的语句。

需要下列字段:

图书 PII 图书册记录路径(可选) 实际操作时间(决定 style 中的 operTime 子参数)

用于转移的 Return() 请求

                            List<string> commands = new List<string>();
                            if (string.IsNullOrEmpty(info.CurrentShelfNo) == false)
                                commands.Add($"currentLocation:{StringUtil.EscapeString(info.CurrentShelfNo, ":,")}");
                            if (string.IsNullOrEmpty(info.Location) == false)
                                commands.Add($"location:{StringUtil.EscapeString(info.Location, ":,")}");
                            if (string.IsNullOrEmpty(info.BatchNo) == false)
                                commands.Add($"batchNo:{StringUtil.EscapeString(info.BatchNo, ":,")}");

                            lRet = channel.Return(null,
                                "transfer",
                                "", // _patron.Barcode,
                                entity.PII,
                                entity.ItemRecPath,
                                false,
                                $"item,biblio,{StringUtil.MakePathList(commands, ",")}" + operTimeStyle, // style,
                                "xml", // item_format_list
                                out item_records,
                                "xml",
                                out string[] reader_records,
                                "summary",
                                out biblio_records,
                                out string[] dup_path,
                                out string output_reader_barcode,
                                out ReturnInfo return_info,
                                out strError);

以上是 Return() 请求的语句。

需要下列字段: 当前架位 要移交去的永久架位 移交操作批次号 图书 PII 图书册记录路径(可选) 实际操作时间(决定 style 中的 operTime 子参数)

归纳一下

总共需要下列字段: 1) 记录 ID 2) 操作 3) 读者证条码号(或工作人员账户) 4) 图书 PII 5) 图书册记录路径 6) 实际操作时间 7) 批次号 8) 去向最新架位 9) 去向永久馆藏地 10) 典藏移交方向(in/out) 11) 同步错误码 12) 同步错误信息 13) 同步次数 14) 状态

虽然和同步操作无关,但增加下列字段可以帮助调试和观察: RFID 标签 UID RFID 标签所在读卡器 RFID 标签所在天线 RFID 标签采用的 ISO 协议(15693/14443A) RFID 标签 EAS 状态 RFID 标签内容 bytes

DigitalPlatform commented 4 years ago

2020/4/23 改进

dp2SSL

1) 读者信息区增加了等待动画; 2) 所有使用 LibraryChannel 的地方,对 channel 都设置了较短的超时时间值,这样可以在通讯不正常的情况下尽快返回错误,避免读者长时间等待。

dp2ManageCenter

1) 增加了(点对点)聊天窗

DigitalPlatform commented 4 years ago

dp2library 中 Borrow() 和 Return() API 关于同步顺序的改进

最新版 dp2library 中对 Return() API 进行同步时遇到“该册为未借出状态”报错的情况,做了以下改进: 1) 如果此同步操作前册记录中有 checkInOutDate 元素,并且 Return() API 调用的 strStyle 参数中 operTime 子参数时间早于 checkInOutDate 元素的时间,则返回的错误码不是 NotBorrowed,而是 SyncDenied。这样错误码更合理一些了,表示同步请求不被接纳的最主要原因是时间顺序反了。 2) 如果 Return() API 调用的 strStyle 参数中 operTime 子参数时间晚于册记录中 checkInOutDate 元素的时间,则返回 NotBorrowed 错误码之前,服务器程序会修改册记录中的 checkInOutDate 元素内的时间为 strStyle 参数中的实际操作时间。原则上,晚一点的同步操作必然会修改册记录里面的 checkInOutDate 时间。这样做的目的是可以防止后继同步请求中违背顺序的兑现。

为了方便进行零星测试,dp2circulation 最新版的快捷出纳窗,在其工具条上的“...”子菜单里面增加了一个“测试同步”菜单项,可以勾选它或者消除勾选。在勾选状态下,当进行借书和还书操作的时候,软件会自动弹出一个对话框询问输入实际操作时间,并把这个时间加入 strStyle 参数里面的 operTime 子参数中,以达到测试目的。

DigitalPlatform commented 4 years ago

以前版本出纳台还书“该册为未借出状态”的漏洞

为了便于理解这个漏洞,下面描述一个场景: 1) dp2ssl 智能书柜中针对一册图书进行了还书和借书两笔操作,但因为网络故障,没有及时同步到 dp2library 服务器。 2) 此时若读者拿着这一册图书到出纳台进行还书,旧版本的 dp2library 服务器会报还书失败:“该册为未借出状态”。原因很简单,因为此前在智能书柜中的操作并没有同步到 dp2library 服务器,所以该册图书还处于没有被任何人借阅的状态,所以也谈不上还书成功。此时工作人员是否该收下这一册图书?按规则是应该收下的。 3) 等智能书柜的网络恢复正常以后,dp2ssl 会自动重试同步上述两个操作,旧版本的 dp2library 服务器会这样处理同步请求:对还书同步操作报错“该册为未借出状态”(随即变为 dontsync 状态);对借书同步操作给与兑现,注意这里结果就不正确了。因为刚才第二步工作人员已经收下了这一册图书,但现在系统里面却说读者借了这一册未还。

这里值得探讨的是,工作人员在系统报错的情况下收下这一册图书,这个做法是有一定风险的。为了避免出现问题,最好为还书 API 增加一种声明还书的机制,内务用这种机制来进行还书,册记录中 checkInOutDate 元素里面会记下声明的时间,工作人员再收下这一册图书(注意这个收下的时间点晚于智能书柜两笔操作的时间)。

改进后,当上面场景的 3) 步骤中,当同步还书和借书操作均会被拒绝,因为两笔同步的实际操作时间均早于册记录里面 checkInOutDate 元素里面记载的时间。

DigitalPlatform commented 4 years ago

2020/4/26 改进

1) 增加本地缓存册记录机制。当通讯不畅的时候,会自动使用本地数据库中以前缓存的册记录。

2) 同步请求做了优化,当本地已经有书目摘要的时候,不再附加请求从服务器 Borrow() 或 Return() API 返回。这样减少了通讯包尺寸。

DigitalPlatform commented 4 years ago

2020/4/28 改进

1) dp2managecenter 中书柜查询窗增加了下列栏目:姓名、书名、操作细节、关联 ID。姓名是读者的姓名;操作细节是借书或者还书操作的附加信息,比如借书时的应还时间,还书时的超期情况;关联 ID 是一个借书操作关联的还书操作的 ID,如果一个借书操作有关联的还书操作,表示这次借书已经还掉了。 2) dp2managecenter 中书柜查询窗内的行颜色做了优化。当 Action 为 "borrow" 并且 LinkID 为空的,表示这一个动作目前还没有还书,那么这行文字颜色为黄色,否则为白色。 3) dp2managecenter 中聊天窗口增加了针对书柜机器人的命令 check book 和 check patron。(原来的 check 命令改为 check book)。check patron 命令可以查看书柜本地操作历史信息中的在借(未还)册情况。 4) dp2managecenter 对于点对点消息的拼装做了改进和优化。上述 check patron 一般情况会超过单条消息的 data 尺寸,需要用到拼装功能。 5) dp2mserver 进行了改进,在传递 chunk 消息的时候,.id 为空的那些消息的 .groups 成员内容为消息所从属的群名(旧版本 .groups 成员内容为空)。 6) dp2circulation 的快捷出纳窗在请求 Return() API 的时候,会携带一个当前时间的 operTime 子参数。当还书操作时遇到“尚未借出”报错时,按规定工作人员应该收下这一册图书。 7) dp2ssl 做了改进,本地操作历史记录增加了若干字段。原先的 actions.db 需要删除以后,才能正常启动 dp2ssl。

DigitalPlatform commented 4 years ago

2020/4/29 改进

1) dp2ssl 的点对点通讯中断恢复以后,中断期间别人对书柜发送的聊天指令可以确保在通讯恢复后收到和执行。 2) dp2mserver 的 SetMessage() API 允许消息记录的 .groups 成员为空,这表示希望发送给当前用户所参加的所有群。 3) dp2ssl 配置参数中去掉了 dp2mserver 账户所参与的群名。这些群名可以从当前 dp2mserver 账户的配置信息中自动得到。 4) 书柜所用的 dp2mserver 账户,可以加入多于一个的群。每当书柜有动作的时候,它会给它参与的所有群都发消息。管理员在其中一个群里面给书柜发送聊天信息指令,书柜会回复消息到这个群,而不会回复到其它群里面。 5) dp2ssl 初始化阶段,请求 Return() API 会携带 operTime 子参数。此时若遇到返回错误码 SyncDenied,会自动作为 Information 类型处理,不会当作 Error 类型(此类型会导致报错并暂停初始化)。

DigitalPlatform commented 4 years ago

2020/5/6 改进

1) 改进了网络不通时候提交对话框的显示:通讯错误的报错显示在对话框里面是蓝色底色,不是以前的红色底色,这样可以避免读者紧张;同一 PII 的积压的几个动作,因为前面的第一个动作无法同步(因为网络不通么),那么后面几个动作也会跳过同步,以前版本这里看到的就是等待动画一直在转圈,为了避免读者看了感到焦虑,现在这里改为明确显示“暂时跳过同步” 2) 在网络不通的情况下,以前版本上述对话框里面的书目摘要有时候会出不来,现在已经做了改进,会尽量从本地缓存里面获得书目摘要显示出来

DigitalPlatform commented 4 years ago

2020/5/7 改进

1) 初步实现读者库记录自动同步到 dp2ssl 本地的功能。同步分为两个阶段:第一阶段获取全部读者记录,这个只会执行一次,dp2ssl 以后重启也不再执行;第二个阶段是零星同步,为了测试方便目前是每一分钟尝试同步一次,以后会改为更长的间隔 如果想要再次执行上述第一个阶段,可以打开用户文件夹里面的 settings.xml 文件,把下列元素完整删除即可:

  <patronReplication startDate="20200507:3-" />
DigitalPlatform commented 4 years ago

2020/5/8 改进

1) 在断网情况下,刷读者卡以后在智能书柜界面读者信息区显示出来的在借图书信息,是软件从 dp2ssl 本地历史数据库中合成出来的信息。目前限定每个读者最多只能借 5 册,每一册借期都是 31 天。注意这是一直断网的情况下借书以后看到的在借信息,而当网络回复正常以后,本地的动作同步到 dp2library 服务器以后,读者借阅最多册数和借期都是由 dp2library 决定。

DigitalPlatform commented 4 years ago

2020/5/11 改进

1) 断网时候启动 dp2ssl 也能启动和完成初始化。在这种初始化过程中,dp2ssl 就没法从 dp2library 获取册信息和书目摘要信息了,完全靠前一次网络正常期间获取的、缓存在本地的信息。如果正好书柜里面的图书在本地都有缓存信息,那初始化和联网时的效果无异。但如果放入书柜的图书有新的图书(也就是说 dp2ssl 本地没有缓存这些图书的册信息和书目摘要信息),那么初始化的时候就无法获取到册信息和书目摘要信息,但初始化依然会正常完成。所以这里就存在一种可能性,等网络恢复以后,发现某些标签的 PII 在 dp2library 一端没有对应的册记录。

2) 在读者证读卡器上扫入图书 RFID 标签,会弹出一个对话框显示这本书的信息。这个功能可以用于读者在不开门的情况下查看一本图书的信息;或者用于工作人员在诊断一个柜门中的图书的问题的时候作为辅助工具(如果没有这个功能,就只能用把一册一册图书顺次从柜门中拿出并关门观察信息变化这种排除法了)

DigitalPlatform commented 4 years ago

还有一个问题,dp2ssl初始化没有完成时,点【取消】,并退出dp2ssl。这时,盘点灯不会自动灭掉。

测试“断网状态下,书柜settings.xml中没有馆藏地信息时初始化”。步骤如下图。我重复测试好几遍,发现第5步不连网络,重新再启动一次dp2ssl,就会弹出蓝色背景报错。dp2ssl界面底部显示报错信息:“发送消息出现异常:未将对象引用设置到对象实例。”

发生未捕获的异常: 
Type: System.ArgumentNullException
Message: 值不能为 null。
参数名: target
Stack:
   在 System.Windows.Input.InputMethod.SetPreferredImeState(DependencyObject target, InputMethodState value)
   在 dp2SSL.App.<OnStartup>d__28.MoveNext()
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
   在 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   在 System.Windows.Threading.DispatcherOperation.InvokeImpl()
   在 MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
   在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   在 MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   在 System.Windows.Threading.DispatcherOperation.Invoke()
   在 System.Windows.Threading.Dispatcher.ProcessQueue()
   在 System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   在 MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   在 MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   在 System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   在 System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   在 System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   在 MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   在 MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   在 System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   在 System.Windows.Application.RunDispatcher(Object ignore)
   在 System.Windows.Application.RunInternal(Window window)
   在 dp2SSL.App.Main()

 版本: dp2SSL, Version=1.4.7440.19189, Culture=neutral, PublicKeyToken=null
操作系统:Microsoft Windows NT 6.2.9200.0
DigitalPlatform commented 4 years ago

dp2ssl 发布流程(程序员参考)

dp2ssl 最新版实现了 ClickOnce 版本和绿色版本的同一位置发布。

这里介绍 dp2ssl 编译发布的步骤。

0) 根据要发布的是测试版还是正式版,在 GreenIntaller 类代码中找到 dp2ssl_webUrl 这个字符串,修改正确,再进行后面的编译发布 1) 确保 greensetup 这个 Project 编译通过,然后用它的“发布”功能,在其 bin\release\netcoreapp3.1\publish 目录中创建好唯一一个 greensetup.exe 文件 2) 确保 dp2ssl 这个 Project 编译通过,然后用它的 ClickOnce 发布功能进行发布 3) 在 dp2ssl project 所在目录中,执行 buildgreen.bat 批处理命令。(注:如果打算为 dp2ssl 的 dev 版本进行发布,则要执行 buildgreendev.bat 批处理命令) 4) 在 publish\dp2ssl\v1 (v1_dev) 中可以看到 app.zip data.zip 和 greensetup.exe 文件。在 Application Files 子目录下删除较旧的子目录 5) 用 dp2libraryconsole 上传到服务器

DigitalPlatform commented 4 years ago

dp2ssl 绿色版安装操作步骤

dp2ssl 最新增加了绿色版。 绿色版具有如下特点: 1) 可以由普通 Windows 用户和 Administrator 用户(Windows Service 的 LocalSystem 身份)来启动。而 ClickOnce 难以被后者启动; 2) 可以由 dp2ssl 具有的远程管理界面进行重启。包括自动升级后重启新版本。 3) 日常自动升级; 4) 安装时加入当前 Windows 用户的 startup 组,当 Windows 操作系统重启后进入桌面时 dp2ssl 可以自动启动; 5) 适应某些不适合 ClickOnce 安装的场合。

绿色版安装步骤

dp2ssl 绿色版的安装操作步骤: 1) 用浏览器下载 http://dp2003.com/dp2ssl/v1/greensetup.exe 文件到本地。(注:dev 版地址为 http://dp2003.com/dp2ssl/v1_dev/greensetup.exe) 2) 启动 greensetup.exe,程序会自动安装好绿色版。安装过程将把可执行程序复制到 c:\dp2ssl 子目录。(注:这一步不确定是否要先在电脑上安装 .NET Core 3.1 的 Runtime 模块。greensetup 是用 .NET Core 3.1 开发的)

.NET Core 3.1 的 Runtime 在这里下载: https://dotnet.microsoft.com/download/dotnet-core/3.1 选择页面右侧的“Desktop Runtime 3.1.5”下载。(它包含了 .NET Core Runtime,不需再下载后者)

如果当前电脑以前曾经安装过 dp2ssl 的 ClickOnce 版本,则安装绿色 dp2ssl 过程中,会自动把原先版本在 Windows 当前用户目录下的 dp2ssl 子目录(称为用户文件夹)复制到 c:\programdata\dp2\dp2ssl 下,并且会在原先用户文件夹中创建一个 userDirectoryMask.txt 的文件,这个文件的作用是当 dp2ssl ClickOnce 版本启动的时候探测到它的存在,会警告并退出。

意思就是说当 dp2ssl 绿色版装好以后,原先的 ClickOnce 版本就不允许再继续使用了。建议用户卸载掉 ClickOnce 版本的 dp2ssl。卸载 ClickOnce 版本的时候,不会删除原先的用户文件夹。

注意后面为 dp2ssl 进行数据备份的时候,数据已经存储在 c:\programdata\dp2\dp2ssl 这个新位置了。

绿色版自动升级原理

绿色版的 c:\dp2ssl 目录中有两个可执行文件,greensetup.exe 和 dp2ssl.exe。安装为桌面创建的“dp2SSL 自助借还(绿色)”快捷方式,和当前 Windows 用户 startup 命令组里面的快捷方式,都是指向 greensetup.exe 的。

greensetup.exe 每次启动的时候,会进行如下检查: 1) 如果 install_state.txt 文件中的状态信息表明此前成功下载了 app.zip 和 data.zip 但尚未来得及展开,则展开它们,然后启动 dp2ssl.exe。(注:dp2ssl.exe 本身在启动状态,是无法展开两个 .zip 文件到 c:\dp2ssl 目录的,所以当 dp2ssl 自身进行了 .zip 文件的下载以后,只能借助别的程序来展开 .zip 文件) 2) 其它情况,会直接启动 dp2ssl.exe

(注:当 dp2ssl 第一次进行绿色安装的时候,greensetup.exe 是会自动下载 .zip 文件和进行安装的。和以上描述的安装后的日常启动过程有所不同)

平时 greensetup.exe 并不负责直接下载 .zip 文件,dp2ssl.exe 负责每隔一段时间自动探测 dp2003.com 服务器上是否出现了新版本,如果有新版本则下载两个 .zip 文件,然后书架界面上会出现黄色底色的文字提醒重启以后可以使用新版本。这两个 .exe 是这样分工配合的。

如果 greensetup.exe 本身更新了,dp2ssl.exe 也能探测到并自动下载到 c:\dp2ssl 目录。

DigitalPlatform commented 4 years ago

dp2Commander 远程管理模块

安装

目前 dp2Commander 是用一个 .zip 文件承载的绿色程序文件。在这里可以下载: http://dp2003.com/dp2commander/dp2commander.zip

安装步骤如下: 0) 确保电脑上安装了 .NET Core 3.1 Runtime;创建一个 dp2mserver 消息用户(账户),供 dp2Commander 使用,操作步骤后面另行介绍。 1) 将 .zip 文件展开到一个目录,例如 c:\dp2Commander 下; 2) 用“以管理员身份运行”方式打开一个 Windows 命令提示符窗口。进入 c:\dp2Commander 目录; 3) 对 dp2Commander 进行参数配置。执行 dp2Commander setting 命令。按照提示一步一步输入信息。顺次需要输入:dp2mserver 服务器 URL(应为 http://dp2003.com:8083/dp2mserver );消息用户名;消息用户密码。 4) 执行 dp2Commander install 命令,注意观察所显示的执行结果信息。这一步是把 dp2Commander 安装为 Windows Service 模块; 5) 执行 'dp2Commander start' 命令,注意观察所显示的执行结果信息。这一步是将 Windows Service 启动起来。

至此 dp2Commander 模块安装完成。

常见问题: 执行 dp2Commander install 时报错: The dp2CommanderService service can only be installed as an administrator 这是因为没有用管理员身份启动 Windows 命令提示符的缘故。请关闭这个窗口,重新用管理员身份启动。

配置优化

可以将 dp2Commander 的 Windows Service 模块的崩溃后自动重启功能启用。

升级

升级操作步骤如下: 1) 以管理员身份启动一个 Windows 命令提示符窗口。进入 c:\dp2Commander 目录; 2) 执行 dp2Commander uninstall 命令。注意观察所显示的执行结果信息。这一步是将 Windows Service 模块卸载; 3) 将 dp2Commander 的 .zip 绿色文件包展开到 c:\dp2Commander 目录。也就是说用新的可执行文件覆盖以前的可执行文件; 4) 执行 dp2Commander install 命令,注意观察所显示的执行结果信息。这一步是把 dp2Commander 安装为 Windows Service 模块; 5) 执行 'dp2Commander start' 命令,注意观察所显示的执行结果信息。这一步是将 Windows Service 启动起来。

注:以前的配置信息在上述升级过程中不会变化。配置信息是存储在 settings.xml 文件中。

至此 dp2Commander 模块升级完成。


注:如果dp2command使用的消息帐户的密码变更了,需要先dp2Commander uninstall`卸载,再dp2Commander setting设置新密码,再dp2Commander install,最后dp2Commander start。

dp2mserver 消息用户

需要为 dp2Commander 模块开辟一个 dp2mserver 账户。建议名字用类似 xxxcommander 这样的形态命名。例如,书柜的账户为 robot,那么它所在电脑上的 dp2Commander 模块的账户可以命名为 robotcommander。

这个 xxxcommander 账户需要设置群组为 gn:<group>,其他字段暂时不需要设置。注意 group 是代表一个群名,应和书柜账户同在一个群,这样便于管理。

当维护人员在聊天界面里面需要对 dp2Commander 下达命令的时候,可以用 @xxxcommander 这样的内容开头,dp2Commander 就能收到和执行命令

远程交互

可以用聊天窗口和 dp2Commander 模块交互。这里假定 dp2Commander 模块的消息用户名为 robotcommander。

和它打招呼,输入命令 @robotcommander hello,如果它和 dp2mserver 服务器正常连接的话,它会回应 'hello!'

故障排除

如果通过聊天窗口和 dp2Commander 模块打招呼得不到回应,则可能是因为 dp2Commander 没有正确连接到 dp2mserver 服务器。

首先需要核实一下消息用户名和密码是否正确。利用 Windows 命令提示符运行 dp2Commander setting 命令,可以观察和重新输入 dp2mserver 服务器 URL,消息用户名和消息密码。重新输入以后,注意要重启一下 Windows Service 模块,让新输入的参数生效。

然后,在 dp2Commander 安装目录内的 log 子目录中,检查当天的日志文件,看看里面是否有关于连接消息服务器出错的报错信息。

DigitalPlatform commented 4 years ago

书柜初始化阶段,需要测试的特殊情况

1) 放入柜门里面的 ISO15693 标签,数据解析错误 2) RFID 标签解析正确,但 PII 在服务器中找不到册记录 3) 柜门里面放入了 ISO14443A 的标签(需要柜门里面是 RL8600 这样的多协议读卡器才能测试) 4) 网络通讯故障

DigitalPlatform commented 4 years ago

2020/6/21 改进

1) 最新版 biblio.db 中读者记录表结构有变化,需要删除此文件然后重启 dp2ssl。但读者记录无法自动重新从 dp2library 服务器同步,需要进入参数设置页面,用页面左边的菜单项中的重新全量同步命令来同步一次。如果不专门重新同步一次,恐断网状态下刷读者卡会有问题 2) 在 dp2ssl 这里借书,然后去内务前端还书。然后在断网状态下,刷读者卡,会看到册依然在借阅列表中(然而这些册其实已经用内务还过了)。这个问题在最新版已经解决。解决的方法是,给本地缓存的读者记录增加了一个 LastWriteTime 字段,记载最后同步的时间。晚于这个时间的本地动作才会被合成进去,这样就避免了上述问题 3) 断网状态下刷读者卡,可借总数和当前还可借数字不正确的问题已经解决 4) 断网状态下,借书以后依然会去同步一次(显然会同步失败)。新版本改为此时不会去同步,而是立即显示跳过同步。如果有超额的情况会立刻显示和警告出来。超额状态是用本地缓存的从 library.xml 过来的读者借阅权限参数本地计算出来的。

DigitalPlatform commented 4 years ago

dp2library Borrow() API 增强

dp2library 的 Borrow() API 为 dp2ssl 的同步需要做了增强。

这些增加主要通过 strStyle 参数实现。

子参数介绍

overflowable 子参数 -- 允许超额

"overflowable" 表示允许超过借书限额进行借书。超出限额的册的借期只有一天(必须在当天之内还回)。在请求调用之前,前端并不知道本次请求的图书是否会被当作超额处理。超额算法是根据图书和读者的类型,以 library.xml 中的借阅权限参数表来测算。

operTime 子参数 -- 操作发生时间

operTime:xxx 子参数提供了前端借阅操作发生的实际时间。xxxx 部分是实际动作时刻,为 RFC1123 格式。

注意这个时间不是 Borrow() API 请求的时间。如果同步请求不成功,dp2ssl 一般会多次尝试重新同步,那么后面的同步请求时刻就在借阅操作时刻之后了。

如果 strStyle 中包含了 operTime 子参数,表示本次请求的目的是同步操作,实际动作时刻其实早于本次提交请求的时刻。 API 执行时会检查册记录里面的 checkInOutDate 元素内的时间,如果实际操作时刻早于这个时间,则本次同步请求会被拒绝。

requestPeriod 子参数 --- 前端指定的借期

requestPeriod 子参数提供了前端指定的借期,希望 dp2library 服务器直接用这个借期来同步借阅动作。

overflow 子参数 --- 前端指定溢出方式

"overflow:xxx" 表示前端希望本次用溢出方式借阅。借期为一天。xxx 为溢出情况说明

用例

注:以下都需要包含 operTime:xxx 参数

超额的情况进行同步

用 overflow:xxx 参数。xxx 部分包含了超额情况说明,前端要提供这部分字符串。如果有多个原因需要说明,之间用分号间隔。

没有超额,确知借期的同步

用 requestPeriod 参数

前端让服务器决定借期和是否超额的同步

用 overflowable 参数

子参数之间的关系

overflow 子参数会优先于 overflowable 参数起作用。overflow 的意思是明确本次请求就是超额。一般用了 overflow 参数就不要用 overflowable 参数了。

overflow 参数会压制 requestPeriod 参数。overflow 参数意味着超额,那么超额的借期就是一天。requestPeriod 参数提供的借期就不会被使用了。

DigitalPlatform commented 4 years ago

2020/6/24 改进 -- 增加远程控制指令

增加了远程控制指令 dialog 和 press xxx。

dialog 指令用于显示当前最顶层的对话框或者窗口的文字。其中按钮用 [停止] 这样的形态表示。

press xxx 指令用于按下界面上的按钮。xxx 代表按钮上的文字,可以是按钮上文字的中间一部分即可,比如按钮上文字实际是 '开始处理',那么指令可以是 press 开始


远程紫外线消毒命令sterilamp,例如为名为robot书柜,则发送@robot sterilamp

2021/1/29 添加:

sterilamp 命令

开始或者中断紫外灯消毒过程。

以下是开始消毒的命令: sterilamp sterilamp on sterilamp begin sterilamp turnon

以下是中断消毒的命令: sterilamp off sterilamp end sterilamp turnoff

消毒过程一旦启动,会自动进行十分钟。在消毒过程中,随时可以用命令中断。

DigitalPlatform commented 4 years ago

LED 屏串口命令

注:左边的数字是文档提供的,右边的数字是经过验证正确的(在rfid中心配置时,设置效果使用右边的数字,一般用02)

0 "随机" 00 1 "立即显示" 01 2 "左移" 02 3 "连续左移" 4 "右移" 03 5 "上移" 04 6 "连续上移" 7 "下移" 05 8 "飘雪" 06 9 "冒泡" 07 10 "分散拉伸" 08 11 "画卷打开" 09 12 "画卷闭合" 10 13 "向左拉伸" 11 14 "向右拉伸" 12 15 "向上拉伸" 13 16 "向下拉伸" 14 17 "向左镭射" 15 18 "向右镭射" 16 19 "向上镭射" 17 20 "向下镭射" 18 21 "水平百叶" 19 22 "垂直百叶" 20 23 "左覆盖" 21 24 "右覆盖" 22 25 "上覆盖" 23 26 "下覆盖" 24 27 "左上角覆盖(斜线)" 25 28 "右上角覆盖(斜线)" 26 29 "左下角覆盖(斜线)" 27 30 "右下角覆盖(斜线)" 28 31 "左上角覆盖(直线)" 29 32 "右上角覆盖(直线)" 30 33 "左下角覆盖(直线)" 31 34 "右下角覆盖(直线)" 32 35 "左右对开" 33 36 "上下对开" 34 37 "左右闭合" 35 38 "上下闭合" 36 39 "中间向四周(矩形)" 37 40 "四周向中间(矩形)" 38 41 "中间向四周(十字)" 39 42 "四周向中间(十字)" 40 43 "中间向四周(菱形)" 41 44 "四周向中间(菱形)" 42 45 "闪烁" 43 46 "中间移出" 44 47 "左右移入" 45 48 "左右交叉移动" 46 49 "左右交叉覆盖" 47 50 "上下交叉覆盖" 48

DigitalPlatform commented 4 years ago

远程控制 LED 屏幕

在聊天界面使用 @robot led 要显示的文字这样的命令,就可以在 LED 屏幕上显示文字。

下面详细介绍这个命令的格式。

led -text:文字内容 -x:0 -y:0 -ledName:* -fontSize:24 -effect:moveLeft -moveSpeed:fast -duration:1 -horzAlign:left -vertAlign:top

下面逐一介绍命令参数:

X起始位置 -x

格式 -x:0

用一个数字表示文字显示在 LED 屏上 X 方向的起始像素位置。缺省为 0。原点是左上角。

Y起始位置 -y

格式 -y:0

用一个数字表示文字显示在 LED 评上 Y 方向的起始像素位置。缺省为 0。原点是左上角。

控制卡名字 -ledName

格式 -ledName:*

指定控制卡的名字。目前暂时为 *,表示所有控制卡

文字内容 -text

格式 -text:显示文字

指定要显示的文字内容。这个参数比较特殊,也可以直接用显示文字,省略前面的 -text:部分。比如:

led 测试文字

文字内容中可以使用转义序列表达特殊字符。 \r\n 回车换行 \w 空格

字体尺寸 -fontSize

格式 -fontSize:24

指定字体尺寸。可以为 16 24 32 三个值之一。缺省为 32。

特效 -effect

格式 -effect:moveLeft

指定文字显示的特效。缺省为 still。可用值如下表:

            "00:随机,random",
            "01:立即显示,still",
            "02:左移,moveLeft",
            "03:右移,moveRight",
            "04:上移,moveUp",
            "05:下移,moveDown",
            "06:飘雪",
            "07:冒泡",
            "08:分散拉伸",
            "09:画卷打开",
            "10:画卷闭合",
            "11:向左拉伸",
            "12:向右拉伸",
            "13:向上拉伸",
            "14:向下拉伸",
            "15:向左镭射",
            "16:向右镭射",
            "17:向上镭射",
            "18:向下镭射",
            "19:水平百叶",
            "20:垂直百叶",
            "21:左覆盖",
            "22:右覆盖",
            "23:上覆盖",
            "24:下覆盖",
            "25:左上角覆盖(斜线)",
            "26:右上角覆盖(斜线)",
            "27:左下角覆盖(斜线)",
            "28:右下角覆盖(斜线)",
            "29:左上角覆盖(直线)",
            "30:右上角覆盖(直线)",
            "31:左下角覆盖(直线)",
            "32:右下角覆盖(直线)",
            "33:左右对开",
            "34:上下对开",
            "35:左右闭合",
            "36:上下闭合",
            "37:中间向四周(矩形)",
            "38:四周向中间(矩形)",
            "39:中间向四周(十字)",
            "40:四周向中间(十字)",
            "41:中间向四周(菱形)",
            "42:四周向中间(菱形)",
            "43:闪烁",
            "44:中间移出",
            "45:左右移入",
            "46:左右交叉移动",
            "47:左右交叉覆盖",
            "48:上下交叉覆盖",
            "49:连续左移,紧凑左移,moveLeftCompact",

注意表中数字,汉字,英文名称,都可以使用。

例如下面三种用法都是同样的效果。 -effect:02 -effect:左移 -effect:moveLeft

移动速度 -moveSpeed

格式 -moveSpeed:fast

指定文字移动的速度。可用值为 slow normal fast 之一。缺省为 normal。

停留时间 -duration

格式 -duration:1

指定中间停留的时间,单位是秒。值可以是小数。缺省为 LED 单元个数 * 1秒。

水平对齐 -horzAlign

格式 -horzAlign:left

指定文字水平对齐方向。可用值为 left center right 之一。缺省为 left。

垂直对齐 -vertAlign

格式 -vertAlign:top

指定文字垂直对齐方向。可用值为 top center bottom 之一。缺省为 top。

扩展风格 -style

格式 -style:xxx

指定扩展的风格。目前暂未使用此参数。

DigitalPlatform commented 4 years ago

2020/7/3 改进

当dp2ssl测试版 自助借还 模式,发现当停留在主界面上,给读写器上取放标签,主界面上的数字不会感应变化。

已经解决。测试时候注意在自助借还模式和书柜模式都测试一下。有余力再测试一下不退出 dp2ssl 情况下从配置参数里面切换两个模式以后,数字显示是否还正确。

dp2ssl自助借还模块,只要发生过一次借还,(图书信息卡片左上角的)在借/在架状态就显示不对了。 发现dp2ssl自助借还模式下,图书的借阅信息好像受biblio.db影响了,删除这个文件,再启动dp2ssl上述状态显示正常了。但继续进行借还操作后,状态又不正确了

已经解决。是因为册记录本地缓存造成的问题。在自助借还模式下,最新版改为只用从 dp2library 当时获得的最新册记录。

新安装完dp2ssl测试版,还没有配置服务器参数,启动dp2ssl提示选择启动模式。当初始安装完后,还未配置参数时,希望不要出现这个提示框

已经解决。现在断网模式只对书柜模式起作用。

dp2ssl最新版,不是绿色版。有一本册条码号:NKX0006347。书柜借还时提示“暂时跳过同步”,本地数据库状态state显示“null”。看一下册记录

通过删除本地缓存的几个 .db 文件问题消失。但仍需要继续分析一下代码。

DigitalPlatform commented 4 years ago

shelf.xml 改进

新增属性

原来的 shelf 元素改名为 group 元素,以反映“一组”这样一种含义,更准确一些。

group 元素和 door 元素均可以使用以下属性:

left,top,width,height -- 左上角、宽、高

这是旧版本已经具备的属性,新版本做了一定增强

padding -- 内边距

用法举例: padding="8" padding="1,1,2,2" 注:四个数字顺序为:double left, double top, double right, double bottom

margin -- 外边距

用法举例: margin="8" margin="1,1,2,2" 注:四个数字顺序为:double left, double top, double right, double bottom

borderBrush -- 边框颜色

用法举例: borderBrush="DarkGray"

borderThickness -- 边框宽度

用法举例: borderThickness="1" borderThickness="1,2,1,2" 注:四个数字顺序为:double left, double top, double right, double bottom

cornerRadius -- 边框圆角半径

用法举例: cornerRadius="8" cornerRadius="4,4,8,8" 注:四个数字顺序为:double topLeft, double topRight, double bottomRight, double bottomLeft

缺省为 "0,0,0,0",即直角状态。

openBrush -- 开门状态颜色

用法举例: openBrush="DarkRed" openBrush="#22ff0000" 注: argb 方式定义颜色,a 表示 Alpha 通道(透明度) openBrush="#ff0000" 注:rgb 方式定义颜色 openBrush="imageMap:shelf_compact_open.png" 注:用图像文件的映射渲染当前门的打开状态。图像文件应当放到用户文件夹内 openBrush="image:door1_open.png" 注:用图像文件渲染当前门的打开状态。图像文件应当放到用户文件夹内

注1:如果希望定义全透明的颜色,可以用 #00ffffff,这是全透明的纯白色 注2:图像文件的映射,是指截取当前门所在位置的一小块(由 left top width height 属性决定)

closeBrush -- 关门状态颜色

用法举例: closeBrush="DarkRed" closeBrush="#22000000" 注: argb 方式定义颜色,a 表示 Alpha 通道(透明度) closeBrush="#000000" 注:rgb 方式定义颜色 openBrush="imageMap:shelf_compact.png" 注:用图像文件的映射渲染当前门的关闭状态。图像文件应当放到用户文件夹内 openBrush="image:door1_closed.png" 注:用图像文件渲染当前门的关闭状态。图像文件应当放到用户文件夹内

foreground -- 普通文字颜色

用法举例: foreground="White"

errorForeground -- 报错文字颜色

用法举例: errorForeground="DarkRed"

两种元素布局结构

最新版 shelf.xml 中支持两种元素布局:第一种是 group 元素下具有 door 元素;第二种是只使用 group 元素。

在第一种布局方式下,door 元素可以不具备上述位置属性 left,top,width,height,而是它上级的 group 元素具备这几个属性。door 元素会自动竖向分布在 group 的显示范围内。

第二种布局方式下,door 元素具备 left,top,width,height 这几个属性,它自己能够独立定位位置,不依赖上级的 group 元素定义。(注:door 元素的这几个属性定义的位置,是相对于整个坐标体系左上角的原点的,和 door 元素的父级 group 元素定义的位置无关)

第一种和第二种布局方式可以混合使用。也就是说,group 元素下的若干 door 元素,这些 door 元素可以具有 left,top,width,height 属性,也可以不具有这些属性(自动采用 group 元素的这些属性)。

注1:对于一个具体的 group 和 door 元素的属性来说, left,top,width,height 这几个属性,要使用就四个一起使用,不允许只使用其中的部分属性。 注2:如果一个 group 或 door 元素的上述 left top width height 属性被采用,则同一元素的 margin padding 等其他属性也会自动被采用。如果这些属性缺省,则用它们的缺省值。例如,一个 door 元素定义了 left top width height 属性,但没有定义 margin 属性,door 元素上级的 group 元素定义了 margin 属性。在这种情况下,door 元素的 margin 属性缺省值会被使用,而不会使用 group 元素的 margin 属性。

DigitalPlatform commented 4 years ago

greensetup.exe 的错误日志位置

greensetup.exe 本身的错误日志在 c:\programdata\dp2\dp2ssl\greensetup_logs 子目录中。

注意 dp2ssl 的绿色版,其错误日志在 c:\programdata\dp2\dp2ssl\log 子目录中(ClickOnce 版在 Windows 当前用户目录下的 dp2ssl\log 子目录中)

DigitalPlatform commented 4 years ago

SIP 资料

https://www.niso.org/standards-committees/sip

http://wiki.chilifresh.com/lib/exe/fetch.php/sip2_developers_guide.pdf

http://developer.polarislibrary.com/media/614/3msip.pdf

https://clcohio.org/sip-testing-tool/

DigitalPlatform commented 4 years ago

shelf.xml 改进(2)

根元素增加了一个属性 background,可以定义书柜背景图没有覆盖到的部分的背景色。如果书柜背景图中本身有透明部分,那么透明部分也会显示 background 属性定义的背景色。

例1:

<root background="Green" >
...

如果要定义渐变的背景色,可以在根元素下使用一个 background 元素定义。

例2:

<root >
    <background>
            <LinearGradientBrush 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
EndPoint="1,1" StartPoint="0,0">
                <GradientStop Color="Black" Offset="0"/>
                <GradientStop Color="#FF363636" Offset="1"/>
            </LinearGradientBrush>
    </background>
...
DigitalPlatform commented 4 years ago

dp2library GetItemInfo() API 增强

所返回的册记录中,服务器会自动即时创建好一个 oi 元素,里面是这一册图书应有的机构代码。如果创建元素过程有错,oi 元素里面会有一个 error 属性,里面是出错原因。

dp2ssl 本地缓存的册记录信息,已经把 PII 这个 key 约定为 OI.PII 形态。

DigitalPlatform commented 4 years ago

重建读者本地缓存

dp2SSL 中智能书柜功能为了能够在断网状态下继续运行,为读者信息准备了一个本地缓存。dp2SSL 安装和首次启动的时候,它会主动从 dp2library 服务器获取全部读者记录保存到这个缓存中,并且时刻保持同步。

读者缓存信息保存在 dp2SSL 用户文件夹的 biblio.db (SQLite)数据库文件中。注意这个数据库中还保存了书目和册记录的缓存信息。

如果因为某些原因系统管理员故意删除了 biblio.db 数据库文件,dp2SSL 重新启动以后会自动重建相关的缓存,不会引起故障。

当 dp2SSL 启动的时候,它会自动检查一下 biblio.db 这个文件是否已经存在,如果存在,里面是否已经有了读者缓存信息。如果文件不存在或者其中没有读者缓存信息,则 dp2SSL 会自动在后台启动一次全量获取读者记录的过程,重新把全部读者记录获取并保存到本地缓存中。

DigitalPlatform commented 4 years ago

RfidCenter Inventory() error 分析

21:34 出现 1 次 22:07 出现 2 次 1:15 出现 100 次(导致重启) 1:26 出现 35 次(导致重启) 2:21 出现 2 次 4:15 出现 1 次 5:53 出现 1 次 8:05 出现 1 次 8:15 出现 100 次(导致重启) 8:45 出现 2 次 8:55 出现 100 次(导致重启)

RfidCenter 中操作历史如下:

10:07 版本号: 1.9.7550.30161
10:07 当前读卡器数量 2。包括: 
RL8600
M201
10:07 POS 打印机初始化成功
11:07 没有发现更新
12:07 没有发现更新
13:07 没有发现更新
14:07 没有发现更新
15:07 没有发现更新
16:07 没有发现更新
17:07 没有发现更新
18:07 没有发现更新
19:07 没有发现更新
20:07 没有发现更新
21:07 没有发现更新
21:34 (1 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=M201

22:07 没有发现更新
23:07 (2 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=M201
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600

23:07 没有发现更新
0:07 没有发现更新
1:07 没有发现更新
1:15 (100 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=-30, ret=-30, readerName=RL8600
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
3) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
4) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
5) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
6) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
7) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
8) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
9) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
10) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
... (余下 90 项被略去)
1:15 *** 自动重启 RFID *** 因最近阶段内 inventory 出错次数为 104
1:16 当前读卡器数量 2。包括: 
RL8600
M201
1:26 (35 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
3) 21:34:02:inventory 出错: Inventory() error, errorCode=-30, ret=-30, readerName=RL8600
4) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
5) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
6) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
7) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
8) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
9) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
10) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
... (余下 25 项被略去)
1:26 *** 自动重启 RFID *** 因最近阶段内 inventory 出错次数为 35
1:26 当前读卡器数量 2。包括: 
RL8600
M201
2:07 没有发现更新
2:21 (2 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600

3:08 没有发现更新
4:08 没有发现更新
4:15 (1 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=readerRespondTimeout, ret=-5, readerName=M201

5:08 没有发现更新
5:53 (1 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600

6:08 没有发现更新
7:08 没有发现更新
8:05 (1 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=M201

8:08 没有发现更新
8:15 (100 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=-30, ret=-30, readerName=RL8600
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
3) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
4) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
5) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
6) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
7) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
8) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
9) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
10) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
... (余下 90 项被略去)
8:15 *** 自动重启 RFID *** 因最近阶段内 inventory 出错次数为 105
8:15 当前读卡器数量 2。包括: 
RL8600
M201
8:45 (2 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600

8:55 (100 项) 压缩日志 inventory 出错: {0}
1) 21:34:02:inventory 出错: Inventory() error, errorCode=-30, ret=-30, readerName=RL8600
2) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
3) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
4) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
5) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
6) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
7) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
8) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
9) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
10) 21:34:02:inventory 出错: Inventory() error, errorCode=ioError, ret=-2, readerName=RL8600
... (余下 90 项被略去)
8:55 *** 自动重启 RFID *** 因最近阶段内 inventory 出错次数为 102
8:55 当前读卡器数量 2。包括: 
RL8600
M201
9:08 没有发现更新
DigitalPlatform commented 4 years ago

dp2library 增加 adjustOverflow 类型的操作日志记录

<root>
  <libraryCode></libraryCode>
  <operation>adjustOverflow</operation>
  <borrowID>b85ca86b-e39e-4058-bbcb-85f70134b75c</borrowID>
  <patronBarcode>R0000001</patronBarcode>
  <itemBarcode>T0000050</itemBarcode>
  <confirmItemRecPath></confirmItemRecPath>
  <borrowDate>Fri, 04 Sep 2020 21:35:35 +0800</borrowDate>
  <borrowPeriod>31day</borrowPeriod>
  <returningDate>Mon, 05 Oct 2020 12:00:00 +0800</returningDate>
  <borrow barcode="T0000050" recPath="中文图书实体/1268" biblioRecPath="中文图书/614" location="智能书柜" overflow="读者 'R0000001' 所借图书数量将超过 馆代码 '' 中 该读者类型 '本科生' 对所有图书类型的最多 可借册数 值 '1'" borrowDate="Fri, 04 Sep 2020 21:35:35 +0800" borrowPeriod="1day" borrowID="b85ca86b-e39e-4058-bbcb-85f70134b75c" returningDate="Sat, 05 Sep 2020 12:00:00 +0800" operator="supervisor" type="普通" price="" />
  <operator>supervisor</operator>
  <operTime>Fri, 04 Sep 2020 21:36:41 +0800</operTime>
  <clientAddress via="net.pipe://localhost/dp2library/xe">localhost</clientAddress>
  <version>1.08</version>
</root>
DigitalPlatform commented 4 years ago

dp2ssl 中对 RFID 标签 OI 字段用法的增强

1) dp2library 最新版的 GetReaderInfo() API 返回的读者记录中会有一个 oi 元素,提供了读者所在的机构代码。注意这是 API 返回记录前临时在内容中增加的 oi 元素,并不意味着数据库中的读者记录一定有 oi 元素。

2) dp2library 检查超期的后台任务,会给所有没有 oi 元素的读者记录加上 oi 元素。

3) dp2library 在借书环节,在给读者记录添加 borrows/borrow 元素的时候,会添加一个 oi 属性,是这一册的所属机构代码。另外也会顺便给读者记录中添加一个 oi 元素。

4) dp2ssl 本地动作库记录的 PII 字段,以前是一个纯粹的 PII,没有 OI 部分,现在改进为包含 OI 部分的字符串,形态为 xxx.xxx。如果 OI 为空,也会形成 .xxx 的形态,点不会被省略。

5) dp2ssl 中借书时的超额运算过程中,PII 字符串改为含有 OI 部分的格式。这样可以避免馆外机构的标签的运算出现漏洞。

6) dp2ssl 在刷 ISO14443A 读者卡,或者刷人脸的时候,一旦从 dp2library 服务器获得读者记录,在写入本地缓存数据库的时候,检索点会包含 OI 部分(从读者记录中的 oi 元素获得);读者进行借书还书操作的时候,也会带上 OI 进行运算。需要测试一下专门制作一张 ISO15693 的读者卡,OI 和当前图书馆不同,但 PII 碰巧和当前读者库中某个读者记录的 PII 相同,以此观察算法是否严密(这种读者卡软件应该能识别出不属于本馆)

7) OI 为空或 OI 不等于当前图书馆机构代码的这两种情形的图书标签,在 dp2ssl 初始化阶段会被识别出来。另外它参与借书和还书的时候,应该不被当作本馆图书、而是被当作临时外来的图书加以处理。

8) 测试的时候注意先删除 dp2ssl 本地的所有 .db 数据库文件再开始测试。因为本地数据库中的内容格式有一定变化,怕以前残留的数据产生干扰。

DigitalPlatform commented 4 years ago

RfidCenter 对读卡器元数据利用方式的改进

以前版本在为一个探测到的(COM 口)读卡器 Product ID 去元数据中找对应的 Driver Name 时,如果没有找到,就当作这个 COM 口不是读卡器处理,跳过它继续探测后面的 COM 口。这样不但会漏掉这个读卡器,而且会掩盖问题。

新版本改进为,当在元数据中找 Driver Name 如果没有找到,会直接报错(也许后面的读卡器就不探测了),这样会明确提示用户发生了错误,根据错误信息开发者会去增补元数据。

DigitalPlatform commented 4 years ago

最近改进

1.5.4 (2020/9/16)

1) 当用 Administrator 身份启动 dp2ssl,并且这一次是首次创建 settings.xml,dp2ssl 会特意把 settings.xml 的修改权限设置为 everyone,以便后面再用普通 Windows 账户身份启动 dp2ssl 的时候,保存 settings.xml 的时候不会出现报错。 2) 小键盘按钮改大了一点。 3) dp2ssl 在首次登录 dp2library 账户的时候会自动检查其权限是否低于一个最低的权限。如果低于,就会报错。 4) 设置画面增加关机按钮。

1.5.5 (2020/9/17)

1) 为书柜界面增加保存分割条位置的功能。分割条拖动时候有个最大最小范围。

1.5.6 (2020/9/21)

1) 缩短书柜开门后首次警告不要忘了关门的时间,从 30 秒缩短到 15 秒。然后每隔 10 秒持续提醒(修改前是 15 秒) 2) 关门以后,提醒借书还书情况的对话框里面读者姓名、柜门名字、借还册数的文字字号变大一倍左右。另外增加了借还册数的语音提示。 3) 读者在书柜以外(例如内务前端)进行人脸登记,刷新了人脸照片,dp2ssl 书柜界面会在同步时自动删除本地缓存的照片文件,这样当该读者在书柜刷卡的时候就能显示最新的照片了。 4) 自助借还界面刷读者卡以后直接语音念读者姓名,去掉了“欢迎您”几个字。

1.5.7 (2020/9/22)

1) 读者刷卡书柜开门时候的语音提示增加了内容“取放图书后请及时关门”。

1.5.8 (2020/9/22)

1) 设置对话框底部增加“关闭”和“打开触摸键盘”两个按钮。

1.5.9 (2020/9/23)

1) 书柜读者信息界面显示的在借册信息,当读者记录中有旧版本 dp2library 写入的 borrows/borrow 元素时,因为缺乏 oi 元素,书目摘要文字显示为“(空)”的问题已经解决。解决方法是把严格要求 oi 改为宽松使用 oi 属性。

1.5.10 (2020/9/23)

1) 写入书目摘要本地库的算法修改,直接 Add,如果捕获异常表示 PII 已经存在则改为 Update。 2) 远程命令 dialog 查看对话框功能改进,忽略隐藏状态的窗口。

1.5.11 (2020/9/24)

1) 将绿色版探测升级时间间隔改为一个小时。 2) dp2ssl 本地计算借阅权限时,遇到较早的读者记录中 borrow 元素没有 oi 属性的情况,做宽容处理。也就是说构建的 PII 字符串是没有点的形态。 3) 对 SaveActions() 内的操作逻辑增加了保护,防止并发调用时候发生互相干扰。static AsyncSemaphore _actionsLimit = new AsyncSemaphore(1);

1.5.12 (2020/9/25)

1) 绿色版和 ClickOnce 版更新文件的策略,都改为只要更新成功一次以后便不再检查更新。 2) 远程命令 restart,给 greensetup 传递了参数 delay,这样 greensetup 启动新的 dp2ssl 进程前会等待 30 秒,目的是等待前一个 dp2ssl 进程完全退出。这样可以避免错误日志文件重叠产生。 3) 日志同步读者记录的时候,如果遇到读者记录中没有 oi 元素,会利用 libraryCode 元素推导出 oi 元素。避免在读者本地缓存数据库中创建出 xxxx.xxxx 和 .xxxx 两个 PII 的两条记录,影响断网状态下刷卡。 4) 远程命令增加 rebuild patron cache 命令,可以重新全量创建读者记录本地缓存。创建的大致过程会发送到点对点群里。

1.5.14 (2020/9/26)

1) 解决全量同步读者记录时偶尔会出现 PII 冲突的问题。

1.5.15 (2020/9/27)

1) FaceCenter 在断网情况下启动,暂时没有 dp2libraryServerUID;当网络恢复以后,会自动重新获得 dp2libraryServerUID。 2) 改进 dp2ssl 借书还书时候发出的点对点消息,增加了读者证条码号和单位名称;读者刷卡时发出的点对点消息增加了刷卡或者人脸、指纹识别类型的文字。 3) dp2ssl 断网情况下读者刷卡时,如果本地没有缓存的证照片,则用一张固定图片代替显示。

1.5.16 (2020/9/29)

1) 馆员上架和下架对话框,把右侧按钮隐藏到 + 按钮之内。 2) 配置参数里面增加“工作人员刷卡免密码时长”参数,缺省值为“无”。

1.5.17 (2020/9/30)

1) 加固点对点消息遇到 dp2server 一端 ConnectionInfo 没有找到情况的恢复处理过程。

1.5.18 (2020/10/15)

1) 智能书柜功能,消除多个读写器情况下,后一个读写器会清掉前一个读写器的同天线序号的门的图书数字的 bug。 2) 工作人员上调入调出图书的功能,凡是有批次号的方式,dp2ssl 都会在对 dp2library 发出(Return() API) transfer 请求的时候,使用 writeLog 风格,这样会确保无论册记录是否发生实质性修改,dp2library 都保证会写入一条操作日志记录。 当 dp2library 在册记录没有实质性修改的情况下写入的操作日志记录,其 style 元素中会包含一个 onlyWriteLog 值,有别于普通的操作日志记录。 dp2ssl 初始化书柜阶段也会对 dp2library 发出 transfer 请求,但这种请求不带有 writeLog 风格,这样只有当册记录发生了实质性修改,dp2library 才会写入一条操作日志记录。注意这个阶段所产生的 dp2library 操作日志记录,是没有 batchNo 元素的。 (注:上述功能需要配合最新版 dp2library(xe) 才奏效) 3) (2020/10/17) NewTagList 类中 GetTagInfo() 函数改掉了一个关于在不同读卡器之间移动标签情况下的 bug。

DigitalPlatform commented 4 years ago

FaceCenter 可断网运行

新版本 1.4 在网络不通的情况下启动 FaceCenter,会自动进入断网状态,本地缓存文件中的人脸特征会正常加入高速缓存,继续提供人脸识别功能。

测试要点

1) 网络通的情况下启动,观察。然后中途断掉网络 2) 网络不通的情况下启动,观察,应该可以利用遗留的缓存文件提供人脸识别功能。然后中途接通网络,利用内务前端修改读者记录,FaceCenter 应能自动同步。 3) 测试一下第一次安装 FaceCenter,然后网络就不通的情况。此时本地并没有缓存文件,所以启动过程应该报错(而不是有缓存文件那时的不报错表现)。实际上 FaceCenter 是根据记忆的断点位置字符串是否为空来进行判断的。

DigitalPlatform commented 3 years ago

工作人员扫入一维码或二维码登录

要制作工作人员身份条码,可以用如下网站在线制作: (一维码) http://qinms.com/webapp/barcode/index.aspx 注意选择 Code128B 类型,因为工作人员的账户名前面要添加一个字符 '~',有很多条码类型是不包含这个特殊字符的。

(二维码) https://cn.online-qrcode-generator.com/

假如工作人员的账户名为 supervisor,那么条码应该制作为 ~supervisor,这样软件才能识别出这是一个工作人员的账户名。

dp2ssl 设置画面专门增加了“工作人员条码输入方式”,只要不是禁用状态,就允许扫入工作人员身份条码。

DigitalPlatform commented 3 years ago

定义借书超额时语音播报的次数

在 shelf.xml 配置文件的根元素下,定义如下片段

<settings>
    <key name="超额时语音播报次数" value="3"/>
</settings>

如果没有定义适当的 key 元素,软件默认播报 1 次。

DigitalPlatform commented 3 years ago

定义菜单页面是否显示图书馆名

在 shelf.xml 配置文件的根元素下,定义如下片段:

  <settings>
    <key name="菜单页面显示图书馆名" value="true"/>
  </settings>

key/@value 属性值可以是 "true" 或 "false",缺省为 "true"。分别表示显示和不显示图书馆名。