tag:blogger.com,1999:blog-1711161798177547192024-02-21T11:19:52.261+07:00budilaksBudi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.comBlogger122125tag:blogger.com,1999:blog-171116179817754719.post-53420077912217966972023-12-17T20:17:00.007+07:002023-12-18T19:55:59.255+07:00Pullback atau Delayed Curvature<p> Pullback atau Delayed Curvature adalah perubahan planarity hanya pada homogenous tile setelah keluar kiln atau bahkan sudah dingin dan panas suhu ruangan. Misalkan saat awal planarity conveks menjadi concave beberapa hari kemudian atau jam, dan kembali conveks bbrp hari kemudian. Atau satu kali terjadi perubahan planarity.</p><p>Ada article dari seorang proffesor mengenai delayed curvature, dan cara mengatasinya yaitu:</p><p>1. terjadi kelebihan silica pada formula</p><p>2. terjadi proses quenching yang terlalu cepat</p><p>Lanjut.</p><p>Hal tersebut diatas mendapatkan bukti literature di internet sbb:</p><p>PORCELAIN TILE PYROPLASTIC DEFORMATION DURING FIRING AND POST-FIRING VARIATIONS IN PLANARITY</p><p>A. Bresciani, B. Spinelli</p><p>SACMI Imola S.C.</p><p><span style="font-size: medium;"><b>dikutip :</b></span></p><p><span style="font-size: medium;"><b>An excessive quantity of quartz should not be added in order to avoid problems in the cooling phase owing to the allotropic transformation of the β quartz − α quartz phase at 573°C, the problems becoming more pronounced as tile size increases.</b></span></p><p><span style="font-size: medium;"><b><br /></b></span></p><p>Dan dari :</p><p>“DELAYED CURVATURES IN PORCELAIN TILE. ANALYSIS AND MEASUREMENT OF</p><p>INFLUENCING FACTORS” V. Cantavella(1), J. García-Ten(1), E. Sánchez(1), E. Bannier(1), </p><p><br /></p><p><span style="font-size: medium;"><b>Dikutip dari literature diatas :</b></span></p><p><span style="font-size: medium;"><b>The existence of residual stresses in ceramic tiles may be due to two causes: stresses caused by rapid cooling of the tile in the industrial kiln, associated with thermalgradients in the piece, and stresses produced by the glaze–body fit.</b></span></p><p><br /></p><p>Diatas ini teorinya, prakteknya sudah saya lakukan sebelum saya menemukan literature diatas. Karena praktek tanpa ilmu tak akan ada orang percaya. Maka saya lakukan pencarian di internet untuk literature delayed curvature. Banyak juga saya dapatkan, tetapi kedua literature diatas sangat dekat dengan praktek saya.</p><p>Secara formula mudah, kurangkan silica saja. Tapi akan mempengaruhi properti keramik yang dihasilkan. Dan kecepatan tinggi kiln akan menyulitkan. Pada literature bbrp hal bisa dilakukan, seperti menambahkan clay, merubah feldspar dan atau menurunkan silica.</p><p>Untuk rapid cooling, saya temukan tekniknya yang diajarkan oleh alm mentor saya. Yang mana saat itu tanpa teori, hanya ikuti petunjuk setting rapid cooling sedemikan sehingga tidak terjadi delayed curvature. </p><p>Lanjutkan nanti lagi detail setting rapid cooling, dasar teori rapid cooling adalah quenching pada proses tempered glass (kaca mobil).</p><p><br /></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-6155485503187569182023-12-17T18:16:00.001+07:002023-12-17T18:16:17.770+07:00Dimple<p> Dimple sangat mudah terjadi pada keramik glossy.</p><p>Dapat dilihat pada glazeline saat glaze belum mengering.</p><p>Banyak yang menduga karena debu, sehingga glaze line sering saya lihat ditutup bagai keranda. Padahal setelah beberapa waktu maka akan terkumpul debu dan menyatu menjadi makin besar dan tertiup angin dan terbang ke keramik yang basah. </p><p>Sebenarnya, dimple berasal dari adanya engobe pada glaze. Engobe memiliki komponen serupa body sedangkan glaze memiliki banyak fritz teruntuk glosssy. Oleh karena itu, sejak lama dilakukan pencucian ballmill dan tank bila mengganti isinya.</p><p>Dari hasil pengamatan saya, walau dicuci, masih akan tersisa engobe didalam ballmill ataupun tank. Spt kata pepatah ada nila setitik rusak susu sebelanga. Pepatah ini seperti cocok dengan engobe dan glaze.</p><p>Hasil pengamatan ini saya dapatkan saat saya memimpin Lab, PPC dan QA sehingga memiliki akses data yang lengkap.</p><p>Dan sejak itu, saya terapkan pemisahan peralatan ballmill, stirrer, tank, pipe, pump dst bagi engobe dan glaze. Setiap tank dituliskan engobe atau glaze. Service tank pada glaze line juga dituliskan agar tidak tertukar, walaupun tetap dicuci setelah tidak digunakan. Transfer tank juga terpisahkan.</p><p>Juga sejak itu saya lakukan topup pada tank Glaze Preparation, sehingga dihemat penggunaan tank. Malahan menjadi berlebih jumlah tank kosong. Ballmill juga begitu, milling berkali kali hingga tank penuh atau 2 tank penuh, tergantung jumlah order.</p><p>Nah jadi malah hemat air, hemat tank, dan terutama DIMPLE tidak pernah terjadi lagi.</p><p>Sebetulnya ada terjadi, 8 bulan kemudian, langsung saya tracing dan ada satu dokumen dicoret coret, tapi saya yakin inilah yang terjadi. Operator salah mengisi tank dan kebetulan dia menuliskan di dokumen. Karena dokumen bernomor urut sesuai standard ISO, sehingga supervisor hanya bisa mencoret coret hampir tak terlihat nomor tank sebelumnya. Tapi dokumen ini bertepatan saat kejadian dimple tersebut.</p><p>Demikian cerita saya mengenai kasus DIMPLE, yang sangat sederhana mengatasinya.</p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-21835092904685846642023-05-07T17:21:00.002+07:002023-05-07T17:21:34.521+07:00Production Process AnalystPada dahulu kala, boss mendapatkan laporan dari Manager Production, bahwa diperlukan penambahan ballmill karena tidak bisa supply 4 kiln high speed high productivity.<div><br /></div><div>Saat itu langsung boss memanggil saya, tuk memeriksa apa penyebabnya. Karena menurut perhitungan beliau, jumlah ballmill yang terpasang sudah mencukupi. Dan area kosong tuk ballmill baru adalah tuk ekspansi penambahan 2 kiln lagi yang lebih panjang dan besar.</div><div><br /></div><div>Tentu saja saya harus membuat laporan berdasarkan bukti data dan dipresentasikan dengan jelas dan hanya selembar kertas (baca buku one minute manager, tuk kenapa harus selembar kertas).</div><div><br /></div><div>Saya kisahkan mesin nya adalah satu unit wheel loader, satu unit weighing scale static 20 ton dan 7 ballmill kapasitas material 20 ton (ballmill 34000).</div><div><br /></div><div>Pengisian 20 ton bbrp material dalam satu unit static weighing scale memerlukan waktu berkisar 10 menit, proses pengiriman ke ballmill (loading) memerlukan waktu 15 menitan dan milling material memerlukan waktu 8 jam (homogenous product) serta proses pengeluaran slyp ke slyp tank dari ballmill membutuhkan waktu 15 menit.</div><div><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFQd2AN_19PxM9sKtwmiJwJElI-2bbudUGlTg9MRx5LMhTKuB-81JNudGk_iXfeLPL0-x_ffEEmDakeVdKwB-pOipsQBN-oXoGB5ev4-XvfQQQmQGGhQuPrKC_OaYAH6Rkp3N63bc_sXU58-pXETpuzK_6Q0VoBA1Z50bFbwAKS7He1VX3NAyoyIbRqg/s423/BodyPrep.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="175" data-original-width="423" height="269" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFQd2AN_19PxM9sKtwmiJwJElI-2bbudUGlTg9MRx5LMhTKuB-81JNudGk_iXfeLPL0-x_ffEEmDakeVdKwB-pOipsQBN-oXoGB5ev4-XvfQQQmQGGhQuPrKC_OaYAH6Rkp3N63bc_sXU58-pXETpuzK_6Q0VoBA1Z50bFbwAKS7He1VX3NAyoyIbRqg/w650-h269/BodyPrep.png" width="650" /></a></div><div><br /></div><div>Saya memerintahkan 2 orang tuk melakukan pencatatan menit demi menit aktivitas di area ballmill, dilakukan 3 shift. Mencatat bila wheel loader mengisi weighing scale, bila selesai, bila mulai kirim ke ballmill nomor berapa, bila selesai kirim (Loading) dan bila ballmill nomor berapa mengeluarkan isinya ke slyp tank.</div><div><br /></div><div>Setelah membaca hasil pengamatan, maka saya minta dibuatkan timeline weighing scale</div><div>selama 24 jam dari pada lembar kertas. Kemudian dibawahnya timeline weighing scale, dibuat masing masing timeline 7 ballmill dengan titik segaris tuk waktu 24 jam.</div><div><br /></div><div>Weighing scale saat wheel loader mengisi, diberi warna kuning, saat weighing scale terisi penuh dan tidak melakukan kirim ke ballmill diberi warna hijau, dan saat weighing scale mengirim material ke ballmill diberi warna kuning.</div><div><br /></div><div>Demikian pula dengan time line ballmill masing masing, saat pengisian (loading) berwarna kuning, saat milling berwarna hijau dan saat pengeluaran (unloading) berwarna kuning.</div><div><br /></div><div>Sudah 2 warna yaa, kurang satu warna lagi yaitu merah. Weighing scale timeline akan diwarnai merah bila kosong dan ballmill timeline masing masing juga demikian. Ballmill timeline akan diwarnai merah bila dari hasil pengamatan pada timeline tidak ada aktivitas alias kosong.</div><div><br /></div><div>Satu lembar kertas berisi 8 timeline, saya berikan ke boss. Dan boss tidak membaca keterangan warna merah, tapi langsung bertanya ke saya apa artinya merah. Saya bilang artinya kosong tidak ada kegiatan atau tak ada isi. Saya juga bilang ada ditulis di keterangan warna.</div><div><br /></div><div>Penjelasan:</div><div>Warna merah pada salah satu ballmill, ternyata bersesuaian dengan warna merah pada weighing scale. Yang artinya saat ballmill membutuhkan diisi, weighing scale masih kosong. Dan saat wheel loader mengisi weighing scale, menjadi berwarna kuning tapi di ballmill ybs tetap merah karena belum selesai mengisi penuh weighing scale 20 ton material.</div><div><br /></div><div>Wuih, boss cepat nangkap hasil laporan. Itulah yang terjadi bila kita melakukan proses analyst dengan baik dan berdasarkan data aktual dan melaporkannya dalam bentuk yang mudah dicerna.</div><div><br /></div><div>Dari sini aku temukan filsafatnya, bahwa aku harus selalu sediakan air panas tuk membuat susu botol bayi. Kasihan kan harus masak air panas dulu sementara bayi sudah haus. Demikian pula bila ada tamu, dibuatkan teh/kopi. Tentu harus sedia air panas dalam thermos, agar tamu tidak segera pulang karena ga ada teh/kopi disediakan.</div><div><br /></div><div>Cerita ini saya tulis setelah menonton YouTube, mengenai job yang tidak/belum tersentuh AI (artifical intelegence). Data Analyst adalah salah satu job yang belum tergantikan. Karena diperlukan luasnya pemikiran, kreatif data apa yang diperlukan dan bagaimana hasil akhir akan dibuat.</div><div><br /></div><div>Process analyst dapat dilakukan disetiap lini pabrik apapun, sehingga process menghasilkan produktivitas yang tinggi. Tidak hanya cepat selesai perbaikan, kadang bukanlah perbaikan mesin tapi perbaikan process. Spt halnya proses cek residu viscosity saya ubah saat menggunakan 2 material tanah saja. Lihat pembahasan formula keramik di blog ini.</div><div>Juga saya buat analisa aging time pada slyp tank untuk mengetahui kapan suatu proses dilaksanakan (pengukuran viscosity) sehingga proses berjalan lancar. Lihat mengenai defloculant.</div><div>Pengukuran viscosity sesaat setelah ballmill turun, adalah salah. Sehingga didapatkan kesimpulan yang salah yaitu tambah garam, dan akhirnya kemudian tambah air karena kelebihan garam yang ditambahkan. Jadi proses yang salah akan mengakibatkan proses semakin panjang.</div><div><br /></div><div>semoga bermanfaat </div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-57878180931890699082023-04-01T16:32:00.001+07:002023-04-01T16:32:50.298+07:00Aging timeSaya coba rangkum aging time:<div>1. Stirrer aging time, sptnya udah saya tulis. Begini, bila viscosity slyp dah stabil maka aging time dah selesai. Hasil pengukuran saya didapatkan 2 jam sejak turun ballmill terakhir pada tank yang dimaksud.</div><div>2. Service tank yaitu tank slyp menuju Spraydrier, ga perlu aging time.</div><div>3. Silo powder memerlukan aging time minimal 8 jam, terbaik 16 jam atau lebih. Kalau ini berdasarkan pengalaman, tentunya setiap jam Spraydrier di monitor output dalam spec.</div><div><br></div><div>Makin pendek aging time akan makin produktif tapi terlalu pendek apakah maksud aging time bisa dipenuhi? Dengan mencari optimum aging time maka kedua hal ini dapat terpenuhi.</div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-15012016032958195642023-03-14T15:29:00.001+07:002023-03-14T15:29:36.528+07:00Display HMI or Application Kiln problems Sering terjadi layar monitor PC menunjukkan data yg aneh atau tidak betul. <div>Contoh pada aplikasi Kiln atau Dryer atau Spraydrier. Dimana data yg di layar monitor tidak sama dengan data di PLC.</div><div>Sebabnya adalah sinkronisasi koneksi tidak terjadi dan kadang pembuat aplikasi tidak membuatkan alarm no connection.</div><div><br></div><div>Cara mengatasi adalah mudah. PLC adalah master, jadi cukup hidup dan matikan aplikasi atau PC monitor agar PC melakukan sinkronisasi kembali.</div><div><br></div><div>Utk selanjutnya, perhatikan tulisan alarm bila ada. Juga bisa perbaikan aplikasi untuk menampilkan no connection alarm dengan lebih agresif alias huruf gede banget ditengah layar monitor sbg pop up.</div><div><br></div><div>Sekian terimakasih.</div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-21296502949707475542023-03-14T12:36:00.001+07:002023-03-14T12:36:07.380+07:00sakit gigiDah lama saya temukan cara menjaga gusi tidak sakit.<div>Saya gunakan Listerine.</div><div>Kalau lagi sakit gigi, kumur trus tiap 2 jam. Awalnya kurangi rasa sakit dan setelah bbrp hari sakitnya hilang. Asalkan jangan minum dingin dan kunyah kacang atau yg keras keras.</div><div>Oh ada lagi kegunaan Listerine, yang ini saya selalu pakai TAR TAR CONTROL dari Listerine. Lbh rasanya enak dan hasilnya gigi bekas rokok atau hitam hitam hilang. Saya kumur Listerine lalu gosok gigi dengan pasta, barulah kumur air bersih. Setiap pagi dan sore. Lama juga habiskan botol besar, krn kumur sedikit aja spt sesendok teh.</div><div>Demikian pengalaman hidup yg baik.</div><div>Akan saya ulas yg lain agar tetap sehat.</div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-15942903275666696742023-03-08T17:38:00.001+07:002023-03-08T17:41:56.299+07:00Why Do We Press<p> Mau cerita bagaimana dan apa itu press pada pabrik keramik.</p><p>Saya dapatkan materi bagus dari boss, dia dah merangkum dalam beberapa lembar paper.</p><p>Dari sini saya mengenal press dan bagaimana sebab akibatnya.</p><p>Bagaimana memainkan drawer, apa guna liner, bahan liner titanium dengan besi biasa, rubber polyurethane, membuat gambar die untuk pojok/bumper/motif dll.</p><p>Juga saya diajarkan oleh boss, mengenai ejection sandwitch. Agar tidak terjadi crack pada green tile. Juga tentang first drop dan second drop.</p><p>Ada suatu pabrik menggunakan 3 pressure, first pressure 10Bar, second pressure 80Bar(lupa lagi) dan final pressure 240Bar. Lalu kenapa begini, kan jadi lambat kerja press. Toh first pressure itu sama dengan berat silinder bila kita turunkan. Jadi saya ubah, dengan melakukan breaking pada saat first pressure dengan sedikit powder berhamburan. Langsung dilakukan pressure 80Bar dan dilanjutkan 240Bar. Jadi hanya 2 pressure dan satu deaeretion. Strokes press per minute bisa jauh naik.</p><p>Pernah boss membuat formula, diperkirakan akan laminasi. Jadi saya diperintahkan untuk menaikkan waktu deaeration. Langsung joss dan formula powder berjalan sempurna. Boss saya mengenal formula dan bisa memprediksi kejadian pada SD, Press dan Kiln. Juga saya diminta untuk membuat Naber Electric Kiln tuk sesuai kurva Kiln. Jadi saya plotkan timing dan temperature Kiln ke Naber Kiln. Saat dah selesai experimen di Naber, maka diterapkan ke Kiln bisa langsung joss.</p><p>dibawah ini lembaran yang boss berikan pada saya</p><p><a href="https://drive.google.com/file/d/11yO6EGBj604xJ5NeT9ydeJ6WH5vuze-k/view?usp=share_link" target="_blank">Why do we Press</a><br /></p><p><br /></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-28513036114177382622023-02-11T18:44:00.001+07:002023-02-11T18:44:04.509+07:00Arduino SDCard MBR not valid, assuming Super Floppy format.<p> kisahnya dimulai saat dapat tugas membuat scada kecil kecilan, yaitu mendapatkan data meter gas dan listrik dan menyimpannya tuk diolah setiap awal bulan pemakaian energy.</p><p>Jadi dibuatlah counter tuk gas meter per m3 rotation, dan modbus rs485 ke meteran listrik digital. Menggunakan esp32 dan wemos D1 mini.</p><p>Saat menggunakan server sql, terjadi problem tidak disediakan spesifik PC tuk SQL. Jadi saya buatlah menggunakan esp32 dengan SDCard module. Awalnya berjalan lancar, tapi kemudian saya lihat data lama perlahan bertahap hilang. Kemungkinan karena adanya file lain. Atau ada ketidakberesan di sdcard.</p><p>Lalu saya buat baru menggunakan wemos D1 mini, tapi saat install dan dijalankan menemui masalah baru. Yang mungkin saat pakai esp32 tidak saya perhatikan debug error nya.</p><p>Masalah baru adalah ini:</p><p>MBR not valid, assuming Super Floppy format.</p><p>Berkutat, format, sampai pakai MiniTool Partition masih belum berhasil.</p><p>Dua hari dua malam tak kunjung ketemu solusinya, googling sana sini.</p><p>Pada akhirnya ketemu satu jawaban yang menyarankan menggunakan <a href="https://www.sdcard.org/downloads/formatter/" target="_blank">SDCard Formatter</a>.</p><p>Download dan install, lalu coba format dan......</p><p>Yeyyyyyy.......voila.................berhasil berhasil berhasil</p><p>SDcard lancar jaya, dan siap tuk di install program webserver dengan sdcard</p><p>Nanti saya tuliskan program nya di lain post</p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-54949086954948064922022-07-14T20:04:00.002+07:002022-07-14T20:22:20.606+07:00ERH Lit omron ETN21<p> Sebetulnya sudah beberapa bulan, ERH di ETN21 omron saya menyala.</p><p>Bulan lalu, salah satu ETN21 di mesin gak konek ke Weintek HMI, jadi kelabakan. Lalu saya ganti ETN baru, masih ga konek. Lalu clear memory, isi ulang lagi semuanya......Lah ERH mati dan koneksi ethernet jalan sempurna, dengan 3 unit Weintek di ujung ujung mesin.</p><p>Padahal ber bulan bulan, saya coba cari masalah ERH solusinya. Enggak nemu yang berhasil baik.</p><p>Tadi pagi, kena lagi di mesin lainnya yang masih ERH menyala merah. Dan sebetulnya saya tidak ingat apapun solusi di mesin sebelumnya tuk ERH merah dan koneksi ethernet gagal.</p><p>Hampir 4 jam pertama, coba clear memory dan isi ulang. Horee sukses, ERH mati dan koneksi ke 4 Weintek dan satu PLC Lainnya berhasil. Saya check, tcp terkoneksi sebanyak 5. Selama clear memory dan isi ulang transfer all saya gunakan kabel toolbus omron. Kemudian saya coba koneksi laptop dengan ethernet spt biasa saya lakukan, eh ga mau konek. </p><p>Nambah waktu lagi setelah makan siang, periksa semuanya lagi. Kesel kesel, saya lakukan clear memory dua kali berurutan, matikan hidupkan, kemudian isi ulang. Voila......laptop bisa koneksi ke PLC dan nampak di softSW pada ETN21 windows di I/O bagian TCP tertera ada 6 koneksi.</p><p>Akhirnya ERH merah dan koneksi kaga lancar, bisa normal kembali.</p><p>Googling lama ber bulan bulan, ga dapat solusi. Anehnya saya lakukan clear memory setelah konek menggunakan kabel data omron ke CJ1M saya, maka tulisan clear memory dibawah tab PLC hidup (alias ga grey), dan bisa dilakukan clear memory semuanya. Tentu saja DM dll hilang tidak berbekas.</p><p>Dicatat di blog biar enggak lupa</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrtCbT0APyxMQrMRHcCQW6EqaFDvBRFBJRO9m43gJu3YiE9l2ecOnVtTfiAB2bOek_U97v72QUN6KOgz0IibvKRDdEDsOsfByhjjuUEiVh2i66SM1ggliJawAgMCMARRehrDQywa9QP9j-F-GFcJLEiVBNrMXPnGE-TuHr6gIkUEXDs2ZTwy8RMprzHw/s466/clear%20memory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="466" data-original-width="313" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrtCbT0APyxMQrMRHcCQW6EqaFDvBRFBJRO9m43gJu3YiE9l2ecOnVtTfiAB2bOek_U97v72QUN6KOgz0IibvKRDdEDsOsfByhjjuUEiVh2i66SM1ggliJawAgMCMARRehrDQywa9QP9j-F-GFcJLEiVBNrMXPnGE-TuHr6gIkUEXDs2ZTwy8RMprzHw/s320/clear%20memory.png" width="215" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNbeEXJQYJQfVf9rJH1TVmvmRNqijAoHXTW9lk8-k-DtR9vOc9sgsK9Clw3GVUItyOvzGBl5gPnjpRq85gejCtuG5Y6hBJqa1-SSvc2WGyY9QlrpC8nFqHhEKgBea14N3dsdC9-n98qv3kSYtnL46m_NatAMepglZ8jPffWJ4TyyIqmVbYOgKw5yLnXA/s606/softSW.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="496" data-original-width="606" height="262" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNbeEXJQYJQfVf9rJH1TVmvmRNqijAoHXTW9lk8-k-DtR9vOc9sgsK9Clw3GVUItyOvzGBl5gPnjpRq85gejCtuG5Y6hBJqa1-SSvc2WGyY9QlrpC8nFqHhEKgBea14N3dsdC9-n98qv3kSYtnL46m_NatAMepglZ8jPffWJ4TyyIqmVbYOgKw5yLnXA/s320/softSW.png" width="320" /></a></div><br /><p><br /></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-33251939662221778712022-07-14T19:47:00.002+07:002022-07-14T19:47:35.833+07:00Arduino Nano Project P10 display temperature<p>Saya hendak share pembuatan display suhu menggunakan P10 led display matrix size 32x16.</p><p>Menggunakan display bekas pakai, dimana salah satunya terdapat board dengan IC berlabel TK4S dan temperature controller TK4s. Display lainnya kosong hardware, jadi saya buat aja dengan arduino yang murah.</p><p>Fungsi dan peralatan:</p><p>1. P10 display led matrix 32x16 satu unit.</p><p>2. Arduino Nano v3</p><p>3. 6 pushbutton momentary, sepasang tuk Calibration Sensor Suhu, sepasang tuk Setting Max temperature alarm, sepasang tuk setting Minimum suhu.</p><p>4. Sensor fotocell 10-24Vdc tuk deteksi keramik, karena saat ada keramik didekat sensor suhu maka alarm akan diaktifkan.</p><p>5. sensor suhu infrared, 4-20mA output dan vcc 6-24Vdc. Untuk ukur suhu keramik.</p><p>6. power supply 5V 2Amp</p><p>7. step up 12v dari 5v untuk supply sensor suhu dan fotocell</p><p>8. modul 4-20mA to 0-3.3Vdc karena ADC pada arduino mengukur tegangan, tuk sensor suhu</p><p><br /></p><p>Pada awalnya hendak menggunakan potentiometer atau rotary encoder tuk merubah setpoint, tapi setelah ujicoba, saya tak jadi pakai. Karena memerlukan interupt yg hanya ada 2 di arduino. Dan karena nilai setpoint yang diubah kecil saja, golongan byte, hanya antara nilai 70 sampai dengan 150 maka saya tetapkan tuk menggunakan momentary pushbutton.</p><p>Proses pengerjaan programming sbb:</p><p>1. testing P10 display dengan 3 digit fix, kemudian mencari font yang baik.</p><p>2. testing menambahkan suhu dari internal suhu arduino. Agar ga ribet diawal.</p><p>3. Eh mulai berkedip saat perubahan suhu, lumayan mencari solusi agar tidak berkedip. Dibawah akan saya paparkan solusi nya.</p><p>4. menambahkan program setpoint untuk 2 pushbutton up dan down.</p><p>5. menambahkan program setpoint max dan min untuk 4 pushbutton up dandown.</p><p>6. membuat display memunculkan setpoint max dan min saat pushbutton ditekan berkali kali, jadi display yang tadinya memunculkan suhu akan berubah menjadi 2 baris tuk max dan min setpoint. Setelah 3 detik akan kembali menampilkan suhu.</p><p>7. menyimpan setpoint kedalam EEPROM, agar saat mati listrik maka data setpoint tidak hilang.</p><p>8. memasang modul 4-20mA dan sensor suhu. serta fotocell.</p><p>9. uji coba secara keseluruhan dan terpasang.</p><p>Code atau skecth total dibawah ini, yang akan saya jelaskan kemudian.</p><p>Eh, arduino nano saya salah colok 12Volt masuk digital input. Jadi ini kesel, lalu ubah ke esp32 yang sedang otw dari China. Moga segera sampai. Nanti diedit lagi ditambahin penjelasan lainnya</p><p><br /></p><textarea cols="42" rows="3" style="background: none 0% 0% repeat scroll rgb(0, 0, 0); border: 2px inset rgb(204, 204, 204); color: white; height: 400px; line-height: 1.5em; padding: 0px 7px; width: 800px;">/*
Programmed by Budi Laksamana
collect from all tutorial in searching Google
P10 32x16 led, pin GPIO 6,7,8,9,11,13
*/
/* Insert file library */
#include <SPI.h>
#include <DMD2.h>
#include <EEPROM.h>
#include <fonts/SystemFont5x7.h>
#include <fonts/MyBigFont.h>
/* Deklarasi Variable */
const byte upButtonPin = 14; //calibration up
const byte downButtonPin = 15; //calibration down
const byte upButtonPin1 = 16; //min up
const byte downButtonPin1 = 17; //min down
const byte upButtonPin2 = 18; //max up
const byte downButtonPin2 = 19; //max down
// set sensorFtc pin numbers, for detecting tiles near temperature sensor for giving alarm status
const int sensorFtcPin = 20; // the number of the pushbutton pin
#define sensorSuhu A7 // Define sensor pin = pin A5
// set alarm digital logic pin
const int RELAY_PINx = 2;
const int RELAY_PINy = 3;
// Variables calibration will change:
volatile byte setpoint;
bool setpointChanged = false;
// Variables Min will change:
volatile byte setpointMin;
bool setpointChangedMin = false;
// Variables Max will change:
volatile byte setpointMax;
bool setpointChangedMax = false;
bool setNewDraw = false;
int sensorFtcState = 0; // variable for reading the sensor status
float suhu;
int suhuX;
char chr[3];
char chrX[3];
SoftDMD dmd(1,1); // DMD controls the entire display
DMD_TextBox box(dmd, 0, 0,32,16); // "box" provides a text box to automatically write to/scroll the display
void setup(void)
{
// initialize the button pins as a input with internal pullup:
pinMode(upButtonPin, INPUT_PULLUP);
pinMode(downButtonPin, INPUT_PULLUP);
pinMode(upButtonPin1, INPUT_PULLUP);
pinMode(downButtonPin1, INPUT_PULLUP);
pinMode(upButtonPin2, INPUT_PULLUP);
pinMode(downButtonPin2, INPUT_PULLUP);
// initialize digital pin as an output.
pinMode(RELAY_PINx, OUTPUT);
pinMode(RELAY_PINy, OUTPUT);
// fotocell input
pinMode(sensorFtcPin, INPUT);
// read eeprom calibration
setpoint = EEPROM.read(1);
if (setpoint == 0) {setpoint = 100;}
// read eeprom Min
setpointMin = EEPROM.read(2);
if (setpointMin == 0) {setpointMin = 100;}
// read eeprom
setpointMax = EEPROM.read(3);
if (setpointMax == 0) {setpointMax = 100;}
// Just for initial setpoint for EEPROM to save, first flashed enable and second time disable
//setpoint = 100;
//setpointMin = 100;
//setpointMax = 100;
// Setup DMD
dmd.setBrightness(58);
dmd.selectFont(MyBigFont);
dmd.begin();
Serial.begin(9600);
}
void loop(void)
{
checkUpButton();
checkDownButton();
if (setpointChanged == true) //setup calibration
{
EEPROM.update(1, setpoint);
setNewDraw = false;
setpointChanged = false;
}
checkUpButtonMin();
checkDownButtonMin();
if (setpointChangedMin == true) //setup Minimum setpoint
{
EEPROM.update(2, setpointMin);
setNewDraw = true;
setpointChangedMin = false;
}
checkUpButtonMax();
checkDownButtonMax();
if (setpointChangedMax == true) //setup Maximum setpoint
{
EEPROM.update(3, setpointMax);
setNewDraw = true;
setpointChangedMax = false;
}
suhu = (99.0*suhu + ((2.4*analogRead(sensorSuhu)*setpoint)/1024))/100.0;
suhuX = int(suhu+0.5);
delay(50);
Serial.println(suhuX);
Serial.println(setpoint);
Serial.println(setpointMax);
Serial.println(setpointMin);
// display change setpoint Max and Min
if (setNewDraw == true)
{
dmd.clearScreen();
dmd.selectFont(SystemFont5x7); // Font used
displayNew();
}
dmd.selectFont(MyBigFont); // Font used
dtostrf(suhuX, 3, 0, chr);
dmd.drawString( 0, 0, chr);
sensorFtcState = digitalRead(sensorFtcPin);
if (sensorFtcState == LOW )
{
if ((suhu > setpointMin) && (suhu < setpointMax))
{
digitalWrite(RELAY_PINx,LOW);
digitalWrite(RELAY_PINy,HIGH);
Serial.println("inside");
}
else
if ((suhu < setpointMin) || (suhu > setpointMax))
{
digitalWrite(RELAY_PINx,HIGH);
digitalWrite(RELAY_PINy,LOW);
Serial.println("outside");
}
}
if (sensorFtcState == HIGH )
{
digitalWrite(RELAY_PINx,LOW);
digitalWrite(RELAY_PINy,LOW);
Serial.println("FTC off");
}
}
double GetTemp(void)
{
unsigned int wADC;
double t;
// The internal temperature has to be used
// with the internal reference of 1.1V.
// Channel 8 can not be selected with
// the analogRead function yet.
// Set the internal reference and mux.
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
ADCSRA |= _BV(ADEN); // enable the ADC
//delay(100); // wait for voltages to become stable.
ADCSRA |= _BV(ADSC); // Start the ADC
// Detect end-of-conversion
while (bit_is_set(ADCSRA,ADSC));
// Reading register "ADCW" takes care of how to read ADCL and ADCH.
wADC = ADCW;
// The offset of 324.31 could be wrong. It is just an indication.
t = (wADC - 324.31 ) / 1.22;
// The returned temperature is in degrees Celcius.
return (t);
}
void checkUpButton()
{
boolean buttonState = 0; // current state of the button
static boolean lastButtonState = 0; // previous state of the button
static unsigned long timer = 0;
unsigned long interval = 50; // check 20 times a second
if (millis() - timer >= interval)
{
timer = millis();
// read the pushbutton input pin:
buttonState = digitalRead(upButtonPin);
// compare the buttonState to its previous state
if (buttonState != lastButtonState)
{
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is LOW then the button went from off to on:
setpoint++;
setpointChanged = true;
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
}
}
void checkDownButton()
{
boolean buttonState = 0; // current state of the button
static boolean lastButtonState = 0; // previous state of the button
static unsigned long timer = 0;
unsigned long interval = 50; // check 20 times a second
if (millis() - timer >= interval)
{
timer = millis();
// read the pushbutton input pin:
buttonState = digitalRead(downButtonPin);
// compare the buttonState to its previous state
if (buttonState != lastButtonState)
{
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is LOW then the button went from off to on:
setpoint--;
setpointChanged = true;
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
}
}
void checkUpButtonMin()
{
boolean buttonState = 0; // current state of the button
static boolean lastButtonState = 0; // previous state of the button
static unsigned long timer = 0;
unsigned long interval = 50; // check 20 times a second
if (millis() - timer >= interval)
{
timer = millis();
// read the pushbutton input pin:
buttonState = digitalRead(upButtonPin1);
// compare the buttonState to its previous state
if (buttonState != lastButtonState)
{
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is LOW then the button went from off to on:
setpointMin++;
setpointChangedMin = true;
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
}
}
void checkDownButtonMin()
{
boolean buttonState = 0; // current state of the button
static boolean lastButtonState = 0; // previous state of the button
static unsigned long timer = 0;
unsigned long interval = 50; // check 20 times a second
if (millis() - timer >= interval)
{
timer = millis();
// read the pushbutton input pin:
buttonState = digitalRead(downButtonPin1);
// compare the buttonState to its previous state
if (buttonState != lastButtonState)
{
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is LOW then the button went from off to on:
setpointMin--;
setpointChangedMin = true;
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
}
}
void checkUpButtonMax()
{
boolean buttonState = 0; // current state of the button
static boolean lastButtonState = 0; // previous state of the button
static unsigned long timer = 0;
unsigned long interval = 50; // check 20 times a second
if (millis() - timer >= interval)
{
timer = millis();
// read the pushbutton input pin:
buttonState = digitalRead(upButtonPin2);
// compare the buttonState to its previous state
if (buttonState != lastButtonState)
{
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is LOW then the button went from off to on:
setpointMax++;
setpointChangedMax = true;
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
}
}
void checkDownButtonMax()
{
boolean buttonState = 0; // current state of the button
static boolean lastButtonState = 0; // previous state of the button
static unsigned long timer = 0;
unsigned long interval = 50; // check 20 times a second
if (millis() - timer >= interval)
{
timer = millis();
// read the pushbutton input pin:
buttonState = digitalRead(downButtonPin2);
// compare the buttonState to its previous state
if (buttonState != lastButtonState)
{
// if the state has changed, increment the counter
if (buttonState == LOW)
{
// if the current state is LOW then the button went from off to on:
setpointMax--;
setpointChangedMax = true;
}
// save the current state as the last state, for next time through the loop
lastButtonState = buttonState;
}
}
}
void displayNew()
{
//The for loop condition true until thisPin is less than 60
for (int thisPin = 0; thisPin < 60; thisPin++)
{
// two lines draw for Max and Min setpoint
dtostrf(setpointMax, 3, 0, chr);
dmd.drawString( 12, 0, chr);
dtostrf(setpointMin, 3, 0, chrX);
dmd.drawString( 12, 9, chrX);
dmd.drawChar( 3, 0, '<', GRAPHICS_OR );
dmd.drawChar( 4, 9, '>', GRAPHICS_OR );
checkUpButtonMin();
checkDownButtonMin();
checkUpButtonMax();
checkDownButtonMax();
//delay(50);
}
setNewDraw = false;
dmd.clearScreen();;
}
// End</textarea>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com1tag:blogger.com,1999:blog-171116179817754719.post-26224557565630019502021-11-20T18:36:00.003+07:002021-11-22T13:18:05.433+07:00XBM-DN32S LS with XG5000 software<p>Beberapa hari saya perlukan tuk mengumpulkan bahan tuk melakukan koneksi XG5000 software ke LS XBM-DN32S dan Weintek MT8051iP</p><p>Problem awal adalah kabel koneksi ke laptop. Saya lihat konektor PS/2 mouse keyboard jaman dahulu, tapi pas di cek, tidak ada kabel nomor 6, 2 dan 3 spt dibawah ini. Kemudian beli konektor mini din 6 pin, dan dengan susah payah menyolder kabelnya.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcprScwQ75yaq_2zcA8RokCvt5D2fiLH-aK_1KP4E65ruDeAdRo4lGrzripfWw4kwj-NVq4uSLTM-pCdv_lzT4gxT9XohyQcKS7vOCGYEwJZsV7L84WuFHwFdQLP1_4fjSGr739oxZsSp1/s825/LS+mini+din.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="282" data-original-width="825" height="109" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcprScwQ75yaq_2zcA8RokCvt5D2fiLH-aK_1KP4E65ruDeAdRo4lGrzripfWw4kwj-NVq4uSLTM-pCdv_lzT4gxT9XohyQcKS7vOCGYEwJZsV7L84WuFHwFdQLP1_4fjSGr739oxZsSp1/s320/LS+mini+din.png" width="320" /></a></div>Akhirnya saya temukan omron kabel bekas dah kuno yaitu Omron SP10-CN221. Lebih afdol aja.<div>Kebetulan dah ada jadi kabel omron dan juga usb-rs232 omron yaitu Omron CS1W-CIF31.<p>Boleh juga digunakan kabel lainnya spt usb-rs232 difoto ini, hanya kekurangannya adalah kestabilan kecepatan tinggi XGT 115200bps yang harus mencari usb-rs232 yang baik.</p><p>Setelah terpasang kabel mini din dan rs232 ke usb port laptop, maka dibawah ini dilakukan tes koneksi.</p><p>Klik open from PLC</p><p style="text-align: justify;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN6UZ_-xs-McYUqBjWHLxqR5A6ZQSc6nf-VQZOQsHTiBB6JoaPvFnCU75W_-dqFmZU4OGV1PcKEQQ95h1-Mmw5OlCZfc1O4XEF_PCvouN18oKglPIuEHruEJ7e4rLMdj-xgIxJHQbG51v-/s488/OpenFromPLC.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="286" data-original-width="488" height="188" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjN6UZ_-xs-McYUqBjWHLxqR5A6ZQSc6nf-VQZOQsHTiBB6JoaPvFnCU75W_-dqFmZU4OGV1PcKEQQ95h1-Mmw5OlCZfc1O4XEF_PCvouN18oKglPIuEHruEJ7e4rLMdj-xgIxJHQbG51v-/s320/OpenFromPLC.png" width="320" /></a></p><p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihdbQCPBeXsZ3L_J2EqTroheZLqeRpGpU8gmgot9wfg_xt_X82y1JuSwjx1f1-8z9qGFWlhOh2NrqAyb6_5Ltv-Oo1KFG3TavzPJfv86pE-MKCBkSy5o8q_gcaStU8pw4mI87mw8g8hZyT/s500/connection+settings.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="500" data-original-width="390" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihdbQCPBeXsZ3L_J2EqTroheZLqeRpGpU8gmgot9wfg_xt_X82y1JuSwjx1f1-8z9qGFWlhOh2NrqAyb6_5Ltv-Oo1KFG3TavzPJfv86pE-MKCBkSy5o8q_gcaStU8pw4mI87mw8g8hZyT/s320/connection+settings.png" width="250" /></a></p><p><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcz6nuUbXIILlbrE7IjXDWqtYaM2KZ1sJpAQY5x5HMgaS_4lKXQ-V-5ZrwKK1_HknBLrlY5wObgPPmKohKb9SQkxQYey63wtfLjMZ8LKdbjPPNI0olihZLd0zvmkcqDcGMYrNZ5h2N4WDE/s676/rs232.png" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" data-original-height="628" data-original-width="676" height="297" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcz6nuUbXIILlbrE7IjXDWqtYaM2KZ1sJpAQY5x5HMgaS_4lKXQ-V-5ZrwKK1_HknBLrlY5wObgPPmKohKb9SQkxQYey63wtfLjMZ8LKdbjPPNI0olihZLd0zvmkcqDcGMYrNZ5h2N4WDE/s320/rs232.png" width="320" /></a></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxmAPnB_Pw2NgLXLidiwQWt_ljOBhym-eKEsn4B0nQfjzrl4SWEr1-S_9PjbYPacDrnKN-YUKRLFaM2FA3LrjyywIfwsGQYUd4qm3ASwHu-1I39Aboj6DSHRKZHPaPmWsyKmjPzJ_aWzpK/s622/autoDetect.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="526" data-original-width="622" height="271" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxmAPnB_Pw2NgLXLidiwQWt_ljOBhym-eKEsn4B0nQfjzrl4SWEr1-S_9PjbYPacDrnKN-YUKRLFaM2FA3LrjyywIfwsGQYUd4qm3ASwHu-1I39Aboj6DSHRKZHPaPmWsyKmjPzJ_aWzpK/s320/autoDetect.png" width="320" /></a></div><div class="separator" style="clear: both; text-align: left;">Setelah terlihat OK (bukan NG yg artinya No Good), maka lanjutkan dengan Connect</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizfBo6ZG2AjA62qcWW_ncNwxaF0PPdL_OyzwrdkRBdHIWGnpwL5yEi5MEwjKdW9bWvwRHQbQTyUIDrU1sANJ1ReYGPAMbchjTy3f5cMC1hgKDZ2fsBOtLRCik4Erf09_l6hT59YRX7FojK/s546/connect.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="498" data-original-width="546" height="292" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizfBo6ZG2AjA62qcWW_ncNwxaF0PPdL_OyzwrdkRBdHIWGnpwL5yEi5MEwjKdW9bWvwRHQbQTyUIDrU1sANJ1ReYGPAMbchjTy3f5cMC1hgKDZ2fsBOtLRCik4Erf09_l6hT59YRX7FojK/s320/connect.png" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4D4aL90bzp1WQyk6MzULvtjI_fB-9rkErYQnFMbJsyRTd3ugTI6cvwwrgWI1wcD9xWg682akT1S0K8G-dKJZphPgSWsrV9LkgBb4rpTIE36TqXztLfo5GR-WbRRXEcDwwxu_o611ROtz4/s596/connecting.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="328" data-original-width="596" height="176" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4D4aL90bzp1WQyk6MzULvtjI_fB-9rkErYQnFMbJsyRTd3ugTI6cvwwrgWI1wcD9xWg682akT1S0K8G-dKJZphPgSWsrV9LkgBb4rpTIE36TqXztLfo5GR-WbRRXEcDwwxu_o611ROtz4/s320/connecting.png" width="320" /></a></div>Dan berhasil membaca isinya PLC.</div><div><br /></div><div>Untuk mengisi PLC, sama proses nya, setelah konek maka dilakukan Write<br /><div class="separator" style="clear: both; text-align: center;"><br /></div><div><br /></div>Dibawah ini, saat menggunakan USB-RS232 yang murah. Kadang koneksi gagal.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPCyUsvYujdqo3F_3hifB46rtgUDhKwMf23pFzJvSQLJdMHcOUBOQV66UV8Z3MJfd3SI6Jr74yDm1e6Ms0P7ziWCJVLJr1wL2rbV-4HwSwKxBh5ZlaiUVgUPJY1IxqIyoq0zOfJdG_CtaH/s830/failed.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="528" data-original-width="830" height="204" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPCyUsvYujdqo3F_3hifB46rtgUDhKwMf23pFzJvSQLJdMHcOUBOQV66UV8Z3MJfd3SI6Jr74yDm1e6Ms0P7ziWCJVLJr1wL2rbV-4HwSwKxBh5ZlaiUVgUPJY1IxqIyoq0zOfJdG_CtaH/s320/failed.png" width="320" /></a></div><div><br /></div>Dibawah ini bbrp foto yg saya ambil. Catatan ini hanya tuk mengingatkan saya bila lupa pada masa akan datang.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV6PtlaKoRVEi0KwnFmC2Me7ZIYvSDvOdlO1oUGbTC7lp6iYPfpyZfiBApT9FfmPqieVAeswsGhNJyj786fqUfhK4CzuXfr8hoRJVGlmMNjnMA68miXgTaEGzqoYA-uTJHl-JGexWyYqCO/s2048/IMG_20211028_082514.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1155" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiV6PtlaKoRVEi0KwnFmC2Me7ZIYvSDvOdlO1oUGbTC7lp6iYPfpyZfiBApT9FfmPqieVAeswsGhNJyj786fqUfhK4CzuXfr8hoRJVGlmMNjnMA68miXgTaEGzqoYA-uTJHl-JGexWyYqCO/s320/IMG_20211028_082514.jpg" width="180" /></a></div><div class="separator" style="clear: both; text-align: left;">Oh iya, jangan lupa memutuskan koneksi di XG5000 bila telah selesai. Dan barulah boleh lepaskan kabel dan kemudian matikan PLC LS XBM-DN32S nya. Karena akan error koneksi saat dihidupkan kembali, sptnya PLC ini mengingat kondisi koneksi dan bila dimatikan akan mencari koneksi dulu (bila sebelumnya tidak dilepaskan koneksi).</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho0rl51Da_VxQsdb7Z-jwa_mB4CjdU-_Su6Bhydrond8eyvx4j0IvYeldEfWrIb38fMW3d3kjr_KYCnR9q44RMpmfdAyQF9Zp3EJUNSM57AF76WKM2lIh5-LyeNlYUecSPx-JYFB5rcJEJ/s2048/IMG_20211028_082522.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1155" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEho0rl51Da_VxQsdb7Z-jwa_mB4CjdU-_Su6Bhydrond8eyvx4j0IvYeldEfWrIb38fMW3d3kjr_KYCnR9q44RMpmfdAyQF9Zp3EJUNSM57AF76WKM2lIh5-LyeNlYUecSPx-JYFB5rcJEJ/s320/IMG_20211028_082522.jpg" width="180" /></a></div><div><br /></div>OMRON usb-rs232 ini bagus sekali, ga pernah gagal konek.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI_C6C_50Tj6r4yT9ZfNXXVGbGyfYnyDV3pL7hCA_y-sUEtiAYn89bOhV2FrpEaf14ZczGaVEdsVBiZYMUB7fqmTaYvwNIxDaz_tLddK2P7lg6OqyOHH-Y_Ji-c8i00SL99N9TXbOT-zrN/s2048/IMG_20211028_082535.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1155" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhI_C6C_50Tj6r4yT9ZfNXXVGbGyfYnyDV3pL7hCA_y-sUEtiAYn89bOhV2FrpEaf14ZczGaVEdsVBiZYMUB7fqmTaYvwNIxDaz_tLddK2P7lg6OqyOHH-Y_Ji-c8i00SL99N9TXbOT-zrN/s320/IMG_20211028_082535.jpg" width="180" /></a></div><div><br /></div>USB-RS232 model dibawah ini kurang baik, kadang koneksi bagus kadang mati.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVBCbNdtEBOjFWQcObqyjXsJFOZx9rntUVzC1kDFeerYtBm5fXEvToqgE4rbrdyMr1qpBOIrW8k-kqcV1XAPMIOvbeqLL2ab4mGmh5Gaqe6U7T8IjYwobm9H47J_M69kRFTxAm2knhfqWb/s2048/IMG_20211028_082613.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1155" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVBCbNdtEBOjFWQcObqyjXsJFOZx9rntUVzC1kDFeerYtBm5fXEvToqgE4rbrdyMr1qpBOIrW8k-kqcV1XAPMIOvbeqLL2ab4mGmh5Gaqe6U7T8IjYwobm9H47J_M69kRFTxAm2knhfqWb/s320/IMG_20211028_082613.jpg" width="180" /></a></div><div><br /></div>dibawah ini koneksi ke Weintek MT8051iP menggunakan RS232, speed 115200, 8bit, none parity and 1 stopbit<div>Kabel merah RX koneksi ke pin 6 (TX)</div><div>Kabel hitam TX koneksi ke pin 9 (RX)</div><div>Kabel SG koneksi ke pin 5 (SG)</div><div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrVOjEyKqRoedmARzr2HIeGA8TMTXOw2v8HHnPp2TmJ1tGdNRqfjiLH71DVAiJSCTKRpQhSRtNp-h7-8DEBnOe4et6uK1oKO5L3i8T59t2uVw4l-ZgS4ttIsfje61QM5DhMP8K4HwMWa4S/s2048/IMG_20211028_082635.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1155" data-original-width="2048" height="180" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjrVOjEyKqRoedmARzr2HIeGA8TMTXOw2v8HHnPp2TmJ1tGdNRqfjiLH71DVAiJSCTKRpQhSRtNp-h7-8DEBnOe4et6uK1oKO5L3i8T59t2uVw4l-ZgS4ttIsfje61QM5DhMP8K4HwMWa4S/s320/IMG_20211028_082635.jpg" width="320" /></a></div><br /><div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><br /><p><br /></p></div></div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-57003215804775572182021-10-19T08:54:00.007+07:002021-11-20T18:36:56.929+07:00Ascon XT 31/95 dengan Weintek HMI<p> Tugas
menyebabkan harus menggunakan ascon XT 31/95 yang kuno dan menghubungkan dengan
Weintek MT8051IP.</p>
<p class="MsoNormal"><span lang="EN-US">Untuk
komunikasi, ascon xt 31/95 hanya menggunakan Ascon Protocol Gamma Uno. Dimana
ascii character dikirimkan. Berbekal googling didapatkan beberapa material
dibawah ini yang membantu yaitu:<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpFirst" style="mso-list: l0 level1 lfo1; text-indent: -18pt;"><!--[if !supportLists]--><span lang="EN-US">1.<span style="font-size: 7pt; font-stretch: normal; font-variant-east-asian: normal; font-variant-numeric: normal; line-height: normal;"> </span></span><!--[endif]--><span lang="EN-US">ALS-en book<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpMiddle"><span lang="EN-US">Berisi panduan ASCON APALS 3/232<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpMiddle" style="mso-list: l0 level1 lfo1; text-indent: -18pt;"><!--[if !supportLists]--><span lang="EN-US">2.<span style="font-size: 7pt; font-stretch: normal; font-variant-east-asian: normal; font-variant-numeric: normal; line-height: normal;"> </span></span><!--[endif]--><span lang="EN-US">Ascon Serial communications protocol
Supplement manual, JM/JT, MF/MC, MS, XE,XF, XS/XP Series.<o:p></o:p></span></p>
<p class="MsoListParagraphCxSpLast"><span lang="EN-US">Berisi panduan ascii protocol<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Banyak lagi
manual yang didapatkan, tapi hanya kedua pdf diatas yang membantu. Tidak adanya
contoh contoh aplikasi menggunakan HMI menyebabkan kesulitan.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Baiklah
kita melihat kemampuan Weintek HMI, dimana tersedia Free Protocol.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Untuk itu
awal adalah setting koneksi yaitu:<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Pada ASCON
XT 31/95 secara manual: <o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Baud rate :
9600, parity None<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Pada
Weintek menggunakan Free Protocol dengan baud rate 4800 (boleh test 9600), parity None, stop bits 1<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Barulah
dilakukan koneksi pada Ascon XT 31/95 dengan APALS/3/232:<o:p></o:p></span></p>
<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCabhFhywLxH54OQLK4QV2k9ghzwzCl_XKZW17wLrUG0Y-RzL79c3W2ekrAwDkQb0k3HhBisyaEv12VxPli7tE70SUiQersipftI5OrIzM3-zoMqtn6KFuZQ5kiITGw3IXVc0FWEJ4TfFi/s502/apals+connection.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="349" data-original-width="502" height="421" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCabhFhywLxH54OQLK4QV2k9ghzwzCl_XKZW17wLrUG0Y-RzL79c3W2ekrAwDkQb0k3HhBisyaEv12VxPli7tE70SUiQersipftI5OrIzM3-zoMqtn6KFuZQ5kiITGw3IXVc0FWEJ4TfFi/w606-h421/apals+connection.png" width="606" /></a></div><p class="MsoNormal">Untuk koneksi
RS232 menggunakan terminal 7(RX), 6(TX) dan 3(SG) menuju Weintek.</p>
<p class="MsoNormal"><span lang="EN-US">Oh iya
selain panduan diatas, saya gunakan juga ASCON_XS_EN_MANUAL pdf, dimana
terdapat kemiripan. Hanya XT 31/95 <b>tidak memiliki modbus protocol</b>.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Lain lain
panduan bisa di googling, palagi yang menggunakan programming Basic, Fortran, C
dll.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Karena tuk
memudahkan, saya gunakan Weintek HMI dengan Free Protocol.<o:p></o:p></span></p>
<p class="MsoNormal"><span lang="EN-US">Macro yang
saya buat adalah spt dibawah ini:<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">// Free
Protocol ASCII for Ascon <o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">// Read
main display<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">macro_command
main()<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">unsigned
char command[10], response[10]<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">short
return_value, c<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">FILL(command[0],
0, 10)// init<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">FILL(response[0],
0, 10)<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[0]
= 0x42 // station no 1<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[1]
= '?' // read coils<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[2]
= 'X' // command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[3]
= ' ' //
command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[4]
= ' ' //
command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[5]
= 0x0D // Carriage Return<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">OUTPORT(command[0],
"MODBUS RTU Device", 6) //
send command asking Display Data Ascon<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">INPORT(response[0],
"MODBUS RTU Device", 5, return_value) // read response<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">SetData(response[0],
"Local HMI", LW, 15, 4) //
send response to LW 15 temporary<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">ASCII2HEX
(response[0], c, 4) //convert ASCII response to HEX<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">SetData(c,
"Local HMI", LW, 25, 1)// send
response to LW 25 for later display on HMI<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">end
macro_command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">sedangkan
untuk menulis setting temperature adalah:<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">// Free
Protocol ASCII Ascon Write Coils <o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">// Untuk
menulis setting temperature<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">macro_command
main()<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">unsigned
char command[10], response[10]<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">short
return_value, b<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">FILL(command[0],
0, 10)// init<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">FILL(response[0],
0, 10)<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[0]
= 0x42 // station no 1<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[1]
= '!' // read coils 3F=?=ask 21=!=write<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[2]
= 'W' // command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[3]
= 'L' //
command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[4]
= ' ' // command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">// get
one word from LW2 to the variable b<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">GetData(b,
"Local HMI", LW, 28, 1) // get word dari LW 28<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">HEX2ASCII
(b, command[5], 4) // convert word HEX into ASCII, put in command[5] to
command[8]<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[9]
= 0x0D // end with Carriage Return<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">OUTPORT(command[0],
"MODBUS RTU Device", 10) //
send command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">INPORT(response[0],
"MODBUS RTU Device", 5, return_value) // read response<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">SetData(response[0],
"Local HMI", LW, 35, 4) //
send response to LW 35, if needed<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">end
macro_command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">satu
lagi macro yang saya buat adalah :<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">// Free
Protocol ASCII for Ascon <o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">//
Membaca setelan target temperature<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">macro_command
main()<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">unsigned
char command[10], response[10]<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">short
return_value, c<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">FILL(command[0],
0, 10)// init<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">FILL(response[0],
0, 10)<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[0]
= 0x42 // station no<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[1]
= '?' // read coils<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[2]
= 'W' // command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[3]
= ' ' // command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[4]
= ' '// command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">command[5]
= 0x0D// CR<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">OUTPORT(command[0],
"MODBUS RTU Device", 6)// send
command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">INPORT(response[0],
"MODBUS RTU Device", 5, return_value)// read response<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">SetData(response[0],
"Local HMI", LW, 45, 4)// send
response to LW<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">ASCII2HEX
(response[0], c, 4)<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US">SetData(c,
"Local HMI", LW, 55, 1)// send
response to LW<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">end
macro_command<o:p></o:p></span></p>
<p class="MsoNoSpacing"><span lang="EN-US"> </span></p>
<p class="MsoNoSpacing"><span lang="EN-US">Dengan
ketekunan, googling, dan belajar akhirnya dapat melakukan komunikasi antara
Weintek HMI dengan APALS/3/232 dan diteruskan ke ASCON XT 31/95<o:p></o:p></span></p>
<p class="MsoNoSpacing" dir="rtl" style="text-align: right;"><span lang="EN-US"> </span></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGxTUTYF2mTPQgKyBMTiu4Iy_BXO8IdUj5L9txacsi06Ot4Uft8uB3yvDjZcqfguGJ4dupXAu-8RmctNbXxzjSrj7C0DPL59xDvVZb0yk4Ck1TvEYksMOmOwZUj1cGSaX2HCKuqaCfaymo/s1366/Ascon_connection.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="768" data-original-width="1366" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGxTUTYF2mTPQgKyBMTiu4Iy_BXO8IdUj5L9txacsi06Ot4Uft8uB3yvDjZcqfguGJ4dupXAu-8RmctNbXxzjSrj7C0DPL59xDvVZb0yk4Ck1TvEYksMOmOwZUj1cGSaX2HCKuqaCfaymo/w641-h360/Ascon_connection.png" width="641" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnEoG54lxRJT_3Dkrb6EB1475enX7HqMuh-96NXnJ7m-Mz0dua5mtaIeQLltd5lJkCDrh0xtMfgvZxBQ5g-d2qXjFtK-wQgKlnQMMc0Nu9ING9HgQ4nirTVT4oF845l59878iBImt5fveH/s1366/Ascon.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="768" data-original-width="1366" height="362" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnEoG54lxRJT_3Dkrb6EB1475enX7HqMuh-96NXnJ7m-Mz0dua5mtaIeQLltd5lJkCDrh0xtMfgvZxBQ5g-d2qXjFtK-wQgKlnQMMc0Nu9ING9HgQ4nirTVT4oF845l59878iBImt5fveH/w643-h362/Ascon.png" width="643" /></a></div><br /> <span face="Calibri, sans-serif" style="font-size: 11pt;">Dapat dilihat screenshoot diatas, address 1
hingga 3. Data diisi/diubah dan response maupun temperature muncul dilayar HMI.</span><p></p><div>oh iya, nama tertulis modbus rtu device, yang sebenarnya adalah Free Protocol tuk komunikasi. Nama boleh aja diganti jadi apapun yg memudahkan. Asalkan pada macro tertulis nama yang sama persis.</div><div><br /></div><div>dibawah ini konversi ascii vs hex</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy7wPYfv5hR39M7T3B7EiHsY3m3kSgXscbtWY1ErKks8aiXNLQuHR7rBV_q4dLtE9tR-9uyBS3TDflznsWOoLoyZGwazrrSJv5SNGuYHzqna23_fXNwrvKiNUmBJdCIJI46VOtX08ueNqD/s1066/ascii-character-codes-hex.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="804" data-original-width="1066" height="437" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy7wPYfv5hR39M7T3B7EiHsY3m3kSgXscbtWY1ErKks8aiXNLQuHR7rBV_q4dLtE9tR-9uyBS3TDflznsWOoLoyZGwazrrSJv5SNGuYHzqna23_fXNwrvKiNUmBJdCIJI46VOtX08ueNqD/w580-h437/ascii-character-codes-hex.png" width="580" /></a></div><br /><div><br /></div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-69281739822774764602021-03-14T06:18:00.004+07:002021-03-14T07:03:29.294+07:00Baterai Huawei P10 Plus 60%<p> Beberapa minggu lalu, hape Huawei P10 Plus yang sudah aku miliki sejak 2015 bermasalah pada baterai. Setiap di cas, hanya sampai 60% saja dan tentunya jadi cepet habis. Buka settingan enggak ada yang bener. Googling dapatnya mesti ke service centre dan diganti baterai. Padahal baterai normal alias enggak gendut dan enggak panas, habisnya juga normal secara dari 60%. Sudah ganti kabel, kepala cas, setel di settingan. Akhirnya tetap aja enggak mau.</p><p>Akhirnya Factory Reset, dengan mengamankan dan login laptop ke Cloud Huawei. Karena dah lama jadi lupa password Huawei. Oh iya jangan lakukan Factory Reset.</p><p>Lakukan berikut dibawah ini :</p><p>1. Install dan buka HiSuite, yi aplikasi Huawei pada laptop</p><p>2. Sambungkan hape dengan HiSuite, berikan ijin koneksi di hape.</p><p>3. Harus bisa konek HiSuite dengan hape, karena ini jalan terbaik.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt_vzD98QcjVlqvO1VOMBGsIYWx4nAJfMjaWc26IYrWvtBvO0oZDyU70gL2UOqmFYrdfy8woRH8GZjJYhdSvt1CERCCV7Z4iuNlkybHUMvWmf831Y8o2UvrtKcDn_A7UZGAbWO_MuExplk/s1000/Untitled.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="700" data-original-width="1000" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgt_vzD98QcjVlqvO1VOMBGsIYWx4nAJfMjaWc26IYrWvtBvO0oZDyU70gL2UOqmFYrdfy8woRH8GZjJYhdSvt1CERCCV7Z4iuNlkybHUMvWmf831Y8o2UvrtKcDn_A7UZGAbWO_MuExplk/s320/Untitled.png" width="320" /></a></div><br /><p><br /></p><p>4. Buat cadangkan.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv5w6ahdPKaQOanxUJwDuO8hpNnZ0rSJ_9nH8QCm-vsElc5VTNIYDifq_Y7PkcrcSBAWPjbLrYEywILwvKuGNChEqb7kZsUx4mxg2RsJMk3HrPXSbg_DqsjhmkZjscxZMTQG81Fckf8x_-/s700/Untitled.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="560" data-original-width="700" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiv5w6ahdPKaQOanxUJwDuO8hpNnZ0rSJ_9nH8QCm-vsElc5VTNIYDifq_Y7PkcrcSBAWPjbLrYEywILwvKuGNChEqb7kZsUx4mxg2RsJMk3HrPXSbg_DqsjhmkZjscxZMTQG81Fckf8x_-/s320/Untitled.jpg" width="320" /></a></div><br /><p>5. Beri nama dan awasi lokasi penyimpanan HiSuite, agar tidak lupa.</p><p>6. Beri password dan jalankan </p><p>7. Setelah selesai, maka lakukan langkah downgrade system yaitu :</p><p>8. Pembaharuan sistem. Ingat PEMBARUAN SISTEM</p><p>9. Lalu nantinya klik ALIHKAN KE VERSI LAIN</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxM-o6C1qw4V8RW7LvQXd8stNy8R47_5M5IKF7kJNKGIbiTv0jesA8sVD_pXWu7gUxzF8C5gANWBdaDXS2ugxQq5XELjV9sP2u4GL_wcrnch7tIDhvn67XFisx_3WuKeBFSJozATiJ18tE/s660/Untitled2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="455" data-original-width="660" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxM-o6C1qw4V8RW7LvQXd8stNy8R47_5M5IKF7kJNKGIbiTv0jesA8sVD_pXWu7gUxzF8C5gANWBdaDXS2ugxQq5XELjV9sP2u4GL_wcrnch7tIDhvn67XFisx_3WuKeBFSJozATiJ18tE/s320/Untitled2.png" width="320" /></a></div><br /><p><br /></p><p>10. Ikuti aja alurnya, ingat tuk jangan matikan hape. Dibutuhkan beberapa kali restart sendiri hape nya. Juga perlu waktu lama untuk download sistem. Saat ini saya gunakan 9.0.1.179, sehingga screenshoot akan jadi versi 8. Dan yang bermasalah ada versi 10.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYhhBqfmOETZwalZreA3ZA0uTPd_sGerQcF1o8Vbxy42dtmzpK0ZJtf_am-QS_vdxoQGAQF47oe9ND21H8kv8OrpK1DjtGcJoxH_kESNX7liG_I7Kza9piyVcrZIj8GEKswY_yFlkZNz4B/s660/Untitled3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="455" data-original-width="660" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgYhhBqfmOETZwalZreA3ZA0uTPd_sGerQcF1o8Vbxy42dtmzpK0ZJtf_am-QS_vdxoQGAQF47oe9ND21H8kv8OrpK1DjtGcJoxH_kESNX7liG_I7Kza9piyVcrZIj8GEKswY_yFlkZNz4B/s320/Untitled3.png" width="320" /></a></div><br /><p><br /></p><p>11. Pokoke mundur satu level, toh sebelumnya aman aman aja</p><p>12. Bila tidak ada di HiSuite baru anda, maka uninstall dan install <a href="https://drive.google.com/file/d/1CLtbCYct7KfCpNKKt8DrLx2_fqDm3ak9/view?usp=sharing" target="_blank">aplikasi HiSuite lama</a>.</p><p>13. Setelah hidup lagi, lakukan koneksi hape ke HiSuite tuk PULIHKAN (disebelah cadangkan, layar pertama)</p><p>maka hape akan terisi data backup kembali berikut setelannya.</p><p>waktu yang dibutuhkan tergantung koneksi internet untuk download sistem sebesar 3GB.</p><p>Semoga bermanfaat, terimakasih sudah membaca</p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-43941901053011399012021-03-09T15:59:00.002+07:002021-03-10T04:42:53.937+07:00Guna Cache pada aplikasi Catatan Kredit<p>Sepintas, cache adalah data yang tersimpan didalam hape. Data ini diambil dari server Google Cloud saat cache kosong atau cache berbeda isinya dengan server. Karena aplikasi ini hanya diakses oleh satu pengguna saja, maka data di server hanya diubah oleh pengguna ybs. Artinya data server tidak berubah kecuali pengguna ybs merubahnya alias melakukan transaksi, penghapusan atau tambah nama. </p><p>Jadi sifatnya data cache disini tidak seperti data chat yang diakses oleh ribuan pengguna yang akan menyebabkan data server berubah ubah terus bagi pengguna lainnya. Yang pastinya data cache akan sering di sinkron kan dengan data server.</p><p>Karena keuntungan aplikasi ini hanya memiliki satu pengguna untuk satu data khusus miliknya, maka mudah menggunakan data cache hanya tuk sekedar cetak PDF/CSV ataupun urutkan nama/tanggal dls.</p><p>Sehingga saya buatlah untuk hanya akses data cache, karena yakin data tidak akan berubah. Karena saat pengguna melakukan transaksi baru, data cache juga akan diubah otomatis. Bila tidak melakukan apa apa, maka data cache akan pasti selalu sama seperti data server. Jadi data cache adalah data yang ada di memori hape. Dan saya lihat kecil saja. Berkisar 20MByte s/d 100MByte maksimal. Karena itu setelan cache default dari server Google Cloud, hanya memberikan maksimal 100MB, dan saya cek hanya 18MB saja.</p><p>Dibawah ini beberapa contoh Source.Cache aplikasi catatan kredit v50.0</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">exportPDF</span>() {<br /> CollectionReference unisRef = <span style="color: #9876aa;">db</span>.collection(<span style="color: #9876aa;">mUserFb</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>PdfDocument pdfDocument = <span style="color: #cc7832;">new </span>PdfDocument()<span style="color: #cc7832;">; </span><span style="color: grey;">// initial PdfDocument<br /></span><span style="color: grey;"> </span>unisRef.orderBy(<span style="color: #6a8759;">"name"</span>).limit(<span style="color: #6897bb;">100</span>).get(Source.<span style="color: #9876aa; font-style: italic;">CACHE</span>).addOnCompleteListener(task -> {<br /> <span style="color: #cc7832;">int </span>noTransaksi = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;</span></pre><p> dan</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><br /><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">KreditSet</span>() {<br /> <span style="color: grey;">//CollectionReference enengRef = db.collection(mUserFb);<br /></span><span style="color: grey;"> </span><span style="color: #9876aa;">db</span>.collection(<span style="color: #9876aa;">mUserFb</span>).get(Source.<span style="color: #9876aa; font-style: italic;">CACHE</span>).addOnCompleteListener(task -> {<br /> <span style="color: #cc7832;">double </span>sumKuKredit = <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;</span></pre><p>dan</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;">PdfDocument.Page myPage = pdfDocument.startPage(mypageInfo)<span style="color: #cc7832;">;<br /></span>Canvas canvas = myPage.getCanvas()<span style="color: #cc7832;">;<br /></span>title.setTypeface(Typeface.<span style="font-style: italic;">create</span>(Typeface.<span style="color: #9876aa; font-style: italic;">DEFAULT</span><span style="color: #cc7832;">, </span>Typeface.<span style="color: #9876aa; font-style: italic;">NORMAL</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #9876aa;">mRestaurantRef</span>.get(Source.<span style="color: #9876aa; font-style: italic;">CACHE</span>).addOnCompleteListener(task -> {<br /> <span style="color: #cc7832;">if </span>(task.isSuccessful()) {<br /> DocumentSnapshot document = task.getResult()<span style="color: #cc7832;">;<br /></span></pre><p>Tetapi saat dijalankan, kadang perlu 2x klik, kadang malahan crash.</p><p>Setelah cek sana sini di v50.0 ini, akhirnya saya temukan tuk menjaga Source.Cache mendapatkan datanya maka perlu dijagain diakhir task seperti ini</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;">} <span style="color: #cc7832;">else </span>{<br /> Log.<span style="font-style: italic;">d</span>(<span style="color: #9876aa; font-style: italic;">TAG</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"get failed with "</span><span style="color: #cc7832;">, </span>task.getException())<span style="color: #cc7832;">;</span></pre><p>Jadi kalau mau lengkapnya adalah :</p><p> mRestaurantRef.get(Source.CACHE).addOnCompleteListener(task -> {</p><p> if (task.isSuccessful()) {</p><p> DocumentSnapshot document = task.getResult();</p><div><span> </span><span> </span><span> </span><span> </span><span> //do stuff here</span><br /></div><div><span><span> </span><span> </span><span> </span><span> </span><span> //do other stuff here</span><br /></span></div><div><br /></div><div><div> } else {</div><div> Log.d(TAG, "get failed with ", task.getException());</div><div> }</div><div> });</div></div><div><br /></div><div>Jadi semua harus ada else nya, agar aplikasi ga ngeloyor aja kalau ga hasil. Jadi spt anak buah yg suka ngeloyor aja ga laporan kalau terjadi kegagalan atau enggak dikerjakan. Nah else dibawah itu, menyuruh untuk membuat laporan kalau gagal atau ga ada data. Nah dia kan jadi malu, langsung kerjakan tugas nya agar laporan nya bagus. Hehehe.</div><div>Jadi source cache saya selalu menghasilkan cetak pdf/csv dengan baik dan cepat. Walau masih sekali saya temui laporannya tidak tampil dan layar catatan kredit freeze. Mungkin karena memori/cpu ngambek, langsung saya klik Home, dan buka aplikasi lagi. Eh muncul laporan tapi sudah tidak bisa kirim ke sosmed krn gagal share. Begitu klik cetak, langsung normal kembali. Kalau di game itu layar tersendat atau diam. Karena cpu dan memori hape lagi bingung.</div><div><br /></div><div>Sekian ulasan mengenai cache yang sangat membantu kecepatan aplikasi catatan kredit v50.0</div><div><br /></div><div>Terimakasih sudah membaca.</div><div><br /></div><div><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan Kredit</a></div><div><br /></div><div>Lanjutan kupas tuntas aplikasi Catatan Kredit</div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-12199202136099578282021-03-08T09:51:00.003+07:002021-03-10T04:43:37.220+07:00In-App Review Playstore pada aplikasi Catatan Kredit<p>Pada oktober 2020, google meluncurkan fungsi In-App Review. Yaitu fungsi memanggil ulasan rate playstore terhadap aplikasi kita. </p><p>Saya belum pernah membuat tombol atau arahan agar pengguna mengisi rating di playstore. Akibatnya, bila ada masalah maka pengguna mengisi rating di playstore secara terpaksa sambil mengulas dan memberikan nilai jelek. Yaudah diterima aja. Eh, muncul In-App review. Langsung pelajari dari sumbernya di <a href="https://developer.android.com/guide/playcore/in-app-review/kotlin-java" target="_blank">https://developer.android.com/guide/playcore/in-app-review/kotlin-java</a></p><p>Disitu dikatakan untuk tidak membuat tombol khusus, dan sebaiknya dipasang saat pengguna telah memakai aplikasi dengan baik. Sehingga didapatkan rating yang benar. Jadi saya pasang di aktiviti kedua.</p><p>Saya pasang manajer review di onCreate aktiviti kedua</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: grey;">//Request a ReviewInfo object ahead of time (Pre-cache)<br /></span><span style="color: #9876aa;">reviewManager </span>= ReviewManagerFactory.<span style="font-style: italic;">create</span>(<span style="color: #cc7832;">this</span>)<span style="color: #cc7832;">;</span></pre><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #bbb529;">@Override<br /></span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">onStart</span>() {<br /> <span style="color: #cc7832;">super</span>.onStart()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">mRatingAdapter</span>.startListening()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">mRestaurantRegistration </span>= <span style="color: #9876aa;">mRestaurantRef</span>.addSnapshotListener(<span style="color: #cc7832;">this</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">// flow in app review<br /></span><span style="color: grey;"> </span>showRateApp()<span style="color: #cc7832;">;<br /></span>}<br /></pre><p>Sewaktu saya pasang pada onClick transaksi, tidak ada efek apapun. Jadi memang ga boleh dekat button, dan sebaiknya onStart, atau onStop atau onFinish. Juga tidak bisa di MainActivity. Kalau game aplikasi itu, seperti sudah masuk level berikutnya. Itu kata google lho.</p><p>Dan sesuai petunjuk, dipanggil rutin showRateApp().</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">showRateApp</span>() {<br /> com.google.android.play.core.tasks.Task<ReviewInfo> request = <span style="color: #9876aa;">reviewManager</span>.requestReviewFlow()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>request.addOnCompleteListener(task -> {<br /> <span style="color: #cc7832;">if </span>(task.isSuccessful()) {<br /> ReviewInfo reviewInfo = task.getResult()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>com.google.android.play.core.tasks.Task<Void> flow = <span style="color: #9876aa;">reviewManager</span>.launchReviewFlow(<span style="color: #cc7832;">this, </span>reviewInfo)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>flow.addOnCompleteListener(task1 -> Log.<span style="font-style: italic;">d</span>(<span style="color: #9876aa; font-style: italic;">TAG</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"in app review berhasil ..... "</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">else </span>{<br /> Log.<span style="font-style: italic;">d</span>(<span style="color: #9876aa; font-style: italic;">TAG</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"in app review failed ..... "</span><span style="color: #cc7832;">, </span>task.getException())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> })<span style="color: #cc7832;">;<br /></span>}<br /></pre><p>Hasilnya, sekitar tiap hari dapat 1 ulasan baru. Kadang hanya rate kadang berikut ulasannya diisi. Lumayan. Waktu test hanya menunjukkan bahwa flow fungsi berjalan dengan baik, menggunakan FakeManager. Jadi kita sendiri tidak bisa test secara sesungguhnya. Tapi jalan kok di pengguna. Bisa juga test menggunakan trial test, ada tuh di Stack Overflow. Kalau saya ya percaya aja, setelah tes fungsi normal dgn FakeManager. Buktinya setiap hari dapat satu rating.</p><p>Jadi review itu tergantung quota alias sudah berapa per hari, juga tergantung jumlah pengguna, tergantung lama pakai si pengguna. Entah apa lagi. Jadi pengguna yang sudah nge rate tidak akan disodori lagi. Jadi sangat membantu dan review nya kebanyakan bagus sih. Karena bukan merasa terpaksa dan bukan karena ada masalah dengan aplikasi. Kalau ada masalah pastinya si pengguna akan pergi ke playstore dan menuliskan langsung disana.</p><p>Terimakasih sudah membaca.</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/guna-cache-pada-aplikasi-catatan-kredit.html" target="_blank">Lanjutan kupas tuntas aplikasi Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-14656911178268509832021-03-08T05:27:00.004+07:002021-03-08T09:55:25.706+07:00Kesalahan Lokasi GCP di aplikasi Catatan Kredit<p>Kesalahan terbesar dalam pembuatan aplikasi saya ini adalah lokasi GCP server firestore. Dasar developer pemula dan terlalu cepat membuat project di Google Cloud sehingga tidak mengamati hal GCP location, atau tidak ada pilihan lainnya waktu itu, karena tahun 2017 adalah masih baru untuk Firestore (saat setup server). Seingatku sudah mencoba buka pilihan server, yang ada hanya eropa dan amerika saja. Itu yang aku ingat, karena saat itu tidak ada yang membicarakan GCP location, dan di Stack Overflow baru mulai dibicarakan tahun 2019. Bagaimanapun, tuk developer baru yang membaca, agar berhati hati dalam menyiapkan server apapun itu di merek apapun juga.</p><p>Pada awal pembuatan server Google Cloud, tidak ada saran lokasi GCP dan default lokasi server di Amerika. Dalam pengembangan aplikasi ini, telah sampai pada teknik mempercepat aplikasi. Agar tidak lemot. Juga saya mencoba Source.Cache untuk pemakaian yang tidak memerlukan koneksi ke server dengan sukses. Kemudian saya googling lagi, apalagi yang bisa mempercepat aplikasi Catatan Kredit ini. Dan dijumpai bahwa setelan server berada di Amerika.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSUjWVtr43nWxcwX7oMhauxYFcFqmVw54fdlUtAxzstOuZd3y9Wk7HjbwuIrQu6egYBp7TQi6QLucLI06JGBUocAR-qejX3Hw1yMw5RBfVNJINMUVC6OjWdBItmY_JWUOnF1psJS0PN6M6/s480/Untitled.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="159" data-original-width="480" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSUjWVtr43nWxcwX7oMhauxYFcFqmVw54fdlUtAxzstOuZd3y9Wk7HjbwuIrQu6egYBp7TQi6QLucLI06JGBUocAR-qejX3Hw1yMw5RBfVNJINMUVC6OjWdBItmY_JWUOnF1psJS0PN6M6/s320/Untitled.png" width="320" /></a></div><br /><p>Dari hasil googling terdapat server di Jakarta atau Singapore, lupa. regional asia tenggara. Mulailah googling cara memindahkan data ke server terdekat. Agar kecepatan data bolak balik lebih baik, kan dekat. Eh, ternyata GCP location fixed, tidak bisa dipindah. Sampai saat ini tidak ada cara tuk memindahkan server ke lokasi terdekat.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxleIk4PB3rC4hdmKgVnmk4PSsii8yHyaQ0a-e4oZQLkTw8NXgFwYjKgARtC-Vlmt3t6muWV73pjI-mebYvFxjWatfddUT3-JZsq7dz4Nf1666xoBdA5WyJoH8lN23y7q6tZmFHsv6hwmo/s640/Untitled2.png" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="352" data-original-width="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxleIk4PB3rC4hdmKgVnmk4PSsii8yHyaQ0a-e4oZQLkTw8NXgFwYjKgARtC-Vlmt3t6muWV73pjI-mebYvFxjWatfddUT3-JZsq7dz4Nf1666xoBdA5WyJoH8lN23y7q6tZmFHsv6hwmo/s320/Untitled2.png" width="320" /></a></div><br /><p><br /></p><p>Ada petunjuk tuk memindahkan bucket Cloud Storage ke server terdekat, tapi masih minim info. Cloud Storage adalah tempat menyimpan gambar/foto. Mesti lebih serius cari info mengenai pemindahan server Cloud Storage ini, lumayan gambar/foto bisa lebih cepat. </p><p>Semoga para developer baru, tidak mengulang kesalahan saya ini.</p><p>Terimakasih sudah membaca.</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/in-app-review-playstore-pada-aplikasi.html" target="_blank">Lanjutan kupas tuntas aplikasi Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-17322874544453640822021-03-07T15:04:00.004+07:002021-03-08T05:28:43.108+07:00TextWatcher pada aplikasi Catatan Kredit<p>Pada awal aplikasi dibuat, saya mengalami kesulitan menghadapi persoalan titik/koma pada ribuan. Karena setiap hape masing masing memiliki setelan English atau Indonesia atau lainnya, menyebabkan apa yang nampak dilayar akan diterjemahkan berbeda saat masuk dalam data. Untuk menghindari itu, maka saya terapkan text watcher yang tidak mengijinkan user menulis titik/koma. Akan tetapi textwatcher akan membuatkan spasi antar ribuan sehingga user dapat langsung melihat jumlah nol. Saya lihat mBCA juga tidak mengijinkan titik/koma, jadi bisa salah jumlah nol, walaupun mBCA kemudian menuliskan nilai rupiah dalam titik ribuan sebelum melanjutkan ke pin.</p><p>Inilah textwatcher yang saya terapkan diawal aplikasi ini meluncur.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">public class </span>ThousandSeparatorTextWatcher <span style="color: #cc7832;">implements </span>TextWatcher {<br /> <span style="color: #cc7832;">private </span>DecimalFormat <span style="color: #9876aa;">df</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private </span>EditText <span style="color: #9876aa;">editText</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private static </span>String <span style="color: #9876aa; font-style: italic;">thousandSeparator</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private static </span>String <span style="color: #9876aa; font-style: italic;">decimalMarker</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> private int </span><span style="color: #9876aa;">cursorPosition</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> public </span><span style="color: #ffc66d;">ThousandSeparatorTextWatcher</span>(EditText editText) {<br /> <span style="color: #cc7832;">this</span>.<span style="color: #9876aa;">editText </span>= editText<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">df </span>= <span style="color: #cc7832;">new </span>DecimalFormat(<span style="color: #6a8759;">"#,###.##"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">df</span>.setDecimalSeparatorAlwaysShown(<span style="color: #cc7832;">true</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa; font-style: italic;">thousandSeparator </span>= Character.<span style="font-style: italic;">toString</span>(<span style="color: #9876aa;">df</span>.getDecimalFormatSymbols().getGroupingSeparator())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa; font-style: italic;">decimalMarker </span>= Character.<span style="font-style: italic;">toString</span>(<span style="color: #9876aa;">df</span>.getDecimalFormatSymbols().getDecimalSeparator())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">beforeTextChanged</span>(CharSequence charSequence<span style="color: #cc7832;">, int </span>start<span style="color: #cc7832;">, int </span>count<span style="color: #cc7832;">, int </span>after) {<br /> <span style="color: #9876aa;">cursorPosition </span>= <span style="color: #9876aa;">editText</span>.getText().toString().length() - <span style="color: #9876aa;">editText</span>.getSelectionStart()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">onTextChanged</span>(CharSequence charSequence<span style="color: #cc7832;">, int </span>i<span style="color: #cc7832;">, int </span>i1<span style="color: #cc7832;">, int </span>i2) {}<br /><br /> <span style="color: #bbb529;">@Override<br /></span><span style="color: #bbb529;"> </span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">afterTextChanged</span>(Editable s) {<br /> <span style="color: #cc7832;">try </span>{<br /> <span style="color: #9876aa;">editText</span>.removeTextChangedListener(<span style="color: #cc7832;">this</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String value = <span style="color: #9876aa;">editText</span>.getText().toString()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(value != <span style="color: #cc7832;">null </span>&& !value.equals(<span style="color: #6a8759;">""</span>)) {<br /> <span style="color: #cc7832;">if </span>(value.startsWith(<span style="color: #9876aa; font-style: italic;">decimalMarker</span>)) {<br /> String text = <span style="color: #6a8759;">"0" </span>+ <span style="color: #9876aa; font-style: italic;">decimalMarker</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">editText</span>.setText(text)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #cc7832;">if </span>(value.startsWith(<span style="color: #6a8759;">"0"</span>) && !value.startsWith(<span style="color: #6a8759;">"0" </span>+ <span style="color: #9876aa; font-style: italic;">decimalMarker</span>)) {<br /> <span style="color: #cc7832;">int </span>index = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> while </span>(index < value.length() && value.charAt(index) == <span style="color: #6a8759;">'0'</span>) {<br /> index++<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> String newValue = Character.<span style="font-style: italic;">toString</span>(value.charAt(<span style="color: #6897bb;">0</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(index != <span style="color: #6897bb;">0</span>) {<br /> newValue = value.charAt(<span style="color: #6897bb;">0</span>) + value.substring(index)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #9876aa;">editText</span>.setText(newValue)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> String str = <span style="color: #9876aa;">editText</span>.getText().toString().replaceAll(<span style="color: #9876aa; font-style: italic;">thousandSeparator</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">""</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(!value.equals(<span style="color: #6a8759;">""</span>)) {<br /> <span style="color: #9876aa;">editText</span>.setText(<span style="font-style: italic;">getDecimalFormattedString</span>(str))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #9876aa;">editText</span>.setSelection(<span style="color: #9876aa;">editText</span>.getText().toString().length())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: grey;">//setting the cursor back to where it was<br /></span><span style="color: grey;"> </span><span style="color: #9876aa;">editText</span>.setSelection(<span style="color: #9876aa;">editText</span>.getText().toString().length() - <span style="color: #9876aa;">cursorPosition</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">editText</span>.addTextChangedListener(<span style="color: #cc7832;">this</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">catch </span>(Exception ex) {<br /> ex.printStackTrace()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">editText</span>.addTextChangedListener(<span style="color: #cc7832;">this</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /><br /> <span style="color: #cc7832;">private static </span>String <span style="color: #ffc66d;">getDecimalFormattedString</span>(String value) {<br /> String[] splitValue = value.split(<span style="color: #6a8759;">"</span><span style="color: #cc7832;">\\</span><span style="color: #6a8759;">."</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String beforeDecimal = value<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String afterDecimal = <span style="color: #cc7832;">null;<br /></span><span style="color: #cc7832;"> </span>String finalResult = <span style="color: #6a8759;">""</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(splitValue.<span style="color: #9876aa;">length </span>== <span style="color: #6897bb;">2</span>) {<br /> beforeDecimal = splitValue[<span style="color: #6897bb;">0</span>]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>afterDecimal = splitValue[<span style="color: #6897bb;">1</span>]<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #cc7832;">int </span>count = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> for </span>(<span style="color: #cc7832;">int </span>i = beforeDecimal.length() - <span style="color: #6897bb;">1</span><span style="color: #cc7832;">; </span>i >= <span style="color: #6897bb;">0 </span><span style="color: #cc7832;">; </span>i--) {<br /> finalResult = beforeDecimal.charAt(i) + finalResult<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>count++<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(count == <span style="color: #6897bb;">3 </span>&& i > <span style="color: #6897bb;">0</span>) {<br /> finalResult = <span style="color: #9876aa; font-style: italic;">thousandSeparator </span>+ finalResult<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>count = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /> <span style="color: #cc7832;">if </span>(afterDecimal != <span style="color: #cc7832;">null</span>) {<br /> finalResult = finalResult + <span style="color: #9876aa; font-style: italic;">decimalMarker </span>+ afterDecimal<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> <span style="color: #cc7832;">return </span>finalResult<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />}</pre><p>Class diatas dipakai oleh nominal input dibawah ini</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #9876aa;">editText</span>.addTextChangedListener(<span style="color: #cc7832;">new </span>ThousandSeparatorTextWatcher(<span style="color: #9876aa;">editText</span>))<span style="color: #cc7832;">;<br /></span></pre><p>Kemudian saat saya sudah banyak waktu pada masa pandemi corona ini, saya merubah teknik itu. Setelah mengatasi masalah rupiah tidak nol, saya matikan textwatcher dan saya ijinkan menulis titik/koma atau tidak sama sekali. Terserah kemauan user. Pada saat nominal itu mau masuk ke sistem database, maka titik/koma dihapuskan oleh aplikasi secara dibelakang layar seperti dibawah ini.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;">(Integer.<span style="font-style: italic;">parseInt</span>(<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.getText().toString().replaceAll(<span style="color: #6a8759;">"[,|.]"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">""</span>))) * -<span style="color: #6897bb;">1</span><span style="color: #cc7832;">,<br /></span></pre><p>Saat ujicoba di hape saya, tiba tiba nampak kalau saya salah mengisi titik/koma dengan nilai SEN. Dan menjadikan data terisi tambahan 2 buah nol. Hal ini tidak memungkinkan saya untuk menyuruh user tidak membuat SEN. Jadi harus aplikasi yang pintar tuk mengurus ini. Maka saya terapkan uji nominal tuk nilai SEN sebelum menjadi data di Cloud. Bila menjumpai nilai SEN maka aplikasi akan membuang transaksi tersebut walaupun user menekan tombol SIMPAN. Hehehe, tidak terjadi suatu apapun. Dan user pasti sadar bahwa telah mengisi nilai SEN pada nominal sehingga tidak ada transaksi apapun. Begini saya membuat aplikasi.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">if </span>(<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.length() < <span style="color: #6897bb;">3</span>) {<br /> <span style="color: #cc7832;">if </span>(!<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.getText().toString().matches(<span style="color: #6a8759;">""</span>)) {<br /> Rating rating = <span style="color: #cc7832;">new </span>Rating(<br /> <span style="color: #9876aa;">calDarurat</span>.getTime()<span style="color: #cc7832;">, </span><span style="color: #9876aa;">calJtpRating</span>.getTime()<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>(Integer.<span style="font-style: italic;">parseInt</span>(<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.getText().toString().replaceAll(<span style="color: #6a8759;">"[,|.]"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">""</span>))) * -<span style="color: #6897bb;">1</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormText</span>.getText().toString())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(<span style="color: #9876aa;">mRatingListener </span>!= <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #9876aa;">mRatingListener</span>.onRating(rating)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br />} <span style="color: #cc7832;">else if </span>(!(<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.getText().toString().substring(<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.length() - <span style="color: #6897bb;">3</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.length() - <span style="color: #6897bb;">2</span>)).matches(<span style="color: #6a8759;">"[,|.]"</span>)) {<br /> <span style="color: #cc7832;">if </span>(!<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.getText().toString().matches(<span style="color: #6a8759;">""</span>)) {<br /> Rating rating = <span style="color: #cc7832;">new </span>Rating(<br /> <span style="color: #9876aa;">calDarurat</span>.getTime()<span style="color: #cc7832;">, </span><span style="color: #9876aa;">calJtpRating</span>.getTime()<span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>(Integer.<span style="font-style: italic;">parseInt</span>(<span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormRating</span>.getText().toString().replaceAll(<span style="color: #6a8759;">"[,|.]"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">""</span>))) * -<span style="color: #6897bb;">1</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">mBinding</span>.<span style="color: #9876aa;">restaurantFormText</span>.getText().toString())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(<span style="color: #9876aa;">mRatingListener </span>!= <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #9876aa;">mRatingListener</span>.onRating(rating)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br />}<br /></pre><p> Jadi if yang atas tuk mencegah nilai puluhan rupiah dibuang oleh aplikasi karena adanya pemeriksaan else if untuk 3 karakter terakhir. Bila saya lupa terapkan if yang nilai puluhan itu, maka bila user isi nominal dengan hanya sejumlah Rp.25 contohnya, sistem akan membuangnya. Hal ini tidak boleh terjadi. Yang dibuang adalah nilai sen yaitu spt ini Rp.555.555,25 yang akan dibuang oleh aplikasi.</p><p>Semoga paham yaa. Jadi didalam membuat aplikasi, saya mencoba segala kemungkinan user melakukan pengisiannya. Tetapi juga memudahkan user mengisi nominal. Saya dapat membayangkan kesulitan user melihat angka 0 yang rapat berderet sebanyak lima buah, eh atau 6 yaa. </p><p>Jadi dengan memberikan ijin user menulis menggunakan titik atau koma atau spasi di nominal, maka user akan senang dan bisa memeriksa jumlah nol dengan baik Dan menurut pemikiran saya, metode ini lebih baik dari textwatcher. Karena textwatcher seperti membebani aplikasi dan memperlambat user experience. Karena setiap user mengetikkan angka, layar bergerak gerak dan hal ini menghentikan user untuk melanjutkan input berikutnya. Dengan melepaskan textwatcher, user akan terbebas dari gerakan angka di layar. Metode sederhana dan menyenangkan bagi user dan aplikasi juga.</p><p>Terimakasih sudah membaca.</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/kesalahan-lokasi-gcp-di-aplikasi.html" target="_blank">Lanjutan kupas tuntas aplikasi Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-53109446201959449862021-03-07T11:44:00.005+07:002021-03-07T15:06:54.436+07:00Pembuatan Total, Sisa, Jml.Byr, Jml.Trk pada aplikasi Catatan Kredit<p>Beberapa pengguna aplikasi melalui ulasan dan email meminta saya untuk membuatkan total yang tampil di layar. Waduh, saya langsung kepikiran kalau akan terjadi penjumlahan berulang kali dan akibatnya akan membaca Google Cloud berkali kali. Sedangkan yang saya gunakan adalah Paket Spark yaitu paket gratis dari Google Cloud untuk simpan Database, Dokumen dan Gambar/Photo.Paket Spark gratis dibatasi jumlah harian, selebihnya saya akan mendapatkan tagihan.</p><p>Setelah bersemedi beberapa hari, tiba tiba dapat ide. Kenapa tidak saya gunakan teknik yang sama seperti dengan sisa kredit pada transaksi. Bisa diterapkan tuk di layar utama juga. Maka mulailah saya membuat kolom Kredit dan Bayar. Setiap Kredit akan ditambahkan ke kolom Kredit dan setiap Bayar akan ditambahkan ke kolom Bayar dan yang sudah ada adalah kolom Sisa yang akan ditambahkan atau dikurangi oleh transaksi kredit dan bayar. Kemudian saya lakukan kontrol terhadap nilai total Kredit dan Bayar untuk dicocokkan dengan nilai total Sisa. Bila tidak cocok maka nilai tidak akan dimunculkan. Hal ini akibat adanya transaksi yang telah dibuat sebelum sistem baru ini dibuat. Dengan berjalannya waktu atau pemindahan transaksi lama ke transaksi baru dan dihapuskannya transaksi lama, maka Kredit, Bayar dan Sisa akan balance dan akan muncul di layar. Juga akan menunjukkan ketepatan aplikasi dengan server Google Cloud.</p><p>Ide bagus dan saya langsung mulai terapkan, dan 2 kolom baru dibaca berbarengan dengan data lainnya sekaligus, sehingga tidak menambah beban baca Google Cloud harian. Dan tentu saja tidak ada tagihan ke saya. Hehehe, senangnya.</p><p>Ini saya tunjukkan dibawah ini:</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">addNewContact</span>() {<br /> String mName = <span style="color: #9876aa;">mmBinding</span>.<span style="color: #9876aa;">nameKu</span>.getText().toString()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String mEmail = <span style="color: #9876aa;">mmBinding</span>.<span style="color: #9876aa;">keteranganKu</span>.getText().toString()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String mPhone = <span style="color: #9876aa;">mmBinding</span>.<span style="color: #9876aa;">phoneKu</span>.getText().toString()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> final </span>String mPhoto = <span style="color: #6a8759;">"android.resource://com.blogspot.budilaks.catatankredit/drawable/photo"</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Integer mavgRating= <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Integer mNumRating = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Integer namaBayar= <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Integer namaKredit= <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Map<String<span style="color: #cc7832;">, </span>Object> newContact = <span style="color: #cc7832;">new </span>HashMap<>()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">NAME_KEY</span><span style="color: #cc7832;">, </span>mName)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">EMAIL_KEY</span><span style="color: #cc7832;">, </span>mEmail)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">PHONE_KEY</span><span style="color: #cc7832;">, </span>mPhone)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">PHOTO_KEY</span><span style="color: #cc7832;">, </span>mPhoto)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">RATING_KEY</span><span style="color: #cc7832;">, </span>mavgRating)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">BAYAR_KEY</span><span style="color: #cc7832;">, </span>namaBayar)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">KREDIT_KEY</span><span style="color: #cc7832;">, </span>namaKredit)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #9876aa; font-style: italic;">NUMRATING_KEY</span><span style="color: #cc7832;">, </span>mNumRating)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #6a8759;">"price"</span><span style="color: #cc7832;">, </span>FieldValue.<span style="font-style: italic;">serverTimestamp</span>())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newContact.put(<span style="color: #6a8759;">"jtpTanggal"</span><span style="color: #cc7832;">, </span>FieldValue.<span style="font-style: italic;">serverTimestamp</span>())<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">db</span>.collection(<span style="color: #9876aa;">sUserFb</span>).document().set(newContact)<br /> .addOnSuccessListener(aVoid -> Toast.<span style="font-style: italic;">makeText</span>(getApplicationContext()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Data tersimpan......"</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Toast.<span style="color: #9876aa; font-style: italic;">LENGTH_LONG</span>).show())<span style="color: #cc7832;">;<br /></span>}<br /></pre><p> Saat pembuatan nama baru, akan dibuatkan koleksi Bayar dan Kredit. Sedangkan Sisa sudah ada sejak awal aplikasi ini dibuat yaitu pada kolom avgRating. </p><p>Pada saat transaksi, terjadi penambahan nilai Bayar dan Kredit serta penjumlahan tuk nilai Sisa. Kemudian ditampilkan di layar dengan melakukan pembacaan sekaligus.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">int </span>newNumRatings = restaurant.getNumRatings() + <span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span><span style="color: grey;">// Compute new average rating<br /></span><span style="color: #cc7832;">double </span>oldRatingTotal = restaurant.getAvgRating()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">double </span>newAvgRating = (oldRatingTotal + <span style="color: #b389c5;">rating</span>.getRating())<span style="color: #cc7832;">;<br /></span>Date newPrice = <span style="color: #b389c5;">rating</span>.getTimestamp()<span style="color: #cc7832;">;<br /></span>Date newJtpRating = <span style="color: #b389c5;">rating</span>.getJtpRating()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">double </span>oldNamaBayar<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">double </span>oldNamaKredit<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">double </span>newNamaBayar<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">double </span>newNamaKredit<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;">int </span>newNumBayars<span style="color: #cc7832;">;<br /></span><span style="color: grey;">// compute new namaBayar dan namaKredit<br /></span><span style="color: #cc7832;">if </span>(<span style="color: #b389c5;">rating</span>.getRating() < <span style="color: #6897bb;">0.0</span>) {<br /> oldNamaBayar = restaurant.getNamaBayar()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newNamaBayar = oldNamaBayar + (Math.<span style="font-style: italic;">abs</span>(<span style="color: #b389c5;">rating</span>.getRating()))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newNamaKredit = restaurant.getNamaKredit()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newNumBayars = restaurant.getNumBayars() + <span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span>} <span style="color: #cc7832;">else </span>{<br /> oldNamaKredit = restaurant.getNamaKredit()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newNamaKredit = oldNamaKredit + <span style="color: #b389c5;">rating</span>.getRating()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newNamaBayar = restaurant.getNamaBayar()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>newNumBayars = restaurant.getNumBayars()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Log.<span style="font-style: italic;">d</span>(<span style="color: #9876aa; font-style: italic;">TAG</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"hasil.. " </span>+ newNamaBayar + <span style="color: #6a8759;">" -- " </span>+ newNamaKredit<span style="color: #cc7832;">, </span>task.getException())<span style="color: #cc7832;">;<br /></span>}<br /></pre><p>Demikian pula untuk Jml.Byr.(numBayars) dan Jml.Trk.(numRatings) mendapatkan perlakuan yang sama. Karena ada pengguna yang mengatakan senang melihat Jml.Trk. untuk melihat berapa kali cicilan telah dilunasi. Dan ini mengganggu saya untuk membuatkan Jml.Byr. agar pengguna ini tidak perlu mengurangi Jml.Trk. dengan nilai 1, cukup langsung lihat Jml.Byr. yaitu jumlah cicilan yang telah dilunasi.</p><p>Duh senangnya bisa membantu pengguna tanpa membebani munculnya tagihan dari Google Cloud.</p><p>catatan: Setelah baca baca tulisan ini, loh kok numBayars enggak ada di class aplikasi ini. Untung tidak crash karena sistem yang digunakan adalah update. Akan aku update nanti ke v50.0 tuk update update kecil spt ini.</p><p>Terimakasih sudah membaca curhat saya.</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan<span> </span> Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/textwatcher-pada-aplikasi-catatan-kredit.html" target="_blank">Lanjutan kupas tuntas aplikasi Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-56474264937091683762021-03-07T09:48:00.006+07:002021-03-07T12:01:38.660+07:00Antisipasi nilai Null pada Catatan Kredit agar tidak crash<p> Laporan google pada Play Console saya, menceritakan juga banyaknya crash pada aplikasi serta lokasi crash aplikasi catatan kredit.</p><p>Kemudian saya ikuti saran dari Android Studio untuk menambahkan assert restaurant != null; dan Objects.requireNonNull() spt dibawah ini :</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">assert </span>document != <span style="color: #cc7832;">null;<br /></span></pre><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #9876aa;">nengKu </span>= Objects.<span style="font-style: italic;">requireNonNull</span>(document.get(<span style="color: #6a8759;">"name"</span>)).toString()<span style="color: #cc7832;">;<br /></span></pre><p>Tetapi ternyata tidak cukup, saya harus membuat pemeriksaan nilai null dengan lebih teliti dan akhirnya dibuat spt dibawah ini:</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">assert </span>document != <span style="color: #cc7832;">null;<br /></span><span style="color: #cc7832;">if </span>(document.get(<span style="color: #6a8759;">"name"</span>) != <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #9876aa;">nengKu </span>= Objects.<span style="font-style: italic;">requireNonNull</span>(document.get(<span style="color: #6a8759;">"name"</span>)).toString()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><span style="color: #cc7832;">if </span>(document.get(<span style="color: #6a8759;">"avgRating"</span>) != <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #9876aa;">nengKk </span>= <span style="color: #9876aa;">mNumberFormat</span>.format(Objects.<span style="font-style: italic;">requireNonNull</span>(document.get(<span style="color: #6a8759;">"avgRating"</span>)))<span style="color: #cc7832;">;<br /></span>}<br /></pre><p>Dan sudah beberapa hari ini v49.0 belum mengalami crash, juga dengan v48.0</p><p>Ada beberapa user yang belum update dan masih menggunakan v47.0 yang belum dilengkapi antisipasi crash, maka masih nampak terjadinya crash aplikasi. Semoga semua user bisa update ke v49.0 agar saya tenang melihat aplikasi berjalan baik tanpa crash.</p><p>Terimakasih sudah membaca</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/pembuatan-total-sisa-jmlbyr-jmltrk-pada.html" target="_blank">Lanjutan kupas tuntas aplikasi catatan kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-73323275747428479982021-03-07T07:45:00.006+07:002021-03-07T10:14:27.129+07:00Cetak CSV pada Catatan Kredit<p>Waktu itu lebih mudah menjalankan cetak CSV, karena opencsv banyak panduan dan blog di googling. Tapi juga sebetulnya jadi awal yang mudah bagi cetak PDF. Seperti bagaimana mudahnya melakukan share PDF ke aplikasi lainnya.</p><p>Oke kita mulai, untuk CSV perlu third party library, yaitu opencsv. Dan hanya library saja, tanpa meminta data aplikasi dan lain lain yang aneh. Jadi diletakkan di dependencies spt ini:</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;">dependencies {<br /> implementation <span style="color: #6a8759;">'com.opencsv:opencsv:5.2'<br /></span></pre><p>setelah itu perlu ke Manifest untuk membuat alokasi file/cache nantinya csv/pdf disimpan sementara sebelum dikirimkan, dalam proses pembuatan isinya.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"> <span style="color: #e8bf6a;"><provider<br /></span><span style="color: #e8bf6a;"> </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:name</span><span style="color: #6a8759;">="androidx.core.content.FileProvider"<br /></span><span style="color: #6a8759;"> </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:authorities</span><span style="color: #6a8759;">="com.blogspot.budilaks.catatankredit.fileprovider"<br /></span><span style="color: #6a8759;"> </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:grantUriPermissions</span><span style="color: #6a8759;">="true"<br /></span><span style="color: #6a8759;"> </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:exported</span><span style="color: #6a8759;">="false"</span><span style="color: #e8bf6a;">><br /></span><span style="color: #e8bf6a;"> <meta-data<br /></span><span style="color: #e8bf6a;"> </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:name</span><span style="color: #6a8759;">="android.support.FILE_PROVIDER_PATHS"<br /></span><span style="color: #6a8759;"> </span><span style="color: #9876aa;">android</span><span style="color: #bababa;">:resource</span><span style="color: #6a8759;">="@xml/provider_paths"</span><span style="color: #e8bf6a;">/><br /></span><span style="color: #e8bf6a;"> </provider><br /></span><span style="color: #e8bf6a;"><br /></span><span style="color: #e8bf6a;"> </application><br /></span><span style="color: #e8bf6a;"><br /></span><span style="color: #e8bf6a;"></manifest><br /></span></pre><p>File bisa diganti menjadi Cache, tetapi untuk hape yang memori ram nya kecil, akan menjadikan crash aplikasi karena tidak cukup besar memori cache, sehingga saya gunakan File. Silahkan aja test dengan cache dan file dan bandingkan.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #e8bf6a;"><?</span><span style="color: #bababa;">xml version</span><span style="color: #6a8759;">="1.0" </span><span style="color: #bababa;">encoding</span><span style="color: #6a8759;">="utf-8"</span><span style="color: #e8bf6a;">?><br /></span><span style="color: #e8bf6a;"><paths><br /></span><span style="color: #e8bf6a;"> <files-path<br /></span><span style="color: #e8bf6a;"> </span><span style="color: #bababa;">name</span><span style="color: #6a8759;">="data"<br /></span><span style="color: #6a8759;"> </span><span style="color: #bababa;">path</span><span style="color: #6a8759;">="."</span><span style="color: #e8bf6a;">/><br /></span><span style="color: #e8bf6a;"><br /></span><span style="color: #e8bf6a;"> </span><span style="color: grey;"><!--name is the file name<br /></span><span style="color: grey;"> path is the root of external storage, it means here: Environment.getExternalStorageDirectory()<br /></span><span style="color: grey;"> <cache-path name="name" path="path" /><br /></span><span style="color: grey;"> <external-path name="scale" path="."/><br /></span><span style="color: grey;"> <files-path<br /></span><span style="color: grey;"> name="data"<br /></span><span style="color: grey;"> path="."/><br /></span><span style="color: grey;"><br /></span><span style="color: grey;"> another example: Environment.getExternalStorageDirectory() + File.separator + "temps" + "myFile.pdf"<br /></span><span style="color: grey;"><br /></span><span style="color: grey;"> <external-path name="myFile" path="temps"/><br /></span><span style="color: grey;"> --><br /></span><span style="color: #e8bf6a;"></paths></span></pre><p>Mau gunakan file path atau cache path ditulis diatas ini, di xml baru, berjudul provider_paths.xml</p><p>Saat awal perlu inisiasi lokasi tuk file yang hendak dibuat, dengan nama yang sama saat share.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">cetakCsv</span>() {<br /> File file = <span style="color: #cc7832;">new </span>File(getFilesDir()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"transaksi.csv"</span>)<span style="color: #cc7832;">;<br /></span></pre><p>Sehingga saat share bisa ada itu file/cache :</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"> writer.close()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">catch </span>(IOException e) {e.printStackTrace()<span style="color: #cc7832;">;</span>}<br />})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;">try </span>{<br /> <span style="color: #cc7832;">if </span>(!<span style="color: #9876aa;">nengKu</span>.isEmpty()) {<br /> Toast.<span style="font-style: italic;">makeText</span>(getApplicationContext()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Transaksi : " </span>+ <span style="color: #9876aa;">nengKu </span>+ <span style="color: #6a8759;">" dicetak ..."</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Toast.<span style="color: #9876aa; font-style: italic;">LENGTH_LONG</span>).show()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Context context = getApplicationContext()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>File filelocation = <span style="color: #cc7832;">new </span>File(getFilesDir()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"transaksi.csv"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Uri path = FileProvider.<span style="font-style: italic;">getUriForFile</span>(context<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"com.blogspot.budilaks.catatankredit.fileprovider"</span><span style="color: #cc7832;">, </span>filelocation)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Intent fileIntent = <span style="color: #cc7832;">new </span>Intent(Intent.<span style="color: #9876aa; font-style: italic;">ACTION_SEND</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.setType(<span style="color: #6a8759;">"text/csv"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.putExtra(Intent.<span style="color: #9876aa; font-style: italic;">EXTRA_SUBJECT</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Trk. : " </span>+ <span style="color: #9876aa;">nengKu </span>+ <span style="color: #6a8759;">" (tgl." </span>+ <span style="color: #9876aa;">formatFk</span>.format(<span style="color: #cc7832;">new </span>Date().getTime()) + <span style="color: #6a8759;">")"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.addFlags(Intent.<span style="color: #9876aa; font-style: italic;">FLAG_GRANT_READ_URI_PERMISSION</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.putExtra(Intent.<span style="color: #9876aa; font-style: italic;">EXTRA_STREAM</span><span style="color: #cc7832;">, </span>path)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>startActivity(Intent.<span style="font-style: italic;">createChooser</span>(fileIntent<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Share "</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br />} <span style="color: #cc7832;">catch </span>(Exception e) {<br /> e.printStackTrace()<span style="color: #cc7832;">;<br /></span>}<br /></pre><p>Perhatikan filelocation, penting tuk tidak salah ketik nama file/cache spt saat inisiasi. Juga apakah file/cache tipenya. setType menentukan tipe file apakah text atau pdf, untuk Share boleh tulis apa aja sih, entah buat apa ini. Saya ubah ubah (ganti Send Email, dll) ga ngaruh.</p><p>cetakCSV spt dot matrix printer, maju terus pantang mundur, apalagi kekiri kekanan. Jadi header dibuat dulu, lalu masuk iterasi tuk isi nya baris demi baris, barulah dibuat footer alias penjumlahan dan periksa. Lumayan ribet sih logikanya, mesti berpikir urut.</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #9876aa;">mRestaurantRef</span>.collection(<span style="color: #6a8759;">"ratings"</span>).orderBy(<span style="color: #6a8759;">"timestamp"</span>).limit(<span style="color: #6897bb;">100</span>)<br /> .get().addOnCompleteListener(task -> {<br /> <span style="color: #cc7832;">try </span>{<br /> CSVWriter writer = <span style="color: #cc7832;">new </span>CSVWriter((<span style="color: #cc7832;">new </span>FileWriter(<span style="color: #b389c5;">file</span>.getAbsoluteFile())))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #9876aa;">formatFk</span>.format(<span style="color: #cc7832;">new </span>Date())})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">"Nama : "</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">nengKu</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">"Tgl. Transaksi"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"---"</span><span style="color: #cc7832;">,</span><span style="color: #6a8759;">"Tgl. JatuhTempo"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"---"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Kredit"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"---"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Bayar"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"---"</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span><span style="color: #6a8759;">"Berita"</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> double </span>sumKreditKu = <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> double </span>sumBayarKu = <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(task.isSuccessful()) {<br /> <span style="color: #cc7832;">for </span>(QueryDocumentSnapshot document : Objects.<span style="font-style: italic;">requireNonNull</span>(task.getResult())) {<br /> String beritaKu = document.getString(<span style="color: #6a8759;">"text"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String bayarKu = <span style="color: #6a8759;">"0"</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>String kreditKu = <span style="color: #6a8759;">"0"</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> if </span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>) != <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #cc7832;">if </span>(Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>)) < <span style="color: #6897bb;">0.0</span>) {<br /> bayarKu = <span style="color: #9876aa;">mNumberFormat</span>.format(Math.<span style="font-style: italic;">abs</span>(Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>))))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">else<br /></span><span style="color: #cc7832;"> </span>kreditKu = <span style="color: #9876aa;">mNumberFormat</span>.format(Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>)))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /><br /> <span style="color: #cc7832;">if </span>(document.getDate(<span style="color: #6a8759;">"timestamp"</span>) != <span style="color: #cc7832;">null</span>) {<br /> String priceKu = (<span style="color: #9876aa;">formatFb</span>.format(Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDate(<span style="color: #6a8759;">"timestamp"</span>))))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(document.getDate(<span style="color: #6a8759;">"jtpRating"</span>) != <span style="color: #cc7832;">null</span>) {<br /> String jtpKu = (<span style="color: #9876aa;">formatFb</span>.format(Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDate(<span style="color: #6a8759;">"jtpRating"</span>))))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #b389c5;">uniNames</span>.add(<span style="color: #cc7832;">new </span>String[]{priceKu<span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span>jtpKu<span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span>kreditKu<span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span>bayarKu<span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>beritaKu})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /> <span style="color: #cc7832;">if </span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>) != <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #cc7832;">if </span>(Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>)) < <span style="color: #6897bb;">0.0</span>) {<br /> sumBayarKu -= Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">else<br /></span><span style="color: #cc7832;"> </span>sumKreditKu += Objects.<span style="font-style: italic;">requireNonNull</span>(document.getDouble(<span style="color: #6a8759;">"rating"</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> }<br /> }<br /> writer.writeAll(<span style="color: #b389c5;">uniNames</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"_____________"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"_____________"</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">,</span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" Kredit : Rp."</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">mNumberFormat</span>.format(sumKreditKu)<span style="color: #cc7832;">, </span><span style="color: #6a8759;">" Bayar : Rp."</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">mNumberFormat</span>.format(sumBayarKu)})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">,</span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"_____________"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"_____________"</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"____________"</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">,</span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" Sisa : Rp."</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">mNumberFormat</span>.format(sumKreditKu-sumBayarKu)<span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>writer.writeNext(<span style="color: #cc7832;">new </span>String[]{<span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">,</span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" Periksa : Rp."</span><span style="color: #cc7832;">, </span><span style="color: #9876aa;">nengKk</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">" "</span>})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>writer.close()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">catch </span>(IOException e) {e.printStackTrace()<span style="color: #cc7832;">;</span>}<br />})<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;">try </span>{<br /> <span style="color: #cc7832;">if </span>(!<span style="color: #9876aa;">nengKu</span>.isEmpty()) {<br /></pre><p>Ternyata sederhana coding nya, kalau dah temukan polanya.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvUEmT3EC3oRod_bWCIC2XxquUGiNzwV7kG-60ZknmnAUFyepryz8cHGrKeIRpovT-cOWje3N7jkFjFowCGm61onVyWEQ7ZgV8BzP0L44HBkjR0kw1Rm-LRkkrIF_axUFg2fPq1WSSbNrZ/s2048/Screenshot_20210307_110604_com.google.android.apps.docs.editors.sheets.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1152" data-original-width="2048" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvUEmT3EC3oRod_bWCIC2XxquUGiNzwV7kG-60ZknmnAUFyepryz8cHGrKeIRpovT-cOWje3N7jkFjFowCGm61onVyWEQ7ZgV8BzP0L44HBkjR0kw1Rm-LRkkrIF_axUFg2fPq1WSSbNrZ/s320/Screenshot_20210307_110604_com.google.android.apps.docs.editors.sheets.jpg" width="320" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"><br /></div><p>Berguna juga ilmu dasar dulu kuliah, bikin Fortran, dan Visual Basic tuk skripsi dan dilanjutkan bikin database Enterprise menggunakan Microsoft Access tuk gudang, produksi, penjualan, pembelian dan akunting.</p><p>Inilah kupas cetakCSV yang saya contek dari internet dah disesuaikan dengan kebutuhan saya.</p><p>Tidak ada yang sulit selama kita berusaha.</p><p>Terimakasih dah membaca.</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Kupas tuntas aplikasi Catatan Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/antisipasi-nilai-null-pada-catatan.html" target="_blank">Lanjutan antisipasi null pada aplikasi Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-55272664654098301302021-03-07T05:33:00.011+07:002021-03-07T10:09:22.112+07:00Cetak PDF pada Catatan Kredit<p>Sebenarnya cetak CSV yang saya temukan teknik yang mudahnya lebih dahulu daripada PDF. Kemudian dengan kegigihan saya, akhirnya saya menemukan cetak pdf yang simple dan mudah membuatnya. Permintaan pembuatan cetak PDF dari pengguna baik melalui ulasan maupun email. Akhirnya saya dapat mewujudkannya.</p><p>Banyak teknik cetak pdf, spt iText dan lain lainnya. Tanpa sadar, saya hapus semua dependencies iText dll, kemudian saya coba PdfDocument dan berhasil cetak pdf satu baris. Ternyata android studio telah tersedia didalamnya simple pdf . Hanya bisa nulis dan menyisipkan gambar. Nulis disini dalam artikata mengetik, membuat garis dan lingkaran, beberapa font dan size. Lumayan dan cocok, saya suka yang simple aja. Juga karena tidak menggunakan third party library, maka aplikasi bisa kecil dan cepat.</p><p>Untuk pelajarannya, banyak banget link yang saya googling. Dengan keyword "PdfDocument".</p><p>Awalnya yang keyword : "create pdf in java android" yang mengakibatkan beragam cara cetak pdf dan termasuk PdfDocument. Tadinya saya pikir PdfDocument sama dengan PDDocument, ternyata berbeda.</p><p>Ini saya cuplik code cetak pdf dari Catatan Kredit :</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">cetakPDF</span>() {<br /> CollectionReference unisRef = <span style="color: #9876aa;">db</span>.collection(<span style="color: #9876aa;">mUserFb</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>PdfDocument pdfDocument = <span style="color: #cc7832;">new </span>PdfDocument()<span style="color: #cc7832;">; </span><span style="color: grey;">// initial PdfDocument<br /></span><span style="color: grey;"> </span>unisRef.orderBy(<span style="color: #6a8759;">"name"</span>).limit(<span style="color: #6897bb;">100</span>).get().addOnCompleteListener(task -> {<br /> <span style="color: #cc7832;">int </span>noTransaksi = <span style="color: #6897bb;">0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> double </span>numberItemsPage = <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>numberPages = <span style="color: #6897bb;">1</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> int </span>layoutKu = <span style="color: #6897bb;">110</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> double </span>sumnyaKredit = <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> double </span>sumnyaTotalBayar= <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> double </span>sumnyaTotalKredit = <span style="color: #6897bb;">0.0</span><span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Paint title = <span style="color: #cc7832;">new </span>Paint()<span style="color: #cc7832;">; </span><span style="color: grey;">// initial tuk tulis text<br /></span><span style="color: grey;"> </span>PdfDocument.PageInfo mypageInfo = <span style="color: #cc7832;">new </span>PdfDocument.PageInfo.Builder(<span style="color: #6897bb;">842</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">595</span><span style="color: #cc7832;">, </span>numberPages).create()<span style="color: #cc7832;">; </span><span style="color: grey;">// persiapkan halaman<br /></span><span style="color: grey;"> </span>PdfDocument.Page myPage = <span style="color: #b389c5;">pdfDocument</span>.startPage(mypageInfo)<span style="color: #cc7832;">; </span><span style="color: grey;">// inisiasi halaman pertama<br /></span><span style="color: grey;"> </span>Canvas canvas = myPage.getCanvas()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>title.setTypeface(Typeface.<span style="font-style: italic;">create</span>(Typeface.<span style="color: #9876aa; font-style: italic;">DEFAULT</span><span style="color: #cc7832;">, </span>Typeface.<span style="color: #9876aa; font-style: italic;">NORMAL</span>))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>title.setTextAlign(Paint.Align.<span style="color: #9876aa; font-style: italic;">LEFT</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>title.setColor(Color.<span style="color: #9876aa; font-style: italic;">BLACK</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>title.setTextSize(<span style="color: #6897bb;">15</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"LAPORAN DATA UTAMA"</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">80</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">65</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Tgl. " </span>+ (<span style="color: #9876aa;">formatFb</span>.format(<span style="color: #cc7832;">new </span>Date().getTime()))<span style="color: #cc7832;">, </span><span style="color: #6897bb;">80</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">85</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>title.setTextSize(<span style="color: #6897bb;">14</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"No."</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">80</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Nama"</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Byr. Trk."</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">560</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Tgk.Trk."</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">645</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Tgl. Jtp"</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">720</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>title.setTextSize(<span style="color: #6897bb;">12</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">" " </span>+ <span style="color: #9876aa;">formatFk</span>.format(<span style="color: #cc7832;">new </span>Date().getTime())<span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">520</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">" @2021 by Catatan Kredit"</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">530</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"at Playstore, free and no ads."</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">540</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Paint paint = <span style="color: #cc7832;">new </span>Paint()<span style="color: #cc7832;">; </span><span style="color: grey;">// inisiasi pembuatan logo<br /></span><span style="color: grey;"> </span>paint.setColor(Color.<span style="color: #9876aa; font-style: italic;">BLUE</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>paint.setStyle(Paint.Style.<span style="color: #9876aa; font-style: italic;">STROKE</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">// draw a bitmap<br /></span><span style="color: grey;"> </span>Bitmap bitmap = BitmapFactory.<span style="font-style: italic;">decodeResource</span>(<span style="color: #cc7832;">this</span>.getResources()<span style="color: #cc7832;">, </span>R.drawable.<span style="color: #9876aa; font-style: italic;">wm</span>)<span style="color: #cc7832;">; </span><span style="color: grey;">// ambil bitmap dari drawable<br /></span><span style="color: grey;"> </span>bitmap = Bitmap.<span style="font-style: italic;">createScaledBitmap</span>(bitmap<span style="color: #cc7832;">, </span><span style="color: #6897bb;">36</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">36</span><span style="color: #cc7832;">, true</span>)<span style="color: #cc7832;">; </span><span style="color: grey;">// lakukan scale sesuai yang diinginkan<br /></span><span style="color: grey;"> </span>paint.setAntiAlias(<span style="color: #cc7832;">true</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>paint.setFilterBitmap(<span style="color: #cc7832;">true</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>paint.setDither(<span style="color: #cc7832;">true</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawBitmap(bitmap<span style="color: #cc7832;">, </span><span style="color: #6897bb;">80</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">500</span><span style="color: #cc7832;">, </span>paint)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span>title.setTextAlign(Paint.Align.<span style="color: #9876aa; font-style: italic;">RIGHT</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>title.setTextSize(<span style="color: #6897bb;">14</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Kredit Rp."</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">360</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Bayar Rp."</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">455</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>canvas.drawText(<span style="color: #6a8759;">"Sisa Rp."</span><span style="color: #cc7832;">,</span><span style="color: #6897bb;">550</span><span style="color: #cc7832;">, </span><span style="color: #6897bb;">110</span><span style="color: #cc7832;">, </span>title)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span></pre><p>1. Pencetakan boleh naik turun, saya coba dan berhasil. Asal titik koordinat benar maka tidak akan bertumpuk. Bukan spt printer dot matrix, melainkan spt plotter atau printer spanduk. Bisa maju mundur kanan kiri. Untuk nilai rupiah saya buat dari kanan kekiri, jadi bisa mudah membuat selaras hasil pdf.</p><p>2. Kesulitan kedua adalah membuat logo, karena ingin tampil di cetakan pdf sehingga saya berkeinginan membuat logo selain footer. Akhirnya ditemukan bahwa logo atau gambar dalam bentuk bitmap, dengan size yang tidak besar agar aplikasi tetap kecil dan ringan, dan kemudian dikecilkan sesuai kebutuhan dengan dither yang ga boleh dilewatkan. Saya sendiri ga ngerti dither, tapi di Stack Overflow mengatakan disalah satu jawaban pertanyaan.</p><p>3. Kemudian kesulitan ketiga adalah mengirimkan keluar aplikasi menuju share email/sosmed/printer dll. Hal ini sudah berhasil disaat Cetak CSV, sehingga tinggal copy dan ubah menjadi "application/pdf"</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #b389c5;">pdfDocument</span>.close()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;">try </span>{<br /> Context context = getApplicationContext()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>File filelocation = <span style="color: #cc7832;">new </span>File(getFilesDir()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"datautama.pdf"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Uri path = FileProvider.<span style="font-style: italic;">getUriForFile</span>(context<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"com.blogspot.budilaks.catatankredit.fileprovider"</span><span style="color: #cc7832;">, </span>filelocation)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Intent fileIntent = <span style="color: #cc7832;">new </span>Intent(Intent.<span style="color: #9876aa; font-style: italic;">ACTION_SEND</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.setType(<span style="color: #6a8759;">"application/pdf"</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.putExtra(Intent.<span style="color: #9876aa; font-style: italic;">EXTRA_SUBJECT</span><span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Data Utama Tgl. " </span>+ <span style="color: #9876aa;">formatFk</span>.format(<span style="color: #cc7832;">new </span>Date()))<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.addFlags(Intent.<span style="color: #9876aa; font-style: italic;">FLAG_GRANT_READ_URI_PERMISSION</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>fileIntent.putExtra(Intent.<span style="color: #9876aa; font-style: italic;">EXTRA_STREAM</span><span style="color: #cc7832;">, </span>path)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>startActivity(Intent.<span style="font-style: italic;">createChooser</span>(fileIntent<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Share "</span>))<span style="color: #cc7832;">;<br /></span>} <span style="color: #cc7832;">catch </span>(Exception e) {<br /> e.printStackTrace()<span style="color: #cc7832;">;<br /></span>}<br /></pre><p>Pas sudah jadi code dan berjalan sempurna, waduh kok segini aja yaa. So simple. So ringan. Ga ada dependencies library. Pencarian yang sempurna, terimakasih Google dan Stack Overflow.</p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcjfZ19zVvXO73MEjN8gk10FfZJaNoyokEeLvs68tqFJrHcGQP5hfDO6CQIrt5CbcDXLpPJUGROxAjxvHxmkTRSmwQ4Bqozh0nQ7r1qQTjjiTL47a8q-boHi0UmIUphPetLNYYKudCc1zM/s2048/Screenshot_20210302_184248_com.google.android.apps.docs.jpg" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="2048" data-original-width="1152" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhcjfZ19zVvXO73MEjN8gk10FfZJaNoyokEeLvs68tqFJrHcGQP5hfDO6CQIrt5CbcDXLpPJUGROxAjxvHxmkTRSmwQ4Bqozh0nQ7r1qQTjjiTL47a8q-boHi0UmIUphPetLNYYKudCc1zM/s320/Screenshot_20210302_184248_com.google.android.apps.docs.jpg" /></a></div>Dari seluruh permintaan pengguna, satu hal yang belum dapat saya kabulkan yaitu Search atau Pencarian Nama. <p>Karena :</p><p>1. Google Firestore hanya boleh dicari spt pencarian Google yaitu satu kata penuh. Ditambah lagi harus huruf besar/kecil tepat sasaran. Hal ini dikeluhkan juga di Stack Overflow.</p><p>2. Point diatas masih dapat diatasi dengan third party library, yi Algolia. Pada algolia, saya harus memberikan koneksi ke data google aplikasi ini, dan itu mengawatirkan saya.</p><p>3. Sistem search berarti data dibaca dari Cloud yang kemungkinan berkali kali. Dan hal ini akan berakibat naiknya jumlah baca harian yang dimana terbatas tuk paket Spark yang gratis ini. Seperti total dan sisa pada aplikasi ini, menggunakan teknik dompleng pada pembacaan data layar, jadi tidak menaikkan traffic ke Cloud. Cloud ini gratis, dengan batasan baca 50kilo per hari. Diatas itu maka akan ada tagihan ke rekening saya dalam bentuk Dollar. Hadeh, moga ga lewat batasan per hari.</p><p>Begitulah saya berhasil cetak pdf, berikut curhat saya mengenai pencarian nama.</p><p>Terimakasih, semoga bermanfaat.</p><p><a href="https://budilaks.blogspot.com/2021/03/kupas-tuntas-catatan-kredit.html" target="_blank">Awal kisah kupas tuntas Catatan Kredit</a></p><p><a href="https://budilaks.blogspot.com/2021/03/cetak-csv-pada-catatan-kredit.html" target="_blank">Lanjutan cetakCSV aplikasi Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-30890407289850622722021-03-07T04:27:00.009+07:002021-03-07T05:51:11.923+07:00Kupas Tuntas Catatan Kredit<p>Pada akhir tahun 2017, saya menganggur. Disaat itu otak tidak dipekerjakan, dan tiba tiba terlintas dipikiran tuk mencoba membuat aplikasi android. Agar otak ini ada kegiatan selain bersepeda, berjalan kaki, senam minggu di alun alun dan memberi makan ikan di kolam alam RW.</p><p>Kemudian terpikirkan untuk membuat aplikasi Kredit bagi istri, teman dan kakak ipar di kampung. Setelah memeriksa playstore, hanya nampak satu aplikasi Kredit buatan India. Dan saya saat itu ingin membuat semacam itu.</p><p>Mulailah pencarian ilmu di internet, terutama dari google. Akhirnya ditemukan contoh aplikasi Friendlyeats di <a href="https://firebaseopensource.com/projects/firebase/friendlyeats-android/" target="_blank">https://firebaseopensource.com/projects/firebase/friendlyeats-android/</a></p><p>Download dan install, kemudian ikuti petunjuk hingga aplikasi berjalan sempurna. Karena saya lihat daftar restaurant bisa sebagai daftar kustomer kredit dan rating bisa saya jadikan transaksi. Suatu aplikasi sederhana dan menggunakan satu layar saja tuk menampilkan data kustomer.</p><p>1. Aplikasi harus dapat koneksi ke Firestore, saat itu saya ikuti petunjuk di google dan android studio. Link nya ini <a href="https://firebase.google.com/codelabs/firestore-android#0" target="_blank">https://firebase.google.com/codelabs/firestore-android#0</a></p><p>2. Setelah bisa konek, aplikasi bisa isi data. Dan kemudian melihat lihat cara kerjanya. Kemudian saya perhatikan sistem login</p><p>3. Sistem login bisa beragam, dari semua sistem login itu akhirnya saya tentukan tuk gunakan Google Login. Karena pasti hape akan otomatis, dan takkan pernah lupa. Bila lupa maka bisa di reset, walau kemungkinan gagal pun ada. Yaitu user tidak menerapkan sistem dua step verifikasi dan hape rusak/hilang. Begitu banyak teknik Google Login, dan setelah menelusuri dan mempelajari dan mempraktekan ini</p><p><a href="https://developers.google.com/identity/sign-in/android/start-integrating" target="_blank">https://developers.google.com/identity/sign-in/android/start-integrating</a></p><p>dan bertanya ke google dan selalu memilih hasil dari <a href="https://stackoverflow.com/" target="_blank">https://stackoverflow.com/</a></p><p>kemudian barulah blog blog pelajaran lainnya yang muncul di pencarian google.</p><p>Dibawah ini google sign in saya</p><pre style="background-color: #2b2b2b; color: #a9b7c6; font-family: Consolas, monospace; font-size: 9,8pt;"><span style="color: #cc7832;">private void </span><span style="color: #ffc66d;">startSignIn</span>() {<br /> Intent signInIntent = <span style="color: #9876aa;">mGoogleSignInClient</span>.getSignInIntent()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>startActivityForResult(signInIntent<span style="color: #cc7832;">, </span><span style="color: #9876aa; font-style: italic;">RC_SIGN_IN</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">mViewModel</span>.setIsSigningIn(<span style="color: #cc7832;">true</span>)<span style="color: #cc7832;">;<br /></span>}<br /><br /><span style="color: #bbb529;">@Override<br /></span><span style="color: #cc7832;">public void </span><span style="color: #ffc66d;">onActivityResult</span>(<span style="color: #cc7832;">int </span>requestCode<span style="color: #cc7832;">, int </span>resultCode<span style="color: #cc7832;">, </span>Intent data) {<br /> <span style="color: #cc7832;">super</span>.onActivityResult(requestCode<span style="color: #cc7832;">, </span>resultCode<span style="color: #cc7832;">, </span>data)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span><span style="color: grey;">// Result returned from launching the Intent from GoogleSignInApi.getSignInIntent(...);<br /></span><span style="color: grey;"> </span><span style="color: #cc7832;">if </span>(requestCode == <span style="color: #9876aa; font-style: italic;">RC_SIGN_IN</span>) {<br /> Task<GoogleSignInAccount> task = GoogleSignIn.<span style="font-style: italic;">getSignedInAccountFromIntent</span>(data)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> try </span>{<br /> <span style="color: grey;">// Google Sign In was successful, authenticate with Firebase<br /></span><span style="color: grey;"> </span>GoogleSignInAccount account = task.getResult(ApiException.<span style="color: #cc7832;">class</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> assert </span>account != <span style="color: #cc7832;">null;<br /></span><span style="color: #cc7832;"> </span>firebaseAuthWithGoogle(account)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>} <span style="color: #cc7832;">catch </span>(ApiException ignored) {<br /> }<br /> }<br />}<br /><br /><span style="color: grey;">// [START auth_with_google]<br /></span><span style="color: #cc7832;">private void </span><span style="color: #ffc66d;">firebaseAuthWithGoogle</span>(GoogleSignInAccount acct) {<br /> AuthCredential credential = GoogleAuthProvider.<span style="font-style: italic;">getCredential</span>(acct.getIdToken()<span style="color: #cc7832;">, null</span>)<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"><br /></span><span style="color: #cc7832;"> </span><span style="color: #9876aa;">mAuth</span>.signInWithCredential(credential)<br /> .addOnCompleteListener(<span style="color: #cc7832;">this, </span>task -> {<br /> <span style="color: #cc7832;">if </span>(task.isSuccessful()) {<br /> <span style="color: grey;">// Sign in success, update UI with the signed-in user's information<br /></span><span style="color: grey;"> </span>FirebaseUser userFb = <span style="color: #9876aa;">mAuth</span>.getCurrentUser()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> if </span>(userFb != <span style="color: #cc7832;">null</span>) {<br /> <span style="color: #9876aa;">mUserFb </span>= userFb.getUid()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>Toast.<span style="font-style: italic;">makeText</span>(getApplicationContext()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Sign In sukses ..."</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Toast.<span style="color: #9876aa; font-style: italic;">LENGTH_LONG</span>).show()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>onClearFilterClicked()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> } <span style="color: #cc7832;">else </span>{<br /> <span style="color: grey;">// If sign in fails, display a message to the user.<br /></span><span style="color: grey;"> </span>Toast.<span style="font-style: italic;">makeText</span>(getApplicationContext()<span style="color: #cc7832;">, </span><span style="color: #6a8759;">"Gagal Sign In ..."</span><span style="color: #cc7832;">,<br /></span><span style="color: #cc7832;"> </span>Toast.<span style="color: #9876aa; font-style: italic;">LENGTH_SHORT</span>).show()<span style="color: #cc7832;">;<br /></span><span style="color: #cc7832;"> </span>}<br /> })<span style="color: #cc7832;">;<br /></span>}</pre><p>Simple yaa, tapi perjuangannya lumayan berhari hari. Bolak balik tes dan lihat di console Firestore, apakah berhasil masuk kemudian dihapuskan daftar Firestore login, agar saya bisa tes login kembali. Begitu lama, begitu susah, begitu gigih dan otak pun panas. Horee akhirnya login sempurna dengan code yang simple.</p><p>Demikian sebagian kupas tuntas Catatan Kredit, akan disambung kembali dengan hal hal lainnya didalam pembuatan dan mungkin akan lompat lompat. Dibawah akan saya letakkan link ke kupas tuntas berikutnya.</p><p>Hanya saya sedihnya adalah masih ada yang memberi bintang satu dua dan tiga, padahal tanpa iklan dan hanya satu satunya aplikasi android saya. Bintang 5 penting tuk kawan kawan kreditur lainnya bisa melihat potensi aplikasi ini tuk membantu bisnisnya. Bintang satu dua dan tiga tidak untuk saya, tetapi bintang lima tuk sesama rekan yang terjun dalam bisnis yang sama. Berikanlah yang terbaik tuk rekan bisnis dan kustomer anda, maka bisnis ini akan semakin berkembang. Akan semakin banyak kustomer dan semakin banyak rekan bisnis maka akan semakin berkembang suatu bisnis.</p><p>Aplikasi ini bukan untuk saya, karena saya hanya gunakan tuk mengasah otak dikala menganggur. Dan saat ini juga saat menunggu pekerjaan baru sehingga bisa update dan menulis blog ini. Demikian sedikit curhat.</p><p>Terimakasih dah membaca.</p><p><a href="https://budilaks.blogspot.com/2021/03/cetak-pdf-pada-catatan-kredit.html" target="_blank">Lanjutan kupas tuntas Catatan Kredit</a></p>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-79960785119206093472021-01-15T18:46:00.001+07:002021-01-15T18:46:55.485+07:00Formula keramik<div>Dalam pekerjaan sejak lulus sekolah di bidang keramik lantai, formula keramik menggunakan tanah liat, pasir dan batuan feldspar. <br></div><div>Tahun demi tahun, itu itu lagi formulasi. Keramik lantai hingga keramik porselain. Dengan proses milling yang lama, dari 6 jam sampai dengan 13 jam. Dengan biaya milling yang tinggi. </div><div><br></div><div>Pada 2016, boss saya datang dengan ilmu baru. Formulasi bahan keramik berubah total. Menghilangkan pasir dan batuan feldspar menyebabkan milling menjadi sangat cepat. Coba bayangkan dengan hanya menggunakan tanah saja, dapat dihasilkan keramik dengan kadar air 3% saat itu. </div><div><br></div><div>Pada awalnya, beliau kesulitan dalam menaklukkan tanah liat segitu banyaknya. Dengan teknik deflocculant, akhirnya dapat saya temukan kebutuhan deflocculant. Baca artikel saya mengenai deflocculant. </div><div><br></div><div>Awalnya boss tidak mau menggunakan ballmill, mau langsung di mixing di stirrer tank. Akhirnya beliau menyerah, dan mengijinkan saya menggunakan ballmill. 2 bahan tanah dimasukkan dalam ballmill. </div><div><br></div><div>2.5 jam kemudian residu sudah didapat. Tapi manajer produksi ga mau, minta milling 3 jam. Dan saya setujui. Kenapa? Rupanya dia tidak hendak cek residu, langsung 3 jam turun akan menghemat waktu dan tenaga. Krn pada 3 jam residu pasti tercapai dan tidak akan overmilling, krn bukan milling yang 8 jam lebih. Residu antara 2.5 jam dan 3 jam tidak berbeda jauh, hanya lebih konsisten rata. Pintar manager produksi aku.</div><div><br></div><div>Pada awal tes, kami produksi 150kg powder menggunakan ballmill GP dan ditumbuk manual. Kemudian dimasukkan press. Didapatkan bahwa diperlukan deareation cukup tuk menghilangkan laminasi. Sedangkan untuk bending strength dan kekeringan setelah Dryer dan spesifikasi setelah bakar, masuk dalam spek. </div><div><br></div><div>Saat kami melakukan uji spek, didapatkan juga bahwa warna body jauh lebih putih. Seakan body formula menggunakan banyak bahan zirconium yang mahal itu. Seakan memakai pemutih. Dan akibatnya pada permukaan, warna warna glaze, printing menjadi cerah. Warna menjadi lebih menonjol. Itulah keuntungan body putih. </div><div><br></div><div>Tentu saja, bahan tanah liat putih. Tanpa campuran batuan feldspar yang bikin hitam. Jangan salah, batuan feldspar nampak putih. Tetapi bila dibakar akan menjadi hitam. Nah lho. Pada 2001 sudah saya buktikan ke boss, batuan feldspar bikin warna jadi lebih gray.</div><div><br></div><div>Cukup lama kami lakukan riset laboratorium, dari uji deflocculant, uji potmill. Sempat saya ingatkan tuk uji potmill berkali kali, mencari menit yg cocok tuk hasil residu yang sama. </div><div>Kemudian tes bakar di lab furnace, dibandingkan dengan formula biasa. Juga tes bakar di kiln normal. Setelah semua menjanjikan, maka barulah ke skala tes produksi. </div><div><br></div><div>Pada skala ujicoba produksi, tidak mengalami kendala apapun. Karena laminasi, size, residu, deflocculant, milling time, penetration, kesikuan dan concave convex, semua sudah diketahui aman. Sehingga tanpa menunggu lama dilanjutkan dengan skala full produksi.</div><div><br></div><div>120 ton pertama berjalan sukses, dan produksi pun mulai dijalankan penuh. Bahan didatangkan langsung, bahan baku yang tidak terpakai diambil kembali oleh suplier. Milling time 3 jam, hanya random cek visc dan residu. Proses lainnya tidak berubah. Suhu kiln sedikit naik, tuk mendapatkan water absorption yang benar. Warna bodi yang putih banyak membantu kestabilan shading. </div><div><br></div><div>Tiba-tiba terjadi sesuatu, pada permukaan keramik timbul bintik hitam. Segera kami lakukan cek kesegala arah, dan salah satu asisten menemukan sumber masalah pada satu bahan baku dari kedua bahan baku yg digunakan. Esoknya langsung berangkat ke mining, dan problem solved. Kebiasaan saya bila ke mining, adalah memperhatikan semua kegiatan. Sehingga cepat mengetahui kesalahan yang terjadi. Suplier langsung bekerja cepat, dan bahan baku 200 ton sisa ditarik, sambil mengirim bahan yang benar. Kecepatan problem solving sangatlah penting. </div><div><br></div><div>Tidak ada lagi masalah berarti. Finance menanyakan perhitungan yg harus diubah, milling time, jumlah karyawan, spraying karena naik Visc ke 1.75, penghematan alubit balls menjadi 4x lebih awet, penghematan lining menjadi 2x lebih awet, listrik malam berkurang karena hanya jalan 1 shift pagi. Kebetulan memiliki 2 hopper tuk 2 bahan baku, sehingga cepat loading dan tidak diukurnya Visc dan residue menyebabkan cepat unloading, pintarnya manajer produksi melihat peluang memendekkan proses.</div><div><br></div><div>Karena secara hitungan, 6 jam milling menjadi 3 jam milling diperlukan 1.5 shift. Tapi kecepatan loading dan unloading menjadikan 1 shift saja. Bukan main. </div><div><br></div><div>Kalau dibandingkan formulasi hemat lainnya yang mengandalkan harga bahan baku murah, maka formulasi 2 bahan baku ini jauh lebih murah dari segi proses. Karena harga tanah lebih mahal dari debu quarry (quarry dust), dengan proses produksi yang tidak berubah dengan formulasi umumnya. </div><div><br></div><div>Demikian pengalaman saya. Tuk diingat bahwa formulasi dan proses sangat berkaitan dan menentukan biaya akhir produksi.</div><div><br></div><div>Keuntungan :</div><div>1. Bahan murah, krn feldspar dan pasir mahal.</div><div>2. Warna lebih putih.</div><div>3. Loading unloading lebih cepat. </div><div>4. Viscosity lebih rendah, shg bisa saya naikkan ke 1.75 tanpa kesulitan. (lihat tulisan mengenai deflocculant). </div><div>5. Proses jauh lebih hemat dengan milling 3 jam saja. </div><div>Dan hal hal lain yg menyenangkan, seperti stok bahan baku, kebersihan area dll.</div><div><br></div><div>Terimakasih. </div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-54109125149710313902021-01-10T08:24:00.001+07:002021-01-10T08:47:27.664+07:00Color Matching bagian kedua Melanjutkan pembahasan pada <a href="https://budilaks.blogspot.com/2017/10/color-matching.html">Color Matching bagian pertama</a>, saya hendak menceritakan saat bekerja pada salah satu pabrik di Malaysia. <div>Ingat, awal mulai, direktur minta saya tuk belikan stirrer kecil 200 liter tanpa tank dan movable.</div><div>Kemudian meminta kepala laboratorium tuk mengubah semua design yg menggunakan lebih dari 3 warna menjadi maksimal 3 warna, demikian pula tuk digital printing. Hasil disampaikan ke customers dengan ditambahi ancaman, bahwa terjadi perubahan bahan baku sehingga shade berubah. Wkwkwkw bisa aja pak direktur. Dan kepala marketing juga paham, shade yang tidak stabil akan merepotkan sales dengan komplen yg bertubi-tubi. </div><div>Salah bila menganggap: warna ini warna itu dicampur akan menjadi suatu warna. </div><div>Warna keramik bukan spt mengaduk warna tuk cat, cam kan itu. Warna keramik ditentukan oleh:</div><div>1. Bahan baku body, warnanya</div><div>2. Engobe, ketebalan dan warna. </div><div>3. Ketebalan glaze</div><div>4. Suhu pada kiln. </div><div>Dll problem yg mungkin ada.</div><div><br></div><div>Jadi penggunaan 3 warna, atau 2 warna dan bahkan 1 warna akan memberikan kemudahan :</div><div>1. Matching color hanya kurang dan tambah pada 1 warna saja. </div><div>2. Cepat tuk matching color.</div><div>3. Kestabilan shade akan baik, dan sales pun senang. Krn setiap warna inti memiliki titik suhu kiln yang berbeda. </div><div>4. Yg jelas bagian glaze preparation atau digital akan lebih lebih santai. </div><div><br></div><div>Karena kombinasi 2 warna dengan 4 warna akan jauh jumlah iterasi nya.</div><div><br></div><div>Jadi bila sales datang dengan contoh tuk dibuatkan, maka design membuat dengan jumlah warna inti maksimal 3, sukur sukur bisa 2 warna inti saja. Kemudian sales menyetujui, karena sales harus sadar akan hal yang diuraikan diatas. Yaitu jumlah warna inti lebih dari 3 akan menyulitkan produksi dan penjualan. Loh kok penjualan?? Iya karena warna inti banyak akan menyebabkan variasi shade yang sulit dikontrol dan akhir akhirnya sales harus menyetujui shade yg dihasilkan produksi. Kata orang produksi : makan tuh design, minta aja yg susah susah. Padahal salah orang produksi dan laboratorium, mengapa tidak mengerti tentang color matching diatas.</div><div><br></div><div>Ada satu pengalaman juga, digital printing mengalami masalah pada ink tower warna pink. Selama perbaikan dan menunggu ink tower datang dari Italy, produksi mengalihkan ke produk produk yang tidak menggunakan warna pink. Sales diberitahu, tapi dikatakan bahwa produk lainnya bisa didahulukan. Wuih sales pun bernafas lega. Dan ketika tower ink tiba dan berfungsi baik, produksi kembali normal. </div><div><br></div><div>Demikian lah cara tuk proses produksi yang benar dan baik.</div><div><br></div><div>Coba pikirkan. </div><div><br></div><div><a href="https://budilaks.blogspot.com/2020/03/keramik-anti-bakteri.html">Temuan nano keramik antibakteri, tersedia di alam semulajadi </a><br></div><div><br></div><div><a href="https://budilaks.blogspot.com/2017/10/color-matching.html">Color Matching bagian kesatu </a><br></div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0tag:blogger.com,1999:blog-171116179817754719.post-31137566588361701002020-12-15T16:01:00.001+07:002020-12-19T07:19:00.292+07:00Perhitungan Grid Tie Inverter terhadap return of investment.<div>Saya mencoba membuat kalkulasi terhadap penggunaan PLTS dengan Grid Tie Inverter.</div><div>Kenapa saya hendak menggunakan Grid Tie Inverter? Karena tidak memerlukan baterai yang merepotkan. Sekali pasang PLTS-GTI maka bebas dari maintenance dan biaya nya.</div><div><br></div><div>Oke saya berhitung,</div><div>Pemakaian Listrik bulanan 440 kWh untuk daya 900VA</div><div>Minimal pemakaian (peraturan PLN) sebesar 40 kWh</div><div><br></div><div><div>Maka besaran PLTS tuk minimal tagihan PLN sebesar ( 440 – 40 ) kWh = 400 kWh</div><div>Dengan menggunakan meter EXIM atau meter digital export import, maka kelebihan export akan digunakan bulan berikutnya sebanyak 65% (peraturan PLN)</div><div>Bila tanpa memperhitungkan factor 65%, maka bila saya hitung balik,</div><div>400 kWh / bulan = 13.333 Wh / hari ------></div><div>Maka bila 1 (satu) hari, PLTS memberikan daya selama 5 jam saja, dibutuhkan (13.333 / 5) = 2.666 Wh atau bila dibulatkan menjadi 3 kWh.</div><div>Misalkan setiap 200Wp memerlukan biaya 2.5 juta rp, maka 3 kWh memerlukan 37.5 juta rupiah.</div><div>Andaikan satu bulan dihemat 500 ribu rp, maka ROI = 75 bulan atau 6.25 tahun.</div><div>Dan sejak bulan ke 2, tagihan listrik minimal adalah sekitar 70 ribu rupiah.</div><div>Dan seperti diketahui, tariff listrik naik dari tahun ke tahun. </div><div>Rencana saya secara bertahap, dimana GTI dapat di parallel tanpa masalah.</div><div>Panel surya mono crystalline 100Wp x 2 = 1.2 juta rp</div><div>GTI 600 watt = 1.1 juta rp ( rencana 1 unit GTI hanya untuk 100Wp x 4)</div><div>Lain lain = 300 ribu rp</div><div><br></div><div>Mau hitung lain lagi,</div><div>Misalkan terpasang 4 unit x 100Wp. Berapa hemat listriknya?</div><div>Total sehari menjadi 5 jam x 400Wp = 2000 Wh atau 2 kWh</div><div>Total satu bulan = 30 x 2 kWh = 60 kWh </div><div>Total rupiah penghematan per bulan = 1.325 rp x 60 kWh = 79.500 rupiah……Lumayan</div></div><div><br></div><div><b>Rupanya efisiensi atau maksimal keluaran panel surya berkisar 50%, </b>jadi penghematan menjadi sekitar 40 ribu rupiah saja.</div><div><br></div><div>Semalam ada kepikiran, tuk memindahkan semua yg watt tinggi ke siang hari.</div><div>Spt mesin cuci pagi hari, lalu rice cooker setelah mesin cuci, kemudian siang agak sore strika. </div><div>Juga AC hanya tuk siang hari, dengan memperhatikan sirkulasi udara yg baik bila tidak menggunakan AC. Saya pasang turbine atap tuk sedot angin dari dalam rumah, jadi setiap kamar ada tersedia lubang di plafon dan atas pintu.</div><div><br></div><div>Barusan nonton yutub, kabel diganti pakai 16mm, plts nya ada peningkatan watt besar sekali. Jadi rencana mau pakai kabel besar juga.</div>Budi Laksamanahttp://www.blogger.com/profile/14144465442111745813noreply@blogger.com0