2020年4月6日 星期一

for java : pdfbox,為什麼文件呼叫了.load之後頁數一直都是0!?

pdfbox-2.0.19不錯用,大家最常用的是pdf to image這類需求
可是今天試了居然頁數是 0 !?
是這樣寫的
-------------------------------
PDDocument document=new PDDocument();
document.load(new File("E:\\download\\xxxx.pdf"));
PDFRenderer renderer = new PDFRenderer(document);
var pageCount=document.getNumberOfPages();  <--居然是「永遠的0」.....
for(var i=0;i<pageCount;i++){
    BufferedImage image = renderer.renderImage(i);
    ImageIO.write(image, "JPEG", new File("E:\\download\\pdf_"+i+".jpg"));
}
-------------------------------
厚,你嘛幫幫忙,load函數不是這樣用的啦
正確的寫法是這樣的
-----------------------------------
PDDocument document  = PDDocument.load(new File("e:\\download\\xxx.pdf"));
...
...
...
-----------------------------
這樣getNumberOfPages就會是正確的了

居家辦公很悶的,鬧個笑話給各位PG笑一下吧

2020年4月3日 星期五

for java,值為string type的 enum 類別

一般如果是用string做值的enum,比較直接是寫在interface
---------------------
 interface PlayserStatus {
    public final static String PLAY="play";
    public final static String STOP="stop";
}

class Player {
   String  _status;
   Player(String status){
       _status=status;
   }
....
....
}
然後再用
  var player=new Player(PlayserStatus.PLAY);

---------------------
問題是這樣就無法限制Player的建構元一定是要在PLAY/STOP這樣的範圍,太危險了
所以還是enum寫法比較安全
--------------.
enum  PlayerStatus {
     AXN_INIT("init"),
     AXN_PLAY("Play"),
     AXN_PAUSE("Pause"),
     AXN_FINISH("Finshed");

    private final String value;
    private PlayerStatus(String s) {
        value = s;
    }
    public String toString(){
        return value;
    }

}
class Player {
    private  PlayerStatus xntStatus;
    public Player(PlayerStatus status) {
        xntStatus=status;
    }
    @Override    public void setStatus(PlayerStatus s) {
        xntStatus=s;
    }
    @Override    public String getStatus() {
        return xntStatus.toString();
    }
}
....
...
 public void main(String[] args){
  ...  
   ...
   ...
   var player=new Player(PlayerStatus.AXN_INIT);
   System.out.println("status is "+player.getStatus());
   player.setStatus(PlayerStatus.AXN_PLAY);
   System.out.println("status is "+player.getStatus());
   ....
 }
----------------------------------------------------
這樣就可以更具體限制了
有關java enum 類型的更完整的介紹,請參考以下更精闢的好文
https://tpu.thinkpower.com.tw/tpu/articleDetails/1432

同場加映!!
如果是要切換狀態時的行為,不是在player裡大改, 而是由playerStatus決定呢?
------------原班人馬,加個狀態表------------------------------
//先定義行為
interface PlayerIsDoing{
  void doing();
}
class InitPlayer implements PlayerIsDoing{
    public void doing(){
        System.out.println("Doing Init Now");
    }
}
class PlayingPlayer implements PlayerIsDoing{
    public void doing(){
        System.out.println("Playing Now");
    }
}
//再定義行為對映表,順便了解一下直接給值的HashMap的寫法
class HtLolder{
  static PlayerIsDoing p1=new InitPlayer();
  static PlayerIsDoing p2=new  PlayingPlayer();
  static Map<String,PlayerIsDoing> ht=new HashMap<String,PlayerIsDoing>(){
      {
          put("init",p1);
          put("Play",p2);
      }
    };
 }
//改一下enum類別
enum  PlayerStatus {
      
      AXN_INIT("init"),
      AXN_PLAY("Play");
      Map<String,PlayerIsDoing>  ht=HtLolder.ht;
     private final String value;
     private PlayerStatus(String s) {
         value = s;
     }
     public String toString(){
         return value;
     }
     //重點來了!!
     public  void doPlaying(){
         ht.get(value).doing(); <-----------
     }

 }
