智能书柜操作逻辑分析
智能书柜是一种新型的设备,它可以持续探测图书是否存在于书柜空间内,因此带来了一些特定的极具价值的新功能和概念,也对软件设计开发提出了一些新的挑战。
本文试图探讨这些功能和概念,并指导智能书柜软件设计开发应用。
谁能打开门,谁来打开门
读者和工作人员都可以从智能书柜面板操作打开柜门。
读者
读者打开柜门,然后可以从书柜里面取出图书,或者把图书放进书柜。一般可以认为从书柜里面取出图书,意味着该读者“借走”这本图书;放进图书,意味着该读者“还回”这本图书。
有两点值得探讨:
-
读者从书柜取走和放回图书的动作可以很快,可以很细碎,一般来说软件完全同步去调用 dp2library 服务器完成借书和还书操作,速度上不一定跟得上。其实也没有必要“跟上”读者动作。可以把读者关闭柜门当作一个结束动作,此时再统一处理借书还书请求:被拿走的图书被认为是读者借走;放回的、原来书柜里面没有的图书被认为是还回。如果在开门状态下,读者反复取和放回一本图书,软件是不对 dp2library 进行请求的,因为还没有到关门的时候。
-
某些操作的不可抗拒性。读者关门的时候软件集中进行处理,前面探讨了。读者关门时候,可以直接离开,不一定会听从软件界面的指示和警告、报错。比如传统来说读者有个借书册数的上限,如果读者从书柜中拿走超过这个数的图书,软件确实是可以警告提示的,包括文字和声音提示,但读者可以不听从建议直接离开书柜。那么这时候软件应该尽可能把借走的图书信息记载下来,和正常借书差不多(加上一些警告信息),这些图书后面可以正常向读者追讨。这个和一般自助借还差别较大,一般的自助借还遇到情况,软件可以拒绝办手续,如果读者在没有办手续情况下拿走图书,门禁是会报警的。但智能书柜一般不会使用 EAS/AFI 门禁报警机制(这一点后面再详述)
注:dp2library 为 borrow() API 增加了一种 overflowable 方式的新操作,用这种方式来借书,如果超出读者当前允许的册数,也会成功,只是借期只有一天。第二天就会超期。主要目的是让读者可以在当天之内还回,满足智能书柜的特殊要求
工作人员
工作人员打开柜门,然后取、放图书,一般可以认为这是业务需要。放进图书可以理解为把图书“调拨”进入智能书柜,此后这一册图书的归属地变更到了这一个书柜。取走图书的含义暂时不明,当然,智能书柜是能感知到这一册图书被拿走了,会变更内存状态。
下面探讨一下工作人员放进图书时,管理软件应该执行的操作:
- 首先册记录的 location 元素要更改到指向这一个智能书柜所在的馆藏地;shelfNo 元素也要更新,反映图书所在的层号(这一操作可选);
- 册记录的 currrentLocation 元素要更新,指向这一个智能书柜。
取走图书的含义,努力思考一下,暂时可以理解为需要记载到一个地方,表示这一位工作人员因为某种原因取走了这一册图书。比如可以记入操作日志(哪怕并不修改任何数据库记录)。如果后来工作人员在工作电脑上执行了调拨流程操作,那么整个动作才完整了,具有了意义。
那么从这里其实我们也可以推论,也许智能书柜管理软件,可以提供一个进入调拨状态的命令。指明后续操作指向的馆藏地。那么工作人员取出图书的时候,自动执行调拨操作,这样工作人员就不用去其他工作电脑上用原来的方法做调拨操作了。
剩下的一个疑问就是:管理流程和软件,是否允许工作人员无目的地拿出图书?鉴于前面提到的智能书柜有一些操作不可抗拒的特点,所以也需要考虑一下这种无目的的情况(从管理软件角度)如何最妥善地处理。
开机启动
智能书柜理论上是持续运行的,它可以监控图书的进进出出。
但第一次开机的时候是什么情况呢?可以简单分为:开机时候软件检测到书柜里面已经有图书;检测到书柜里面没有图书。两种情况。
开机时候检测到书柜里面已经有图书,管理软件就要初始化一个内存列表,这个列表表示当前在书柜里面的图书。同时软件需要尝试对每一册图书都进行一次“还书”操作,这样避免存在借阅状态的图书后面遇到读者取走时候请求借书发生报错。
开机时候如果书柜里面没有图书,后面可能工作人员和读者会往里放进图书,这个分别按照调拨和还回处理即可。
错放
智能书柜促使我们去重新思考册记录的馆藏地,代表什么,怎么才能做到完美。
考虑读者会把不属于智能书柜管辖的图书也放入智能书柜,而且这种操作具有不可抗拒性,我们要审视一下这种情况的意义和处理方法。
除了这一册图书不是属于智能书柜外,那么从读者的角度,她/他是希望还书的,因为图书放进去就意味着还给图书馆了。那么管理软件对还书的操作不能少,尤其注意要处理 RFID 芯片的 EAS/AFI 更改(虽然正常情况从智能书柜取走图书并不会修改 EAS/AFI。这一点后面详细探讨)
同时,管理软件似乎应该给管理员发出警告,表示有一本书被错放进入了智能书柜,请管理员速来拿走处理。
智能书柜面板的显示图书信息的界面,要考虑到这种错放的图书也应该可以显示和查询。只是显示状态要和属于智能书柜管辖的那些图书区别开来,便于工作人员识别了解。
对于错放进来的图书,软件处理时要把册记录的 currentLocation 元素修改,以反映当前实际位置(虽然是放错的位置),但 location 元素不应修改。因为 location 元素只能只应被管理员用调拨操作修改。location 元素反映的是原始馆藏地,currrentLocation 元素反映的是实际馆藏地。
自助借还机还书时候,可能会提示还书者把某一些被别的读者预约的(刚被还回的)图书放进智能书柜,以便预约者接到通知来图书馆自行取走。当然这一册图书可能会被错放到其他智能书柜,总之,currentLocation 元素反映了实际放入的位置,所以预约者接到的通知信息总不会错。(注意,放进去了才会发出通知,而不是以前的还回时刻就通知。其实也可以通知两轮:还回时候通知已被还回到某个自助机;放入智能书柜的时候再通知一次图书现在到了智能书柜)
谈到错放位置,其实工作人员也可能错放。当然,工作人员如果用调拨功能去放入智能书柜,那是不会“错”的,因为管理软件会以实际放入的书柜为准修改 location 元素和 currentLocation 元素。只能说,如果把调拨和上架拆分为两个步骤,第二步上架可能会发生错误,即放入的位置(currentLocation)和理论位置(location+shelfNo)不符。
那么从这里,我们可以推论:工作人员无目的转移图书,可以理解为就是这种错放的情形。似乎允许这种操作也是有意义的 --- 为啥要错放,也许工作人员自己心里有谱,有某种需求才这么做的,软件要保持一定的灵活性,把这类情况考虑进去善加处理。
调拨操作界面设想
工作人员打开柜门,向书柜里面放入图书,可以理解为典藏调拨操作。调拨的意思,就是把 location 元素为其他馆藏地的图书,修改为书柜的馆藏地。因为同时书确实放进去了,所以也要修改一下 currentLocation 元素。
前面的探讨中,提到工作人员有时候也是无(明显)目的放入图书。即,不一定是要调拨。比如,是放入图书,导致图书的 currentLocation 元素修改,但不希望修改 location 元素。等于这一本图书进入书柜,还是“客人”状态,不是“主人回家”状态,这个书柜还并不是这一册的“家”。
最初容易想到,这样可以在面板界面上,给设计两个不同的功能,调拨和上架。其中上架表示不调拨地上架。
但其实可以把两个功能考虑融合起来做成一个界面,即一个界面功能模块。设想这样:当工作人员开门以后,放入第一本图书开始,软件开始自动运算。软件先观察这一册图书的 location 元素是否符合这个书架的地点字符串定义。如果符合,就是“归家”(归架),location 元素并不需要被修改,无所谓调拨。
如果图书 location 元素表明是外来的,软件会弹出一个对话框,询问是否进入调拨状态?这时候工作人员可以确认,也可以不理会这个对话框,继续放入其他图书。如果不理会对话框继续放入,那么这个对话框上会把后来的图书继续追加到一个列表显示。最终如果工作人员过来处理这个对话框,同意调拨,那么就对累积的列表中的图书批处理做一次调拨操作。如果最终工作人员没有过来处理这个对话框,那么多少时间超时以后对话框会自动关闭,调拨操作不会进行。
(但上述流程中,这些图书无论如何 currentLocation 元素会被修改以反映进入书柜这个状态)
这样,两种目的的操作都在同一套界面上得到了满足。
读者操作界面设想
智能书柜的操作,其实概念很简单,可以概括为:读者开门、取书或者放书、关门。当然,开门有一个条件就是读者先要扫读者证进行身份鉴别,这样软件才能知道读者是谁,是否允许她/他开门。
开门以后,到底是放书还是取书,还是二者兼有。开门前是不知道的。所以这个功能既可以借书也可以还书,也可以同时借书和还书。这个动作的菜单名称可以简单直白叫做“开门”,也可以叫“开门(并借还)”。
开门以后,软件会记住开门期间,读者取走的书,和放入的书的信息。等待关门。关门时,软件会自动向 dp2library 服务器请求借书和还书,这时候会出现一个进度条界面,等操作完毕,语音提示读者操作完成或者发生错误。如果发生错误,要看错误的原因。
如果是读者的借书权限不够,读者取走了超过规定数量的书,那么软件会有一个对话框提示读者,请把多出来的图书放回书柜。如果读者(刷卡)开门放回去,这个对话框会消失。但读者很可能不理会这个对话框,把书直接拿走了,那么这个对话框应该是有个超时读秒机制,等超过一定时间读者不响应,软件自动按照超额借阅来处理这些报错的图书。所谓超额借阅和普通借阅差不多,但有关记录的 comment 元素记载一下这个情况。
在实际场景中,有可能是排队等待的下一位读者看到屏幕上上述对话框,“肇事”读者已经走掉了。但没有关系,下一位读者可以继续操作,而这个对话框会自动在超时以后进行后台超额借阅处理。原则上超额借阅请求一定会满足。如果特殊情况无法满足,比如智能书柜发现 dp2library 通讯失败无法访问,那么智能书柜软件要记入一个特殊的日志,后面会自动重试操作。即便是程序退出,重启,这些日志都能保留,不断尝试重试。
(2024/1/24 注) dp2ssl 实际实现相关功能的时候,借书操作虽然超额,但借书操作当时就完成了。读者可以看到对话框提示。读者可以选择在一天之内还回超额部分图书,也可以不还回。如果不还回,就会触发超期违约金。 另外,如果还回的册并不是界面上标注超额的那一册,但只要还回任何一册,dp2library 服务器会自动重新计算超额,尽可能自动消除超额状态。
EAS/AFI
智能书架因为是一种自然的使用方式,从里面取出图书,或者放进去图书,如果要不破坏这种流畅的方式,那么取出(相当于借出)的时候没有办法把 EAS 修改为 Off。因为软件是通过探测到一册图书“不在无线电感知范围”来作为“取出”信号的,此时想要去修改它的 EAS 为时已晚。
放入图书的时候,软件倒是可能会有充足的时间来对这一册图书的 RFID 标签进行修改 EAS 为 On 的操作。
上面说的是一个原因,无法利用 EAS。
另外一个原因,是智能书架很多时候是放在门禁范围以外,无法利用门禁的防盗检测功能。因而软件也就看起来没有必要去修改 EAS。
但这并不意味着智能书架软件完全不考虑 EAS。如果读者或工作人员把本来不是智能书架的图书放入,那么原则上智能书架软件不去修改这样的册的 EAS。但前面谈到过,有时候读者会把(此类原本不是智能书架管辖的)图书放入智能书架,当作“本册已经还给图书馆了”。如果智能书架软件要自动为这些图书办理还书手续,那么就涉及到一个问题:是否要设置 EAS 为 On?
一个可能是不设置。如果馆员接到通知,来智能书架取走这样的图书,(进入门进去以内)去普通书架上架,那么上架之间馆员要记住,用普通还书电脑去修正一下 EAS。这显然是一种麻烦事,馆员很容易忘记这一步操作。
另外一个可能是智能书架软件在此类图书放入时候,自动设置好 EAS 为 On。(注意,是否设置是要判断馆藏地的,原本属于智能书架管辖的图书不做这种设置。) 那么当馆员来取走这些图书的时候,就省去了还要专门想办法设置 EAS 的步骤,很方便了。
另外,在典藏移交的时候,从门禁管辖区以内以内的馆藏地把图书移交给门禁管辖区以外的智能书架,当图书放入智能书架的时候,最好统一设置一次 EAS 为 Off。这样图书被读者拿出的时候,再走出门禁的时候不会遇到报警,这是正常状态。
反之,当馆员从智能书架中拿出图书,并把这种动作作为移交到另外一个馆藏地来处理,那么有可能需要在拿出时候修改 location 元素,这个因为是修改数据库记录,可以做到;但如果要求此时修改图书标签的 EAS 状态为 On,此时为时已晚,那么可能需要馆员在其他电脑去补一次修正 EAS 的操作。(只要放到启动了修正模块的普通电脑的读卡器上就可以办到,因为从册记录馆藏地可以判断出这一册是需要用到 EAS 的)
所以移交,进、出智能书架,两种方向难度不同。看来出智能书架的方向无法形成完整的功能。
dp2SSL 中智能书柜功能用法
在设置对话框中,先设置好必要的指纹中心、人脸中心、RFID 中心URL,然后设置“功能类型”为“智能书柜”。(功能类型默认为“自助借还”)
然后在dp2SSL 的用户文件夹中创建一个文件 shelf.xml。样例如下:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<shelf>
<door name="左第一排" lock="*.1.1" antenna="*:1" shelfNo="一年级一班:1-1" lamp="*"/>
<door name="左第二排" lock="*.1.2" antenna="*:2" shelfNo="一年级一班:1-2" lamp="*"/>
<door name="左第三排" lock="*.1.3" antenna="*:3" shelfNo="一年级一班:1-3" lamp="*"/>
<door name="左第四排" lock="*.1.4" antenna="*:4" shelfNo="一年级一班:1-4" lamp="*"/>
</shelf>
<shelf>
<door name="右第一排" lock="*.1.5" antenna="*:5" shelfNo="一年级一班:2-1" lamp="*"/>
<door name="右第二排" lock="*.1.6" antenna="*:6" shelfNo="一年级一班:2-2" lamp="*"/>
<door name="右第三排" lock="*.1.7" antenna="*:7" shelfNo="一年级一班:2-3" lamp="*"/>
<door name="右第四排" lock="*.1.8" antenna="*:8" shelfNo="一年级一班:2-4" lamp="*"/>
</shelf>
<patron readerName="RL1700"/>
</root>
door 元素的 lock 属性为门锁参数,例如 *.1.1,星号表示任意锁名字,(第一个)1 表示锁控板编号,(第二个)1表示锁的编号(在当前锁控板内)。星号部分也可以用具体的锁名字,如果 COM 口用到多于一个的话。
antenna 属性为读卡器天线参数。例如 *:1,星号表示任意读卡器名字,1 表示天线编号。星号用法其实不太好,因为智能书柜一般都有多于一个读卡器(其中一个是 ISO14443A 读卡器),所以最佳做法是使用具体的读卡器名字。另外注意天线编号有的读卡器是从 0 开始,有的是从 1 开始,要看具体读卡器型号的说明。(注:RL8600类型的读写器是从0编号的)
shelfNo 属性为架号。一般格式为"书柜位置:竖列号-排号"。书柜位置要根据 dp2library 是否使用了分馆模式,来具体配置。使用分馆模式例如"希望一小/一年级一班";没有使用分馆模式例如“一年级一班”。
lamp 属性为灯名字。目前只能使用 "*"。如果属性值为空,或者没有定义此属性,表示此柜门打开的时候不需要开灯。受目前智能书柜硬件限制,所有定义了 lamp 属性的 door 元素,都共同决定全体灯的亮灭。具体算法是,只要有一扇门开着,那么(定义了 lamp 属性的)所有门内的灯会一齐打开;如果全部门都关上了,那么(定义了 lamp 属性的)所有门内的灯会一齐关掉。
door 元素还可以有一个特殊属性 type,如果值为 "free",表示这不是一个普通的处于柜门内的读卡器,而是一个不属于任何柜门的读卡器。可以这样配置书柜屏幕下方原本用于识读证卡的那个小读卡器,它也可以形成一个“门”的界面,但里面的图书放入和取出不会触发还书和借书请求。
patron 元素的 readerName 属性定义了读者证读卡器的名字。具体名字需要在 RfidCenter 的“操作历史”属性页中观察确认,上面举例的 "RL1700" 不一定符合实际情况。如果不定义 readerName 属性,则 dp2SSL 会持续不停对图书读卡器进行盘点操作;如果正确定义了 readerName 属性,则 dp2SSL 在智能书柜所有的柜门关闭后,会自动暂停对图书读卡器的盘点操作(有门打开时会重启盘点操作)。
请求完成后对话框显示的改进
最前面显示了请求操作的读者姓名。借书和还书操作笔数。然后是用彩色文字醒目显示失败的、警告的和成功的笔数,以便读者看到后进行针对性的处理。
然后是每一笔操作的详细信息。用醒目的颜色显示出操作的状态,报错信息用红色底色文字。
对话框还伴有语音提示。主要是在读者发生超过权限借阅的时候,提醒读者立即放回多拿的图书。
2019/11/7 改进
-
柜门控件的字体按照比例进行自动调整。打开门时候的底色从深红色修改为浅蓝色。关闭时候的底色为深绿色没有改动。"e:1"字体颜色改为红色。
-
书柜页面右侧的读者区可以用手指卷动了
-
借还请求结果对话框文字内容可以用手指卷动了
-
读者刷卡后,右侧出现读者信息。点门打算开门的时候,增加了一个对读者记录状态检查的步骤,如果状态不正常则报错并拒绝开门。
检查的算法如下:
- 检查 state 元素。内容不为空则算作不正常;(读者证挂失等情况)
- 检查是否有 overdue 元素。有则算作不正常;(有已经还书、尚未交费处理的超期情况)
- 检查 borrows/borrow 元素里面的 returningDate 属性,如果发生超期则算作不正常 (有在借图书的超期情况)
注意测试 shelf 之间 door 数量不一致的情况
假设有这样一个 shelf.xml 配置内容:
<?xml version="1.0" encoding="utf-8" ?>
<root>
<shelf>
<door name="第一排" lock="*.1.1" antenna="M201:1"/>
<door name="第三排" lock="*.1.3" antenna="*:3"/>
<door name="第四排" lock="*.1.4" antenna="*:4"/>
</shelf>
<shelf>
<door name="第一排" lock="*.1.5" antenna="*:1"/>
<door name="第二排" lock="*.1.6" antenna="*:2"/>
<door name="第三排" lock="*.1.7" antenna="*:3"/>
<door name="第四排" lock="*.1.8" antenna="*:4"/>
</shelf>
<patron readerName="RL1700"/>
</root>
可以看出第一个 shelf 元素和第二个 shelf 元素它们下级的 door 元素的数量是不等的。dp2ssl 应能正确显示出这样的三个和四个门的界面。
2019/11/9 改进
-
柜门已经打开的状态下再点柜门,会语音和对话框提示“已经打开”
-
柜门控件增加“i”按钮,点它可以查看该门内当前存在的所有图书详情。另外 + 和 - 数字部分也可以点按,作用是查看增加和减少的图书详情
-
在书架页面返回菜单页面的时候,如果有门开着,会提示关门以后才能返回
-
书架页面右侧读者信息区,所显示的在借册,现在可以显示“超额”和“超期”两种状态,状态文字用特殊的底色显示。注意需要和最新版 dp2library(dp2libraryxe) 配套使用
(dp2library 最新版也做了配套改进,超额借阅的时候,读者记录中 borrow 元素会有 overflow 属性;册记录中会有 overflow 元素)
-
读者信息中在借册的显示,原来的显示效果超期日期部分文字会被切掉,现在已经改进
-
shelf.xml 配置文件中 door 元素可以配置 lock 属性为空的情况。这样的门如果尝试在界面打开,会提示说不存在门锁
-
发出还书请求的时候,改用非验证还书方式
2019/11/11 改进
- shelf.xml 中,door 元素要配置属性 shelfNo。具体用法见上面 shelf.xml 格式介绍的段落
- 伴随还书请求增加了“转移”请求。目前的做法是修改图书的 currentLocation 元素内容为当前架号。当前架号在 shelf.xml 中的 door/@shelfNo 中定义
- dp2library/dp2libraryxe 中 Return() 元素的 strStyle 属性中 currentLocation 子参数要进行 Unescape()。为此这两个模块升级了版本
- dp2circulation 的日志窗里面为册显示格式增加了 shelfNo 元素和 currentLocation 元素的显示
在 shelf.xml 中定义背景图和柜门位置
可在 shelf.xml 中定义一个图象文件作为柜门的背景图,并精确指定柜门的位置。
样例 shelf.xml 文件如下。其中 ... 的表示略去的部分,可按照实际情况填充。
<?xml version="1.0" encoding="utf-8" ?>
<root width="525" height="487" backImageFile="shelf.png" >
<shelf left="21" top="30" width="144" height ="361">
<door ... />
<door ... />
<door ... />
<door ... />
</shelf>
<shelf left="178" top="30" width="144" height ="361">
<door ... />
<door ... />
<door ... />
<door ... />
</shelf>
</root>
其中 root 元素需要定义 width 和 height 两个属性,表示图片文件的宽度和高度。还需要 backImageFile 属性来定义背景图象文件名。图象文件要放在 shelf.xml 同一个目录中。
shelf 元素需要定义 left top width 和 height 四个属性,表示这一组柜门的左上角位置和宽度、高度。可以用一个图象编辑软件打开图像文件,然后记下一组柜门需要定位的坐标,用在 shelf.xml 定义这几个属性。
注意 root 元素的 width 和 height 属性,与 shelf 元素的 left 等四个属性之间,应该使用同一种计量单位和坐标系。倒不一定要求是像素数,只要是比例合适的数字值就行(比如毫米数)。
如果这些表示位置的属性都没有定义,那么软件会自动按照多组柜门横向排放来进行处理。这些属性要么完整都定义,要么都不定义。不要出现只定义了其中一部分属性的情况。
2019/11/12 改进
- 转移操作的提示信息里面增加了显示转移目标位置,显示在一个括号中
- 从这一层拿出图书放到另外一层,最后是会提交一个转移请求的。以前的版本这里不提交请求
- door 元素的 lock 属性格式变化为 x.x.x 形态。参见 shelf.xml 文件说明
- 书柜背景图
2019/11/13 改进
- RfidCenter 中增加了灯控功能和 API TurnShelfLamp()。注意要先在“配置参数”属性页启用灯控 COM 口。“文件”菜单里面增加了开灯和关灯的命令
- shelf.xml 中 door 元素增加了 lamp="*" 配置,这样定义了以后,当柜门打开的时候,所有灯会自动点亮
2019/11/17 改进
- RfidCenter 的 API ListTags() 中 reader_name 参数中读卡器名字符串规则做了修改,可包含天线编号。例如
RL8600:1|2|3|4。style 参数中表达天线编号的用法被取消。测试时注意观察智能书柜天线小灯的亮灭情况是否正常 - dp2SSL 中柜门打开后会记住(打开它的)读者身份。这样右侧读者信息随时可以被清除,并且清除时不再触发 submit。submit 改为由每个门关闭的动作触发。
- 刷读者证以后,右侧读者信息区自动开始读秒延迟自动清除。增加了“固定”checkbox,可以固定读者信息不被自动清除。增加了“清除读者信息按钮”,可以随时手动清除读者信息。
2019/11/17 改进
- rfidcenter 尝试了 GetTagInfo() 时候也关闭射频,看看是否解决了(初始化结束后)最后一排天线信号灯常亮的问题;
- 读者信息区固定和清除按钮都用了新的样式;
- 提交请求的对话框允许在它显示的时候,后继继续发生请求,并可以按“继续”按钮观察后面的处理结果。这需要先后打开多个门然后几乎同时关闭来进行测试
2019/11/25 改进
- 初步实现工作人员身份登录进行典藏移交的功能。
如何制备一张工作人员 RFID 卡?可先在内务读者窗里面制备一张 ISO15693 读者卡,然后用“RFID工具窗”将 PII 修改为工作人员的用户名,但需要注意前面增加一个波浪号字符。例如,对于工作人员账户 supervisor 来说,PII 应该是 ~supervisor。然后就可以在 dp2SSL 中使用了
在用工作人员身份取出图书以后,请求提交时,会自动弹出一个对话框询问是否向外移交,需要输入一个目的馆藏地。如果选择要向外移交,那么相关册记录的 location 元素会修改为在对话框中选择的馆藏地。如果选择不向外移交,则不对这些图书执行任何操作。
在用工作人员身份放入图书以后,请求提交时,会自动弹出一个对话框询问是否向内移交。如果要进行移交,那么册记录的 location 元素会被修改为智能书柜所在门的馆藏地字符串。如果不进行移交,则只进行上架操作(所谓上架操作就是将册记录的 currentLocation 元素修改为智能书柜所在门的 shelf.xml 中 shelfNo 属性配置的那个字符串)。无论是否选择移交,上架操作都会进行。
dp2ssl 的基本配置参数中,有一个工作人员账户用来做各种操作。当用工作人员卡刷卡登录后,典藏移交操作会改用这个卡上的工作人员身份来进行。测试的时候请注意在操作后,用内务的日志窗来检查 operator 是否正确。不过,测试可以分两种情况:一种是这两类账户是不同账户;一类是这两类账户用了相同一个账户。
- 测试时候请注意测试典藏移交以后,在 dp2SSL 界面上看到的相关图书详细信息的馆藏地信息要正确刷新。每个图书信息方块的下方,左边是归属馆藏地,右边是当前位置。
2019/11/26 改进
-
在设置画面修改配置 RfidCenter URL 之后,回到主菜单画面,“注册人脸”菜单项能及时出现或者隐藏。
-
智能书柜还书请求的时候会顺带自动修改 RFID 芯片的 EAS/AFI 为 false。这一点在书柜使用中要引起注意,凡是放入过书柜的图书,被工作人员取走去图书馆普通书架上架之前,要记得专门修正一次 EAS 才行。当然,读者从智能书柜借书后,如果要还到普通书架,在普通书架自助借还机或者出纳台用内务进行还书的时候,EAS 是会被自动修改为 true 的,这一点不用担忧。
-
智能书柜尝试还书时如果发现图书原本就没有被借出,还有转移时册记录没有发生实质性修改,这两种情况在显示请求结果的时候都从 warning 改为 information 级别了。
-
开门前要对刷卡的工作人员或者读者所属的馆代码进行检查,看看是否能管辖将要打开的门(在 shelf.xml 的 shelfNo 属性中定义的)馆代码。
-
原先 dp2library 的 Borrow() API 不允许一个分馆的读者借阅另外一个分馆的图书。同时,Return() API 也会检查册记录的馆代码和借阅这一册图书的读者记录的馆代码是否符合,不符合则会报错,无法完成还书操作。引入智能书柜以后,首先用 overflow 风格的 Borrow() API 可以让一个分馆的读者临时借阅另外一个分馆的图书。这样就需要改进一下 Return() API,去掉上述检查,以允许这种状况的还书完成。dp2library 和 dp2libraryxe 都更新了
-
扫工作人员卡的时候,会自动出现一个输入密码的对话框。这里的代码做了重构。当对话框弹出的时候,保持对话框这样,又去扫另一张读者卡或者工作人员卡,这时候当前的登录对话框会自动关闭,相当于取消登录的效果。但后面扫这一次卡仅仅起到关闭刚才登录对话框的作用,并不会显示这一张卡的信息。再继续扫就会正常出现读者信息。
-
原先版本在开门点按屏幕的瞬间就会给门赋予操作者身份(表现为门上会显示操作者姓名)。但这样做是有问题的,假设一个关门的动作,其信号比较迟才能到达处理程序,如果此前(因为点按屏幕上的门)而抢先修改了这个门的操作者身份,则后面到来的关门信号会无法正确提交请求,因为操作者错乱了。正确的做法是,点按屏幕的时候,在一个命令队列里面放入一个命令,当门开信号到来时才去队列中取出命令,按照命令要求修改门的操作者身份。这部分代码做了重构。
RfidManager 类用一个线程扫描 RFID 标签,和门的状态。这样做可以确保 RFID 标签的变化信息和门状态变化信息的先后顺序关系正确。即便消息反应慢,但两者的先后关系不会发生错乱。例如,考虑这样一系列事件:1) 放入或者取走标签;2) 然后关门。上述处理办法可以确保 2 始终在 1 的后面发生。
2019/11/27 改进
- 修正了典藏移交时候显示的图书信息出现等待动画和内容不全的问题
迷你书柜的 shelf.xml
<?xml version="1.0" encoding="utf-8" ?>
<root width="455" height="690" backImageFile="mini_shelf.png" >
<shelf left="68" top="73" width="231" height ="550">
<door name="第一排" lock="*.1.1" antenna="RD242:1" shelfNo="智能书柜:1-1" lamp="*"/>
<door name="第二排" lock="*.1.2" antenna="RD242:2" shelfNo="智能书柜:1-2" lamp="*" />
<door name="第三排" lock="*.1.3" antenna="RD242:3" shelfNo="智能书柜:1-3" lamp="*"/>
<door name="第四排" lock="*.1.4" antenna="RD242:4" shelfNo="智能书柜:1-4" lamp="*"/>
</shelf>
<patron readerName="RL8600"/>
</root>
大书柜的 shelf.xml
<?xml version="1.0" encoding="utf-8" ?>
<root width="867" height="905" backImageFile="shelf.png" >
<shelf left="295" top="147" width="263" height ="652">
<door name="第一排" lock="*.1.1" antenna="RD5100:1" shelfNo="智能书柜:1-1" lamp="*"/>
<door name="第二排" lock="*.1.3" antenna="RD5100:3" shelfNo="智能书柜:1-3" lamp="*" />
<door name="第三排" lock="*.1.5" antenna="RD5100:5" shelfNo="智能书柜:1-5" lamp="*"/>
<door name="第四排" lock="*.1.7" antenna="RD5100:7" shelfNo="智能书柜:1-7" lamp="*"/>
</shelf>
<shelf left="575" top="147" width="263" height ="652">
<door name="第一排" lock="*.1.2" antenna="RD5100:2" shelfNo="智能书柜:1-2" lamp="*"/>
<door name="第二排" lock="*.1.4" antenna="RD5100:4" shelfNo="智能书柜:1-4" lamp="*" />
<door name="第三排" lock="*.1.6" antenna="RD5100:6" shelfNo="智能书柜:1-6" lamp="*"/>
<door name="第四排" lock="*.1.8" antenna="RD5100:8" shelfNo="智能书柜:1-8" lamp="*"/>
</shelf>
<patron readerName="RL8600"/>
</root>
读者有违约的情况
读者有违约的情况,则无法刷卡打开智能书柜的门。这样做有什么缺点?如何减少读者的困扰?
2019/11/29 改进
- 读者信息区增加了显示读者记录状态和一些可借册数的界面元素。
- 延长亮灯的时间。从读者刷卡伊始就亮灯;全部柜门关闭后,并不立即灭灯,而是要延迟十秒钟,没有任何操作了才灭灯。如果延迟过程中又由读者刷卡了,则灯一直不会灭,直到这后一个读者关门和停止操作以后才灭灯。
- 书柜页面左侧增加了显示亮灯状态的小方块。
- 对 dp2ssl 错误日志中记载的两类异常情况巩固代码。初始化阶段如果标签数据不正确会警告,但初始化过程会正常完成,有问题的标签被跳过。Entity.Clone() 里面的一处空指针异常的代码得到修正。
- 书柜页面可以用条码枪扫入读者证条码号。键盘输入的则不起作用。
2019/11/30 改进
- 刷卡后柜门控件颜色动画
- 延长“延迟自动清除读者信息”的时间到 20 秒。延迟关灯的时间也延长到 20 秒
2019/12/5 改进
- 优化了探测门锁状态的速度。方法是给 RfidCenter 的 ListTags() API 增加了同时执行 getLockState 操作的能力。ListTagsResult 这个结构里面也增加了 GetLockStateResult 这样一个成员;
- dp2ssl 从这个版本开始检查所连接的 RfidCenter 版本。给 RfidCenter 的 GetState() API 增加了一个 getVersion 动作;
- 改进了语音汇报从书柜里面取出和放入图书的方式,改为先滴滴响几声,涉及到几册图书就响几声,然后再追加一个语音“取出”或者“放入”。
2019/12/6 改进
- 原先书柜开门时候弹出的等待对话框取消了,代之以在具体的门上显示一个动画表示正在开门。等收到门打开状态后动画会消失。在正在开门状态时如果用手指点门,会语音提示“正在开门”,这一次点按动作会被忽略。这一个改进的目的是允许操作者快速的顺序点按多个门打开它们。
高度小一点的
<?xml version="1.0" encoding="utf-8" ?>
<root width="866" height="709" backImageFile="shelf_compact.png" >
<shelf left="294" top="28" width="263" height ="651">
<door name="第一排" lock="*.1.1" antenna="M201:1" shelfNo="智能书柜:1-1" lamp="*"/>
<door name="第二排" lock="*.1.2" antenna="M201:2" shelfNo="智能书柜:1-2" lamp="*" />
<door name="第三排" lock="*.1.3" antenna="M201:3" shelfNo="智能书柜:1-3" lamp="*"/>
<door name="第四排" lock="*.1.4" antenna="M201:4" shelfNo="智能书柜:1-4" lamp="*"/>
</shelf>
<shelf left="574" top="28" width="263" height ="651">
<door name="第一排" lock="*.1.1" antenna="M201:1" shelfNo="智能书柜:1-1" lamp="*"/>
<door name="第二排" lock="*.1.2" antenna="M201:2" shelfNo="智能书柜:1-2" lamp="*" />
<door name="第三排" lock="*.1.3" antenna="M201:3" shelfNo="智能书柜:1-3" lamp="*"/>
<door name="第四排" lock="*.1.4" antenna="M201:4" shelfNo="智能书柜:1-4" lamp="*"/>
</shelf>
<patron readerName="RL8600"/>
</root>
小白盒子扫码问题
小白盒子在扫入手机屏幕上显示的我爱图书馆微信公众号里面的读者二维码的时候,一般的键盘拦截代码处理都会有问题,表现是大小写发生混乱。
经过检查,发现小白盒子在处理大写字符的时候,是这样的击键顺序:
LShiftKey kbdStruct=KeyCode:160,ScanCode:42,Flags:0,Time:261261875,ExtraInfo:0; intParam=256
P kbdStruct=KeyCode:80,ScanCode:25,Flags:0,Time:261261890,ExtraInfo:0; intParam=256
LShiftKey kbdStruct=KeyCode:160,ScanCode:42,Flags:128,Time:261261890,ExtraInfo:0; intParam=257
P kbdStruct=KeyCode:80,ScanCode:25,Flags:128,Time:261261890,ExtraInfo:0; intParam=257
即:LShiftKey down, 'P' down, LShiftKey up, 'P' up
而一般人在键盘上输入的时候是这种顺序:LShiftKey down, 'P' down, 'P' up, LShiftKey up
注 Shift 键抬起的位置。
可能是这种细微差异造成大部分代码在处理小白盒子输入的击键的时候,Shift 状态出现了混乱。
2019/12/9 改进
- 书柜进行 inventory 时进化到只对打开的门对应的天线进行 ListTags() 请求;
- 书柜界面扫入读者证条码功能正确实现;
- 自助借还界面增加了一个配置参数,定义读者 RFID 卡刷卡后读者信息保持(不因为拿走卡而自动清除)。这是为了适应自助借还机在竖直平面安装读者读卡器的情况
- 自助借还界面可以扫入读者证条码。提供了一个专门的设置参数“(自助借还)启用读者证条码扫入”;
- 当条码阅读器不断重复输入的时候,能自动吞掉重复的输入;