class Player implements PlayerAction{
    private  PlayerStatus xntStatus;
    public Player(PlayerStatus status) {
        xntStatus=status;
    }
    @Override    public void setStatus(PlayerStatus s) {
        xntStatus=s;
    }
    @Override    public String getStatus() {
        xntStatus.doPlaying();<-----------
        return xntStatus.toString();
    }
}

//it's show time!!
public static void main(String[] args) throws IOException {
    var player=new Player(PlayerStatus.AXN_INIT);
    System.out.println("status is "+player.getStatus());
    player.setStatus(PlayerStatus.AXN_PLAY);
    System.out.println("status is "+player.getStatus());
    ...
    ....
}
--------------------------------------------------------------
這樣,有新行為,player不用改(小改啦),把新行為放在PlayerStatus跟行為對映表所在的套件中
會比較靈活些吧
大家加油吧!!



2020年4月2日 星期四

for ms-sql , 16進制字串轉int, 中文字to byte array,to int.....轉換

先講x0ab1234這類的資料轉int怎麼做吧

----------------
declare @R01 int ;
...
...
set @R01=convert(int, convert(varbinary,'0xE000',1));
--------------------------
其中,這個convert(varbinary,'要轉換的16進制字串',1)格式問題
請參考文件

https://docs.microsoft.com/zh-tw/sql/t-sql/functions/cast-and-convert-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15

再來說中文字hi-byte low-byte怎麼轉成unicode整數
------------------------
declare @TestString nvarchar(1);
set @TestString='你'
print
sys.fn_varbintohexstr(substring(cast(@TestString as varbinary(2)),2,1))
 +replace(sys.fn_varbintohexstr(substring(cast(@TestString as varbinary(2)),1,1)),'0x','')
--顯示為"0x4f60"
--再套用 上例「convert(int.....)」

set @R01=convert(int,convert(varbinary, sys.fn_varbintohexstr(substring(cast(@TestString as varbinary(2)),2,1))+replace(sys.fn_varbintohexstr(substring(cast(@TestString as varbinary(2)),1,1)),'0x',''),1));

--R01為20320
----------------------------------------
驗證方式
請到「字碼查詢」
https://www.cns11643.gov.tw/search.jsp?ID=7&SN=&lang=tw
輸入4F60看看是不是得到「你」字

以上,大家加油了!!

2019年12月30日 星期一

查詢最近一次資料庫被「還原」的時間

---script as below---
WITH MostRecentRestore AS
(
SELECT
RowNum = ROW_NUMBER() OVER (PARTITION BY RH.Destination_database_name ORDER BY RH.Restore_Date DESC),
RH.Restore_date,
BS.[database_name] as Source_Database,
RH.Destination_Database_Name,
BS.Backup_Start_Date,
BS.Backup_Finish_Date,
CASE WHEN RH.restore_type = 'D' THEN 'Database'
  WHEN RH.restore_type = 'F' THEN 'File'
  WHEN RH.restore_type = 'G' THEN 'Filegroup'
  WHEN RH.restore_type = 'I' THEN 'Differential'
  WHEN RH.restore_type = 'L' THEN 'Log'
  WHEN RH.restore_type = 'V' THEN 'Verifyonly'
  WHEN RH.restore_type = 'R' THEN 'Revert'
  ELSE RH.restore_type
END AS Restore_Type,
RH.[Replace],
RH.[Recovery],
RH.Restore_Date AS Restored_On,
BMF.physical_device_name AS Restored_From,
RF.destination_phys_name AS Current_DB_File_Location,
RH.user_name AS Restored_By,
BS.machine_name,
BS.Server_Name
FROM msdb.dbo.RestoreHistory RH
INNER JOIN msdb.dbo.BackupSet BS ON RH.backup_set_id = BS.backup_set_id
INNER JOIN msdb.dbo.restorefile RF ON RH.Restore_History_id = RF.Restore_History_id
INNER JOIN msdb.dbo.Backupmediafamily BMF ON bs.media_set_id = bmf.media_set_id
)
SELECT *
FROM MostRecentRestore
WHERE [RowNum] = 1 AND destination_database_name = '那個資料庫名稱'
-------------------------
參考
https://dba.stackexchange.com/questions/33703/how-to-query-last-restore-date-in-sql-server/33705

2019年10月9日 星期三

for Xamarin IOS project,在VS2019 build時發生找不到檔案的錯誤

時光荏苒,從第一次在2016年用Xamarin寫ios的應用到現在已經從vs2017到vs2019 了(下文的vs2017 & vs2019都是在windows之中,不是在mac上面的)
之前在ipad上面的app一年以來都好好的,最近因為系統加了些欄位
只好重啟專案,連帶地,開發環境也「被升級」--從vs2017升到VS2019才能用圖形設計介面去拖拉修改xib檔
Xib檔可以在vs2019裡改,可是在build時就會出錯,迫於時間緊迫,只好分頭進行--改xib檔在vs2019,而build在vs2017裡進行,然後進行社內布署,先給他們用(很好笑,我知道,但事情就是這樣解決了)

現在終於有時間來看一下之前在vs2019開啟vs2017 xamarin 方案後build的錯誤及解決之道
build時錯誤訊息如下
----------------------------------------------------------------
錯誤 System.IO.FileNotFoundException: Could not find file "/Users/john/Library/Caches/Xamarin/mtbs/builds/xoxoxoxoApp/一個很長的id/obj/iPhone/Debug/actool/bundle/??????????????????60x60@2x.png"
File name: '/Users/john/Library/Caches/Xamarin/mtbs/builds/xoxoxoxoApp/一個很長的id/obj/iPhone/Debug/actool/bundle/??????????????????60x60@2x.png'
  at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x00259] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-06/external/bockbuild/builds/mono-x64/mcs/class/corlib/System.IO/FileStream.cs:274
  at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.IO.FileOptions options) [0x00000] in /Users/builder/jenkins/workspace/build-package-osx-mono/2019-06/external/bockbuild/builds/mono-x64/mcs/class/corlib/System.IO/FileStream.cs:106
....
.......
---------------------------------------------------------------
看到????就應該猜出是中文的東西了,打開info.plist檔,看到有檔案的變數應該就是「XSLaunchImageAssets」 & 「XSAppIconAssets」了
(value分別是「Resources/媒體.xcassets/Launch 影像.launchimage」 & 「Resources/媒體.xcassets/應用程式圖示.appiconset」)

既問題錯在這,那就...來人啊,把這兩個設定屬性都給殺了
然後就build成功了,YA~
是啊,build成功了,可是之前的icon就不見了,app佈署到平板去之後,只留下一個unknow的icon,
這樣能「祭孤」嗎?當然不行.

來吧,先去方案總管視窗,找一下「資產目錄」節點,雖然我知道在vs2017內應該是有子目錄,可是在vs2019因為mac & 這裡的改版之故,「資產目錄」那個節點下面是空的,所以請用滑鼠在「資產目錄」節點點右鍵,「新增一個資產目錄」,預設值就是「Assets」,create後,檔案系統就會在專案的目錄下建立一個"Assets.xcassets"資料夾,

回到vs2019的開發環境,double click「資產目錄」->「Assets」節點,左側會出現「AppIcons」,
double click 它,系統會出現各種尺寸icon檔的對應設定介面給你選
(現在請別再把png放在中文路徑了,建議是直接把之前icon png檔放在 AppIcons.appiconset目錄再選取比較快)

設定好AppIcons之後,別忘了到info.plist把「XSAppIconAssets」變數加上去,值是「Assets.xcassets/AppIcons.appiconset」
plist檔改好,Assets.xcaseets檔也設定好了,那就存檔,重新build,佈署
這樣,你的app的icon就會出現在你的設備上了



下次,不知道又是什麼時候要大改版了,希望是很久以後的事了,
大家加油吧

(相關討論請參考這裡)

2019年9月18日 星期三

for Extjs4 如何讓表單檢查是否有異動時跳過某些欄位

有時表單總是會有些參考/暫時性的欄位,如一個做為「輸出至xxx印表機」的下拉選項
而這欄位是不會存入,照理說在檢查是否異動時也應該被忽略
以前比較笨的寫法是一個一個欄位去檢查有沒有dirty
有了Extjs4真的方便多了,
----------------範例---------
    {
                                    itemId: 'cbxPrintTo',
                                    xtype: 'combobox',
                                    fieldLabel:'請選擇輸出目的',
                                    isDirty: function () {
                                        return false;
                                    },
                                    queryMode: 'local',
                                    valueField: 'AttrCode',
                                    displayField: 'AttrName',
                                    height: 20
}
------------------------------
這樣,在getForm().isDirty()時,這欄位總是傳回無異動的狀態,
如同被忽略/排除不計一般
出處在這裡
(哎,真是的,系統都快寫完了才知道這招......)

2019年8月31日 星期六

c# client <--->QTcpSocket server 中文亂碼解決之道

又是個轉碼的問題,很簡單,怎麼來就怎麼去
-----QT端(先不管.h檔中的宣告),readyRead event slot---------------
void MainWindow::onReadyRead()
{
    QTcpSocket* sender = static_cast<QTcpSocket*>(QObject::sender());
    //從client讀入字串,因為是utf8碼,所以要用fromUtf8轉換
    QByteArray datas = sender->readAll();
    QString msgTxt=QString::fromUtf8(datas); 
    qDebug()<<"msg from client"<<msgTxt;

    //回應給client
    QString  msg2Reply("收到");
    sender->write(msg2Reply.toUtf8());
}
---------------------------------------------------------------
-----C# socket client------------------------------------------
 Socket sender = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
 sender.SendTimeout = 60;
 sender.Connect(remoteIP);
 //轉成utf8碼,送到Qt socket server 
 string msg2Send =("Qt5.12真好用!!");
 byte[] msg = System.Text.Encoding.GetEncoding("utf-8").GetBytes(msg2Send);
 int bytesSent = sender.Send(msg);

  //讀取QtSocket server的傳回值,用utf8碼轉成字串
 byte[] bytes = new byte[1024];
 int bytesRec = sender.Receive(bytes);
 string returnMsg = Encoding.UTF8.GetString(bytes, 0, bytesRec);
 sender.Shutdown(SocketShutdown.Both);
 sender.Close();
-------------------------------------------------

以上,大家加油了




2019年8月20日 星期二

for Extjs4,只列出異動列的異動欄位及資料

有時,你真的不必把所有的資料都帶回去server處理,只要列舉有異動到的列,甚至只要異動到的欄位,
如下
-----------------------------------------------------
 for (var i = 0; i < someStore.getModifiedRecords().length; i++) {
      let change=someStore.getModifiedRecords()[i].getChanges();
     //change裡的資料是 key:資料欄位,value:新值的map/object/ {..... }型態
      ..
      ...
 }
------------------------------------------
很實用的
大家一起加油吧

2019年7月18日 星期四

壓縮 disk image的好工具--pishrink

玩pi的朋友應該多少有點經驗--dd了整張sd的image,結果找不到同樣大小的sd卡restore回去
(明明號稱16G,有的就是只有15.5,有的就是15.9)
總不能每次都多買同型的卡幾張備用吧
最好是能shrink一下,這樣放在其他大媒體區也比較好收藏
這時候就要用pishrink工具了
詳細請參考這裡
經驗之談:
第一:我是在Ubuntu中安裝的,要做這動作的話,就要把img檔從其他網路資料夾中複製過來才能作業(既然是copy past,就真的要等ubuntu copy完才行)
第二:別忘了「 -s 」參數 ex:「pishrink -s src.img dst.img」
(原則上ubuntu的image可以直接在src.img中作業--也就是直接用「sudo pishrink.sh xxx.img 」就可以了,而NOOB就一定要在檔案外--新檔作業,所以保險起見,就用另存新檔的方式作業)


玩看看吧

update: 在壓完了image之後,日後在pi上面一定會需要再擴展大小的,這時請用「raspi-config --expand-rootfs」(jess/strech...)把其他磁區擴大到其sd上面的空間